2.1 数据结构---数组

(一)数组

总结:

  利用序:理解二分查找

  利用前缀和:查找、计算、排序

  理解数组: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】回溯算法系列




原文地址:https://www.cnblogs.com/nxf-rabbit75/p/10275635.html