(一)数组
总结:
利用序:理解二分查找
利用前缀和:查找、计算、排序
理解数组:map
用数组实现高级数据结构:一般树(存每个节点的父亲【并查集】);二叉树(下标从1开始a[i]的儿子是a[i*2]和a[i*2+1])(堆)
简单题:分治法求逆序对数;有序数组归并;两个有序数组的中位数;两头扫的方法(2-SUM,3-SUM)
(二)算法
一、排列、组合、子集
1.子集
(1)Leetcode78:子集
题目描述:给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例: 输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
思路:
1.回溯法
2.Python的extend功能
方法1代码:
class Solution: def subsets_way3(self,nums): if not nums: return [] res = [] lens = len(nums) def helper(idx,temp_list): res.append(temp_list) for i in range(idx,lens): helper(i+1,temp_list+[nums[i]]) helper(0,[]) return res
方法2代码:
def subsets_way1(nums): result = [[]] for x in nums: result.extend([subset + [x] for subset in result]) return result
(2)Leetcode90:子集II
题目描述:给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
这道题与78题区别在于,数组里包含了重复数组,一般有3种办法:
1.用上面一题方法,把相同的不加入
2.在递归时候,加个判断,发现此位置和前位置相等,就跳过(必须是有序列表)
3.先按78的思路,把重复的也算出来,再用dict遍历一遍,去重(这个方法比较笨重)
方法1、2代码:
class Solution1: def subsets2(self,nums): if not nums: return [] n = len(nums) res = [] nums.sort() def helper(idx,temp_list): if temp_list not in res:#相同的不加入 res.append(temp_list) for i in range(idx,n): helper(i+1,temp_list+[nums[i]]) def helper2(idx,temp_list): res.append(temp_list) for i in range(idx,n): if i > idx and nums[i] == nums[i-1]: continue helper(i+1,temp_list+[nums[i]]) helper(0,[]) return res
方法3代码:
class Solution: def subsetsWithDup(self, nums): """ :type nums: List[int] :rtype: List[List[int]] """ result = [[]] for x in nums: result.extend([subset + [x] for subset in result]) # print("result",result) return self.transfer(result) def transfer(self, res):#去重 Res = {} for key,value in enumerate(res): res[key] = sorted(value,reverse=True) for key,value in enumerate(res): if tuple(value) not in Res: Res[tuple(value)] = 1 Res[tuple(value)] += 1 res = [list(key) for (key,value) in Res.items()] return res
2.全排列
(1)Leetcode46:全排列
题目描述:给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
思路:
1.直接调用Python的itertools.permutations包,不过返回的是tuple类型的;
2.回溯法,递归得到结果
(1)n个元素的全排列=(n-1个元素的全排列)+(另一个元素作为前缀);
(2)出口:如果只有一个元素的全排列,则说明已经排完,则输出数组;
(3)不断将每个元素放作第一个元素,然后将这个元素作为前缀,并将其余元素继续全排列,等到出口,出口出去后还需要还原数组;
方法1代码:
import itertools def permutations1(nums): return list(itertools.permutations(nums,len(nums)))
方法2代码:
res = [] def permutations2(s,k): if k == len(s): res.append(s.copy()) # print(s) #第0个元素和后面的每个元素交换 #第1个元素和后面的每个元素交换 #...... #k:当前的交换位置(关注点),与其后的元素交换 for i in range(k,len(s)): s[i],s[k] = s[k],s[i]#试探 permutations2(s,k+1) s[i], s[k] = s[k], s[i] # 回溯 #比如123交换之后变为132,那么第二次循环的时候应该从123开始, #而不是从132开始,所以要回溯
(2)Leetcode47:全排列II
题目描述:给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2] 输出: [ [1,1,2], [1,2,1], [2,1,1] ]
两种思路:
1.一种是列出所有的排列,再去重(笨重的方法)
2.递归,交换之前先判断是否已经出现重复的元素,若存在,就不交换
方法1代码:
import itertools class Solution: def permuteUnique(self,nums): return list(itertools.permutations(nums,len(nums))) def unique_permutation(self,candidates):#去重,把每个排列放入字典中,去掉重复的 candidates.sort() result = [] Re = [] dict_ = {} if len(result) == 1: return result else: for i in candidates: if i not in dict_: dict_[i] = 1 else: dict_[i] += 1 for item in dict_: Re.append(item) return Re
方法2代码:依旧全排列的思路,只不过需要定义一个set来存储已经交换过的元素值,也就是已经做过某个排列的首元素的值存放在set中,避免重复这种情形。
import copy class Solution1(object): def permuteUnique(self, nums): self.res = [] self.f(nums,k=0) return self.res def f(self,num_list, k): begindata = set() # 每层递归都要重置 if k == len(num_list): print(num_list) tmp = copy.copy(num_list) self.res.append(tmp) for i in range(k, len(num_list)): if num_list[i] in begindata: # 已经做过某个排列首元素,则跳过这种情况 continue begindata.add(num_list[i]) num_list[i],num_list[k] = num_list[k],num_list[i]#交换两个元素的位置 self.f(num_list, k + 1) num_list[i], num_list[k] = num_list[k], num_list[i]#回溯 S = Solution1() res = S.permuteUnique(nums=[1,2,2]) print(res)
3.组合总和
(1)Leetcode39:组合总和
题目描述:给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
说明:所有数字(包括 target)都是正整数。解集不能包含重复的组合。
示例:
输入: candidates = [2,3,6,7], target = 7, 所求解集为: [ [7], [2,2,3] ]
思路1:回溯法递归求解
class Solution: def combinationSum(self, candidates, target): if not candidates: return [] if min(candidates) > target: return [] candidates.sort() res = [] def helper(candidates_,target_,temp_list): if target_ == 0: # print(temp_list) res.append(temp_list) if target < 0: return for i in range(len(candidates_)): # print(temp_list,candidates_[i]) if candidates_[i] > target_: break helper(candidates_[i:],target_-candidates_[i],temp_list+[candidates_[i]]) helper(candidates,target,[]) return res
(2)Leetcode40:组合总和II
题目描述:给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用一次。
说明:所有数字(包括目标数)都是正整数。解集不能包含重复的组合。
示例:
输入: candidates = [10,1,2,7,6,1,5], target = 8, 所求解集为: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]
思路1:回溯法,递归求解,因为不能重复使用列表中的元素,所以下一次递归要从下个元素开始(不包括自身)
class Solution1: def combinationSum2(self, candidates, target): if not candidates: return [] if min (candidates) > target: return [] candidates.sort () res = [] def helper(candidates_, target_, temp_list): if target_ == 0 and temp_list not in res: # print(temp_list) res.append (temp_list) if target < 0: return for i in range (len (candidates_)): # print(temp_list,candidates_[i]) if candidates_[i] > target_: break helper (candidates_[i+1:], target_ - candidates_[i], temp_list + [candidates_[i]]) helper(candidates, target, []) return res
4.分割回文串
(1)Leetcode131:分割回文串
题目描述:给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。返回 s 所有可能的分割方案。
示例:
输入: "aab" 输出: [ ["aa","b"], ["a","a","b"] ]
思路1:回溯法
题目要求返回所有的可能方案,所以依旧采用回溯算法。循环查看当前字符串的每一个可切分位置位;判断若在当前位置切分,前半部分是否是回文串。若是,则将前半部分存入当前解,并递归分割后半部分。
例如输入字符串为示例:
|a |a |b |
0 1 2 3
首先判断分割位1,发现前半部分‘a’是回文串,将‘a’存入temp_list,将后半部分‘ab’用作递归。当遍历到字符串尾端时,递归结束,将temp_list加入的res,最终返回res.
class Solution (object): def partition(self, s): """ :type s: str :rtype: List[List[str]] """ if not s: return [] res = [] lens = len(s) def helper(idx,temp_list): if idx == lens: res.append(temp_list) for i in range(idx,lens): if s[idx:i+1] == s[idx:i+1][::-1]: helper(i+1,temp_list+[s[idx:i+1]]) helper(0,[]) return res S = Solution() res = S.partition(s='aab') print(res)
二、动态规划
三、查找和排序
1.二分查找
(1)局部极小值
2.元素交换
(1)第一个缺失的正整数
3.排序、中位数
(1)元素最大间距离
(2)众数
4.归并
5.位运算
(1)只出现一次的数
6.前缀和的应用
参考文献:
【1】回溯算法系列