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 { ...
31. 下一个排列
找规律
//
题目链接-来源:力扣(LeetCode)
实现获取 下一个排列 的函数,算法需要将给定数字序列重新排列成字典序中下一个更大的排列。
如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。
必须 原地 修改,只允许使用额外常数空间。
示例 1:
输入:nums = [1,2,3]输出:[1,3,2]
思路:考虑「下一个排列」的特征。
首先,可以想到,如果数组为完全逆序,则意味着我们找不到下一个排序了。此时我们就必须重排数组。
反之,如果数组存在顺序对(即存在 nums[i] < nums[i + 1]),我们可以判定数组中一定存在「下一个排列」。
对于字典序,靠前的元素的权重大于靠后的元素,于是我们应该优先考察靠后元素。我们可以从后往前遍历数组,跳过逆序对,当我们遍历到第一组顺序对(记为 nums[i],nums[i + 1])时,我们有如下推论:
nums[i + 1] 以及之后的元素必为逆序排列, 且 nums[i + 1] 从下标 i 到 length - 1 这个局部的最大值。(因为我们是从后向前遍历的,否则就不会停在这 ...
30. 串联所有单词的子串
利用分组遍历精简字符串的匹配。
//
30.题目链接-来源:力扣(LeetCode)
给定一个字符串 s 和一些 长度相同 的单词 words 。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。
注意子串要与 words 中的单词完全匹配,中间不能有其他字符 ,但不需要考虑 words 中单词串联的顺序。
示例 1:
输入:s = “barfoothefoobarman”, words = [“foo”,”bar”]输出:[0,9]解释:从索引 0 和 9 开始的子串分别是 “barfoo” 和 “foobar” 。输出的顺序不重要, [9,0] 也是有效答案。
思路:本题可以先从暴力解着手开展思路。记 words 的单词个数为 wNum, 每个单词长度为 wLen。我们可以遍历 s 中的每一位,以之为起点开始向后查找单词,总共查找 wNum 次,每次跨越 wLen 个字符,用一个哈希表统计单词出现次数,并与 words 中的次数进行比较,若一致则可将此位加入结果列表中。我们如何优化暴力解呢?考虑如下字符串实例 s = “wo ...
