41. 缺失的第一个正数
自定义哈希算法充分利用原数组空间
//
题目链接-来源:力扣(LeetCode)
给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。
示例 1:
输入:nums = [1,2,0]输出:3
思路:假设不限制空间复杂度,本题显然可以用普通的哈希表存储所有元素,然后从 1 开始遍历正整数,直到找到第一个不发生冲突的正整数即为答案。现在要求 O(1) 空间复杂度,我们考虑如何充分利用原数组空间,螺蛳壳里做道场。我们总共有 n 个元素,并且有长度为 n 的数组空间,并且 n 个元素当中,会有 /[0,n] 个元素或者小于等于 0、或者大于 n(n 个不相同的正整数若均小于等于 n,则最小未出现的元素为 n + 1, 否则,答案一定在 (0,n]之间),不在我们考虑之列,也就是说,我们的数组空间是完全够用的,经过充分的交换之后,可以把所有处于 (0,n] 之间的元素哈希到合适的位置上。(理论上,这里有无数种哈希算法,比如 Hash(x) = (x + a) % n, ...
40. 组合总和 II
回溯遍历可能选项,二分法加快效率,39题的变式
//
题目链接-来源:力扣(LeetCode)
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
注意:解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5], target = 8,输出:[[1,1,6],[1,2,5],[1,7],[2,6]]
思路:此题完全可以沿用上一题的模板。我们先对数组排序,之后二分地找到最大的小于等于 target 的数,从这个数开始向下回溯递归地进行 DFS搜索。只需注意,上题的每个元素是无限量的,而本题中的元素有限。为了不漏掉重复的元素,同时不重复考虑重复的元素,我们只需在每次递归当中,在循环每次要进入下层递归前,判断当前循环考虑的元素是否与前一次循环重复,重复则略过即可。时间复杂度:O(2^n * n) 其中 2^n 是指元素个数为 n 的集合所对应的幂集中的元素数量。空间复杂 ...
39. 组合总和
回溯遍历可能选项,二分法加快效率
//
题目链接-来源:力扣(LeetCode)
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。解集不能包含重复的组合。 示例 1:
输入:candidates = [2,3,6,7], target = 7,所求解集为:[ [7], [2,2,3]]
思路:首先,大于 target 的元素不可能进入解集。因此可以用二分法先确定 target 的大致位置。我们寻找满足小于等于 target 的最大元素的下标,在此下标范围内的元素全部进入我们的备选集。之后在备选集中按一定的顺序(从小到大或从大到小)选择选项,选择某个选项 val 后,用 target 值减去 val,获得的新值 deeperTarget 若不等于 0,我们就把 deeperTarget 作为新的 target 递归地放到剩余的备选集当中查找(记住,深层递 ...
38. 外观数列
迭代计数前驱字符串的特征
//
题目链接-来源:力扣(LeetCode)
给定一个正整数 n ,输出外观数列的第 n 项。
「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。
你可以将其视作是由递归公式定义的数字字符串序列:
countAndSay(1) = “1”countAndSay(n) 是对 countAndSay(n-1) 的描述,然后转换成另一个数字字符串。前五项如下:
1
11
21
1211
111221
第一项是数字 1描述前一项,这个数是 1 即 “ 一 个 1 ”,记作 “11”描述前一项,这个数是 11 即 “ 二 个 1 ” ,记作 “21”描述前一项,这个数是 21 即 “ 一 个 2 + 一 个 1 ” ,记作 “1211”描述前一项,这个数是 1211 即 “ 一 个 1 + 一 个 2 + 二 个 1 ” ,记作 “111221”要 描述 一个数字字符串,首先要将字符串分割为 最小 数量的组,每个组都由连续的最多 相同字符 组成。然后对于每个组,先描述字符的数量,然后描述字符,形成一个描述 ...
37. 解数独
用二进制位记录冲突信息,用位运算替代效率较低的哈希运算。
//
题目链接-来源:力扣(LeetCode)
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
示例:
输入:board = [[“5”,”3”,”.”,”.”,”7”,”.”,”.”,”.”,”.”],[“6”,”.”,”.”,”1”,”9”,”5”,”.”,”.”,”.”],[“.”,”9”,”8”,”.”,”.”,”.”,”.”,”6”,”.”],[“8”,”.”,”.”,”.”,”6”,”.”,”.”,”.”,”3”],[“4”,”.”,”.”,”8”,”.”,”3”,”.”,”.”,”1”],[“7”,”.”,”.”,”.”,”2”,”.”,”.”,”.”,”6”],[“.”,”6”,”.”,”.”,”.”,”.”,”2”,”8”,”.”],[“.”,”.”,”.”,”4”,” ...
36. 有效的数独
用哈希表验证数独表是否有效。
//
题目链接-来源:力扣(LeetCode)
请你判断一个 9x9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
注意:
一个有效的数独(部分已被填充)不一定是可解的。只需要根据以上规则,验证已经填入的数字是否有效即可。
示例 1:
输入:board =[[“5”,”3”,”.”,”.”,”7”,”.”,”.”,”.”,”.”],[“6”,”.”,”.”,”1”,”9”,”5”,”.”,”.”,”.”],[“.”,”9”,”8”,”.”,”.”,”.”,”.”,”6”,”.”],[“8”,”.”,”.”,”.”,”6”,”.”,”.”,”.”,”3”],[“4”,”.”,”.”,”8”,”.”,”3”,”.”,”.”,”1”],[“7”,”.”,”.”,”.”,”2”,”.”,”.”,”.”,”6”],[“.” ...
35. 搜索插入位置
二分法查找有序数组
//
题目链接-来源:力扣(LeetCode)
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
输入: [1,3,5,6], 5输出: 2
思路:借鉴上一题的思路。我们要找到 target,其实就是要找到 target - 1 右侧的最大值所处的位置。若存在 target,则返回目标索引,否则,该索引也是我们所需的插入位置。时间复杂度:O(n)空间复杂度:O(1)
实现:12345678910111213141516171819class Solution { public int searchInsert(int[] nums, int target) { int index = helper(nums, target - 1); return index; } private int helper(int[] arr, int target) { ...
34. 在排序数组中查找元素的第一个和最后一个位置
二分法查找有序数组
//
题目链接-来源:力扣(LeetCode)
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8输出:[3,4]
思路:已知有序数组,那么大体框架依然是二分法。问题在于,我们二分查找的目标是什么。举例,对于示例中的 nums = [5,7,7,8,8,10], target = 8,我们需要二分地查找第一个 ‘8’(位于下标 3 处)和最后一个 ‘8’(位于下标 4 处)。这样的话,我们需要实现两套逻辑,两套逻辑都要先二分找到 target(或者确定 target 不存在),然后第一个逻辑定位到最左侧的 target 值,第二个定位到最右侧。当然,我们也可以进行适当处理,用同一套逻辑覆盖这个问题。比如,我们的查找目标是实现 「二分查 ...
33. 搜索旋转排序数组
在断层环境中应用二分法
//
题目链接-来源:力扣(LeetCode)
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0输出:4
思路:旋转排序数组,某种程度上是有序的,因此考虑二分法。阻碍我们流畅使用二分法的只有一个因素,就是数组旋转后会出现断层。但是断层有一定的规律。我们可以将数组分为「较大部分」和「较小部分」 ...
32. 最长有效括号
贪心算法判定对称条件。
//
题目链接-来源:力扣(LeetCode)
给你一个只包含 ‘(‘ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例 1:
输入:s = “(()”输出:2解释:最长有效括号子串是 “()”
思路:我们可以遍历每个字符。分别记录正括号和反括号的数量 left 和 right。当两个数字相等时,即可判断整个子串有效,更新最大子串长度。而当右括号的数量较大时,我们就可以将两个数字置 0,因为多余的右括号出现在最右时,左侧的子串已经不能构成合法的字符串。这种考虑有个瑕疵,就是当左侧有很多多余的左括号时,我们的 right 永远小于 left,即使遍历到字符串末尾也无法统计出有效子串长度。此时我们只需反向遍历字符串,将前面的判断规则镜像处理,以左括号数量超越右括号为重置条件。
时间复杂度:O(n)空间复杂度:O(1)
实现:1234567891011121314151617181920212223242526272829303132333435363738394041424344class Solution { ...