leetcode(7) | 查找 && 暴力枚举法

目录

一、查找

Find First and Last Position of Element in Sorted Array

Search Insert Position(easy)

Search in Rotated Sorted Array(mid)

Search in Rotated Sorted Array II (mid)

Find Minimum in Rotated Sorted Array

Find Minimum in Rotated Sorted Array II(hard)

Search a 2D Matrix(easy)

Search a 2D Matrix II(mid)

H-Index II

Median of Two Sorted Arrays(sooooo hard)

二、暴力枚举

Subsets(mid)

Subsets II

Permutations(mid)

Permutations II(hard)

Combinations(mid)

Letter Combinations of a Phone Number(mid)


一、查找

Find First and Last Position of Element in Sorted Array

Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value.

Your algorithm's runtime complexity must be in the order of O(log n).

If the target is not found in the array, return [-1, -1].

Example 1:

Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]

Example 2:

Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]

思路一:严格来讲不是O(logn)

二分查找找到target后,往左右两边查找

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> res;
        int start = 0,end = nums.size()-1;
        while(start <= end){
            int mid = (start + end)/2;
            if(nums[mid] < target) start = mid + 1;
            else if(nums[mid] > target) end = mid - 1;
            else{
                int low = mid,high = mid;
                while(low >= 0 && nums[low] == target) low--;
                res.push_back(low+1);
                while(high < nums.size() && nums[high] == target) high++;
                res.push_back(high-1);
                return res;
            }
        }
        return {-1,-1};
    }
};

思路二:两次二分查找上下边界

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> res(2,-1);
        if(!nums.size()) return res;
        
        // 左边界
        int start = 0,end = nums.size()-1,mid;
        while(start <= end){
            mid = (start + end)/2;
            if(nums[mid] < target) start = mid + 1;
            else if(nums[mid] > target || (mid > 0 && nums[mid] == target && nums[mid-1] == target)) end = mid - 1;
            else break;
        }
        if(nums[mid] != target) return res;

        // 右边界
        res[0] = mid;
        start = mid;end = nums.size()-1;
        while(start <= end){
            mid = (start + end)/2;
            if(nums[mid] < target || (mid < nums.size()-1 && nums[mid] == target && nums[mid+1] == target)) start = mid + 1;
            else if(nums[mid] > target) end = mid - 1;
            else break;
        }
        res[1] = mid;
        return res;
    }
};

我写的比较复杂,别人更加清晰版本

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> res(2, -1);
        int left = 0, right = nums.size() - 1;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) left = mid + 1;
            else right = mid;
        }
        if (nums[right] != target) return res;
        res[0] = right;
        right = nums.size();
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] <= target) left = mid + 1;
            else right= mid;
        }
        res[1] = left - 1;
        return res;
    }
};

 

Search Insert Position(easy)

Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.

You may assume no duplicates in the array.

Example 1:

Input: [1,3,5,6], 5
Output: 2

Example 2:

Input: [1,3,5,6], 2
Output: 1

Example 3:

Input: [1,3,5,6], 7
Output: 4

Example 4:

Input: [1,3,5,6], 0
Output: 0

思路:找得到该数字则返回下标,找不到则返回插入位置,基本就是简单二分查找,找不到情况只需返回start所在位置即可(注意一下一开始没想出来,在分情况讨论)

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int start = 0,end = nums.size()-1,mid;
        while(start <= end){
            mid = (start + end)/2;
            if(nums[mid] < target) start = mid+1;
            else if(nums[mid] > target) end = mid-1;
            else return mid;
        }
        return start;
    }
};

 

Search in Rotated Sorted Array(mid)

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).

You are given a target value to search. If found in the array return its index, otherwise return -1.

You may assume no duplicate exists in the array.

Your algorithm's runtime complexity must be in the order of O(log n).

思路:我的思路是(1)使用二分法找到旋转的点(2)找到点后使用二分法找到该值,虽然确实时间复杂度是O(logn),但是其实做了无用功,因为这两步其实可以合起来的

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(!nums.size()) return -1;
        // 找到旋转点
        int pivot = findPivot(nums,0,nums.size()-1);
        // 查找值
        if(pivot == -1) return findNum(nums,target,0,nums.size()-1);
        else if(nums[0] > target) return findNum(nums,target,pivot+1,nums.size()-1);
        else return findNum(nums,target,0,pivot);
    }
    
    int findPivot(vector<int>& nums,int start,int end){
        // 这注意等于就返回了,否则会数组溢出
        if(start >= end) return -1;
        int mid = (start + end)/2;
        if(nums[mid] > nums[mid+1]) return mid;
        return max(findPivot(nums,start,mid),findPivot(nums,mid+1,end));
    }
    
    int findNum(vector<int>& nums,int target,int start,int end){
        if(start > end) return -1;
        int mid = (start + end)/2;
        if(nums[mid] == target) return mid;
        if(nums[mid] > target) return findNum(nums,target,start,mid-1);
        if(nums[mid] < target) return findNum(nums,target,mid+1,end);
        return -1;
    }
};

合起来,这个需要仔细考虑边界条件,且必须没有重复的数字才可以,否则这里边界判断会出现问题

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if(!nums.size()) return -1;

        int start = 0,end = nums.size()-1,mid;
        while(start <= end){
            mid = (start+end)/2;
            if(nums[mid] == target) return mid;
            // 包含旋转点,先升再降
            // 注意是先看是否是顺序的,再判断与target的情况
            if(nums[mid] < nums[start]){
                if(nums[start] <= target || nums[mid] >= target) end = mid-1;
                else start = mid+1;
            } 
            // 说明这段是顺序的
            else{
                if(nums[mid] < target || nums[start] > target) start = mid+1;
                else end = mid-1;
            }
        }
        return -1;
    }
};

 

Search in Rotated Sorted Array II (mid)

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,0,1,2,2,5,6] might become [2,5,6,0,0,1,2]).

You are given a target value to search. If found in the array return true, otherwise return false.

Example 1:

Input: nums = [2,5,6,0,0,1,2], target = 0
Output: true

Example 2:

Input: nums = [2,5,6,0,0,1,2], target = 3
Output: false

Follow up:

  • This is a follow up problem to Search in Rotated Sorted Array, where nums may contain duplicates.
  • Would this affect the run-time complexity? How and why?

思路:这里相较于上一题,出现了重复的情况,使得012000和000120无法判断2到底是出现在mid的左边还是右边,这里其实只要mid与start相等时,将start++即可(因为当两者相等时,mid又不等于target,所以start也不等于target可以往前移动)

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        if(!nums.size()) return false;

        int start = 0,end = nums.size()-1,mid;
        while(start <= end){
            mid = (start+end)/2;
            if(nums[mid] == target) return true;
            // 包含旋转点,先升再降
            if(nums[mid] < nums[start]){
                if(nums[start] <= target || nums[mid] >= target) end = mid-1;
                else start = mid+1;
            } 
            // 说明这段是顺序的
            else if(nums[mid] > nums[start]){
                if(nums[mid] < target || nums[start] > target) start = mid+1;
                else end = mid-1;
            }
            // 处理重复的情况 012000和000120
            else start++;
        }
        return false;
    }
};

 

Find Minimum in Rotated Sorted Array

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e.,  [0,1,2,4,5,6,7] might become  [4,5,6,7,0,1,2]).

Find the minimum element.

You may assume no duplicate exists in the array.

Example 1:

Input: [3,4,5,1,2] 
Output: 1

Example 2:

Input: [4,5,6,7,0,1,2]
Output: 0

思路一:

这类找旋转点的题目,使用二分法时要注意[2,1]这种情况

class Solution {
public:
    int findMin(vector<int>& nums) {
        int start = 0,end = nums.size()-1,mid;
        while(start <= end){
            mid = (start+end)/2;
            // 顺序
            if(nums[mid] > nums[start]) start = mid;
            else{
                // 下面有两种情况要讨论,注意[2,1]这种情况
                if(mid > 0 && nums[mid-1] > nums[mid]) return nums[mid];
                else if(mid < nums.size()-1 && nums[mid+1] < nums[mid]) return nums[mid+1];
                else end = mid-1;
            }
        }
        // 直接顺序,返回第0个即可
        return nums[0];
    }
};

思路二:网上别人的思路,更加简明

(1)首先要判断这个有序数组是否旋转了,通过比较第一个和最后一个数的大小,如果第一个数小,则没有旋转,直接返回这个数。

(2)如果第一个数大,就要进一步搜索。我们定义left和right两个指针分别指向开头和结尾,还要找到中间那个数,然后和left指的数比较,如果中间的数大,则继续二分查找右半段数组,反之查找左半段。终止条件是当左右两个指针相邻,返回小的那个。

class Solution {
public:
    int findMin(vector<int>& nums) {
        int start = 0,end = nums.size()-1,mid;
        if(nums[start] > nums[end]){
            while(start != end-1){
                mid = (start+end)/2;
                // 顺序
                if(nums[mid] > nums[start]) start = mid;
                else end = mid;
            }
            return min(nums[start],nums[end]);
        }
        // 直接顺序,返回第0个即可
        return nums[0];
    }
};

 

Find Minimum in Rotated Sorted Array II(hard)

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e.,  [0,1,2,4,5,6,7] might become  [4,5,6,7,0,1,2]).

Find the minimum element.

The array may contain duplicates.

Example 1:

Input: [1,3,5]
Output: 1

Example 2:

Input: [2,2,2,0,1]
Output: 0

Note:

思路:

我想的有点问题,这里是参考了别人的写法

1. 如何找中间断开的区间(也就是说旋转过)
我们的目的是要找出存在断口的地方。所以我们可以每次求一下mid的值,把mid 跟左边比一下,如果是正常序,就丢掉左边,反之丢掉右边,不断反复直到找到断口。
分析一下:
比如4 5 6 7 0 1 2  从中间断开后,它是由一个有序跟一个无序的序列组成的。
如果left = 0, right = 6,mid = 3, 那么4, 5, 6, 7 是正序, 7, 0, 1, 2是逆序,所以我们要丢掉左边。这样反复操作,直到数列中只有2个数字,就是断开处,这题我们会得到7,0,返回后一个就可以了。

2. 特别的情况:

如果发现 A.mid > A.left,表示左边是有序,丢掉左边。

如果发现 A.mid < A.left, 表示无序的状态在左边,丢掉右边

如果A.mid = A.left,说明无法判断。这时我们可以把left++,丢弃一个即可。不必担心丢掉我们的目标值。因为A.left == A.mid,即使丢掉了left,还有mid在嘛!

每次进入循环,我们都要判断A.left < A.right,原因是,前面我们丢弃一些数字时,有可能造成余下的数组是有序的,这时应直接返回A.left! 否则的话 我们可能会丢掉解。

就像以下的例子,在1 10 10中继续判断会丢弃1 10.

举例: 10 1 10 10 如果我们丢弃了最左边的10,则1 10 10 是有序的

3.对复杂度的影响:

题目中问到了,对复杂度有何影响:实际上是有的,如果全部的数字相等,我们就退化为O(N),但是平均的复杂度仍然是O(LogN),最后复杂度的大小取决于重复的数字的多少。如果重复字数少,与logN相差不大。

class Solution {
public:
    int findMin(vector<int>& nums) {
        if(!nums.size()) return 0;
        
        int start = 0,end = nums.size()-1,mid;
        // 注意循环条件
        while(start < end-1){
            // 顺序,直接返回
            if(nums[start] < nums[end]) return nums[start];
            
            mid = (start+end)/2;
            if(nums[mid] > nums[start]) start = mid;
            else if(nums[mid] < nums[start]) end = mid;
            else start++;
        }
        // 返回两者中小的那个
        return min(nums[start],nums[end]);
    }
};

 

Search a 2D Matrix(easy)

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:

  • Integers in each row are sorted from left to right.
  • The first integer of each row is greater than the last integer of the previous row.

Example 1:

Input:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 3
Output: true

Example 2:

Input:
matrix = [
  [1,   3,  5,  7],
  [10, 11, 16, 20],
  [23, 30, 34, 50]
]
target = 13
Output: false

思路:使用两次二分法

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(!matrix.size() || !matrix[0].size()) return false;
        
        int start = 0,end = matrix.size()-1,mid,tar;
        while(start <= end){
            mid = (start+end)/2;
            if(matrix[mid][0] == target) return true;
            else if(matrix[mid][0] < target){
                if(mid == matrix.size()-1 || matrix[mid+1][0] > target) break;
                else start = mid+1;
            }
            else end = mid-1;
        }
        if(start > end) return false;
        start = 0;end = matrix[mid].size()-1;
        while(start <= end){
            tar = (start+end)/2;
            if(matrix[mid][tar] == target) return true;
            else if(matrix[mid][tar] < target) start = tar+1;
            else end = tar-1;
        }
        return false;
    }
};

 

Search a 2D Matrix II(mid)

Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:

  • Integers in each row are sorted in ascending from left to right.
  • Integers in each column are sorted in ascending from top to bottom.

Example:

Consider the following matrix:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

Given target = 5, return true.

Given target = 20, return false.

 

思路一:使用两次二分法

时间复杂度O(logn*logn)

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        return binarySearch(matrix,target,0,matrix.size()-1);
    }
    
    bool binarySearch(vector<vector<int>>& matrix, int target,int start,int end){
        if(start > end) return false;
        
        int mid = (start+end)/2;
        int left = 0,right = matrix[mid].size()-1,tar;
        while(left <= right){
            tar = (left+right)/2;
            if(matrix[mid][tar] == target) return true;
            else if(matrix[mid][tar] > target) right = tar-1;
            else left = tar+1;
        }
        return binarySearch(matrix,target,start,mid-1) || binarySearch(matrix,target,mid+1,end);
    }
};

 

思路二:分治法

以题目给出矩阵为例,查找数字5。仔细观察矩阵,最右上角的数字为15,由于矩阵是列递增,所以数字5不可能在最右侧15这一列,我们便可将这一列不予考虑,将范围缩减了一列。 
   
             [1,   4,   7,  11] 
             [2,   5,   8,  12] 
             [3,   6,   9,  16] 
             [10,  13,  14,  17] 
             [18,  21,  23,  26]

  再判断数字11,同样11>511>5,又缩减一列。数字7同样小于5,在缩减一列,那么现在的矩阵变为: 
              [1,   4,] 
             [2,   5] 
             [3,   6] 
             [10,  13] 
             [18,  21] 
  判断数字4时,由于5>45>4,目标值肯定不在4所在的行,去点这一行,在进行判断。 
             [2,   5] 
             [3,   6] 
             [10,  13] 
             [18,  21] 
 Okay,判断数字5,找到目标值返回。 
  
 这种算法时间复杂度O(n)O(n),要优于第一种算法

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.size() == 0) return false;
        
        int row = matrix.size(),col = matrix[0].size();
        int i = 0,j = col-1;
        while(i < row && j >= 0){
            if(matrix[i][j] == target) return true;
            else if(matrix[i][j] > target) j--;
            else i++;
        }
        return false;
    }
};

 

H-Index II

Given an array of citations sorted in ascending order (each citation is a non-negative integer) of a researcher, write a function to compute the researcher's h-index.

According to the definition of h-index on Wikipedia: "A scientist has index h if h of his/her N papers have at least h citations each, and the other N − h papers have no more than citations each."

Example:

Input: citations = [0,1,3,5,6]
Output: 3 
Explanation: [0,1,3,5,6] means the researcher has 5 papers in total and each of them had received 0, 1, 3, 5, 6 citations respectively. 
Since the researcher has 3 papers with at least 3 citations each and the remaining two with no more than 3 citations each, her h-index is 3.

Note:

If there are several possible values for h, the maximum one is taken as the h-index.

Follow up:

  • This is a follow up problem to H-Index, where citations is now guaranteed to be sorted in ascending order.
  • Could you solve it in logarithmic time complexity?

思路:在已经顺序的情况下查找,相对简单一些,这里注意下在 l, r溢出以后,返回n-l(我最开始边界定的太复杂了)

注意:[0],[11,17]这种情况

class Solution {
public:
    int hIndex(vector<int>& citations) {
        if(!citations.size()) return 0;
        int n = citations.size();
        int l = 0,r = n-1,mid;
        while(l <= r){
            mid = (l+r)/2;
            if(citations[mid] > n-mid) r = mid-1;
            else if(citations[mid] < n-mid) l = mid+1;
            else return citations[mid];
        }
        return n-l;
    }
};

 

Median of Two Sorted Arrays(sooooo hard)

There are two sorted arrays nums1 and nums2 of size m and n respectively.

Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)).

You may assume nums1 and nums2 cannot be both empty.

Example 1:

nums1 = [1, 3]
nums2 = [2]

The median is 2.0

Example 2:

nums1 = [1, 2]
nums2 = [3, 4]

The median is (2 + 3)/2 = 2.5

思路:

(1)处理m+n为奇数偶数不同情况

中位数的定义:

如果某个有序数组长度是奇数,那么其中位数就是最中间那个,如果是偶数,那么就是最中间两个数字的平均值。这里对于两个有序数组也是一样的,假设两个有序数组的长度分别为m和n,由于两个数组长度之和 m+n 的奇偶不确定,因此需要分情况来讨论,对于奇数的情况,直接找到最中间的数即可,偶数的话需要求最中间两个数的平均值。为了简化代码,不分情况讨论,我们使用一个小trick,我们分别找第 (m+n+1) / 2 个,和 (m+n+2) / 2 个,然后求其平均值即可,这对奇偶数均适用。加入 m+n 为奇数的话,那么其实 (m+n+1) / 2 和 (m+n+2) / 2 的值相等,相当于两个相同的数字相加再除以2,还是其本身。

(2)时间 O(log (m+n))必须使用二分法

这里我们需要定义一个函数来两个有序数组中找到第K个元素

1. 为了避免产生新的数组从而增加时间复杂度,我们使用两个变量i和j分别来标记数组nums1和nums2的起始位置。

然后来处理一些corner cases:

比如当某一个数组的起始位置大于等于其数组长度时,说明其所有数字均已经被淘汰了,相当于一个空数组了,那么实际上就变成了在另一个数组中找数字,直接就可以找出来了。

还有就是如果K=1的话,那么我们只要比较nums1和nums2的起始位置i和j上的数字就可以了。

2. 难点就在于一般的情况怎么处理?

因为我们需要在两个有序数组中找到第K个元素,为了加快搜索的速度,我们要使用二分法,那么对谁二分呢,数组么?其实要对K二分,意思是我们需要分别在nums1和nums2中查找第K/2个元素

注意这里由于两个数组的长度不定,所以有可能某个数组没有第K/2个数字,所以我们需要先check一下,数组中到底存不存在第K/2个数字,如果存在就取出来,否则就赋值上一个整型最大值。如果某个数组没有第K/2个数字,那么我们就淘汰另一个数字的前K/2个数字即可。

有没有可能两个数组都不存在第K/2个数字呢,这道题里是不可能的,因为我们的K不是任意给的,而是给的m+n的中间值,所以必定至少会有一个数组是存在第K/2个数字的。最后就是二分法的核心啦,比较这两个数组的第K/2小的数字midVal1和midVal2的大小,如果第一个数组的第K/2个数字小的话,那么说明我们要找的数字肯定不在nums1中的前K/2个数字,所以我们可以将其淘汰,将nums1的起始位置向后移动K/2个,并且此时的K也自减去K/2,调用递归。反之,我们淘汰nums2中的前K/2个数字,并将nums2的起始位置向后移动K/2个,并且此时的K也自减去K/2,调用递归即可

class Solution {
public:
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int m = nums1.size(),n = nums2.size();
        int tar1 = (m+n+1)/2,tar2 = (m+n+2)/2;
        return (findkth(nums1,nums2,0,0,tar1)+findkth(nums1,nums2,0,0,tar2))/2.0;
    }
    
    // 这里k从1开始
    int findkth(vector<int>& nums1,vector<int>& nums2,int s1,int s2,int k){
        // 注意这里不能用s1 > nums1.size()-1,因为nums1.size()是无符号整数,当其为0时,-1会溢出无限大
        if(s1 >= nums1.size()) return nums2[s2+k-1];
        if(s2 >= nums2.size()) return nums1[s1+k-1];
        if(k == 1) return min(nums1[s1],nums2[s2]);
        
        // 超过长度时的处理
        int mid_val1 = s1+k/2-1 < nums1.size()? nums1[s1+k/2-1]:INT_MAX;
        int mid_val2 = s2+k/2-1 < nums2.size()? nums2[s2+k/2-1]:INT_MAX;

        
        if(mid_val1 < mid_val2) return findkth(nums1,nums2,s1+k/2,s2,k-k/2);
        else return findkth(nums1,nums2,s1,s2+k/2,k-k/2);
    }
};

 

二、暴力枚举

基本思路均为在上一次的结果上再去加一个不断的走下去

Subsets(mid)

Given a set of distinct integers, nums, return all possible subsets (the power set).

Note: The solution set must not contain duplicate subsets.

Example:

Input: nums = [1,2,3]
Output:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

思路:难度不大,每次增加一个数,只需要在所有的数组后面增加一个数,在把它们加进去即可

class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> res;
        res.push_back({});
        for(int i = 0;i < nums.size();i++){
            res = build_sub(res,nums[i]);
        }
        return res;
    }
    
    vector<vector<int>> build_sub(vector<vector<int>> &res,int num){
        vector<int> cur;
        // 注意这里要使用res_size先保存未增加前的大小,否则会陷入死循环
        auto res_size = res.size();
        for(int i = 0;i < res_size;i++){
            cur = res[i];
            cur.push_back(num);
            res.push_back(cur);
        }
        return res;
    }
};

 

Subsets II

Given a collection of integers that might contain duplicatesnums, return all possible subsets (the power set).

Note: The solution set must not contain duplicate subsets.

Example:

Input: [1,2,2]
Output:
[
  [2],
  [1],
  [1,2,2],
  [2,2],
  [1,2],
  []
]

思路:,拿题目中的例子[1 2 2]来分析,当处理到第一个2时,此时的子集合为[], [1], [2], [1, 2],而这时再处理第二个2时,如果在[]和[1]后直接加2会产生重复,所以只能在上一个循环生成的后两个子集合后面加2

class Solution {
public:
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<vector<int>> res;
        res.push_back({});
        for(int i = 0;i < nums.size();i++){
            int count = 1;
            while(i+1 < nums.size() && nums[i] == nums[i+1]){
                i++;count++;
            }
            res = build_sub(res,nums[i],count);
        }
        return res;
    }
    
    vector<vector<int>> build_sub(vector<vector<int>> &res,int num,int count){
        int res_size = res.size();
        vector<int> cur;
        for(int i = 0;i < res_size;i++){
            cur = res[i];
            for(int j = 0;j < count;j++){
                cur.push_back(num);
                res.push_back(cur);
            }
        }
        return res;
    }
};

 

Permutations(mid)

Given a collection of distinct integers, return all possible permutations.

Example:

Input: [1,2,3]
Output:
[
  [1,2,3],
  [1,3,2],
  [2,1,3],
  [2,3,1],
  [3,1,2],
  [3,2,1]
]

思路:我没想出来,这是网上的思路

当n=1时,数组中只有一个数a1,其全排列只有一种,即为a1

当n=2时,数组中此时有a1a2,其全排列有两种,a1a2a2a1,那么此时我们考虑和上面那种情况的关系,我们发现,其实就是在a1的前后两个位置分别加入了a2

当n=3时,数组中有a1a2a3,此时全排列有六种,分别为a3a1a2, a1a3a2, a1a2a3a3a2a1, a2a3a1, a2a1a3
。那么根据上面的结论,实际上是在a1a2和a2a1的基础上在不同的位置上加入a3而得到的。

_ a1 _ a2 _ : a3a1a2, a1a3a2, a1a2a3

_ a2 _ a1 _ : a3a2a1, a2a3a1, a2a1a3

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        if (nums.empty()) return vector<vector<int>>(1, vector<int>());
        vector<vector<int>> res;
        
        int first = nums[0];
        nums.erase(nums.begin());
        vector<vector<int>> last = permute(nums);
        // 上一次的结果,加入first时,这些都不要,只需要加入这些+first后的结果
        // 故这里last和res不能同,res必须为空的一个变量结果
        int last_size = last.size();
        for(auto &cur:last){
            for(int j = 0;j <= cur.size();j++){
                cur.insert(cur.begin()+j,first);
                res.push_back(cur);
                cur.erase(cur.begin()+j);
            }
        }
        return res;
    }
};

思路二:

使用同下面一样的结果空间树,减少了一个剪枝条件

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<bool> visit(nums.size(),0);
        vector<int> out;
        vector<vector<int>> res;
        // out存放一次的结果,res存放总结果
        // visit用来记录哪些数被访问过
        dfs(nums,0,visit,out,res);
        return res;
    }
    
    void dfs(vector<int>& nums,int level,vector<bool>visit,vector<int> &out,vector<vector<int>> &res){
        if(level == nums.size()){ res.push_back(out); return;} 
        
        for(int i = 0; i < nums.size();i++){
            // 上一层中已被访问,剪枝跳过
            if(visit[i]) continue;
            visit[i] = 1;
            out.push_back(nums[i]);
            dfs(nums,level+1,visit,out,res);
            out.pop_back();
            visit[i] = 0;
        }
    }
};

 

Permutations II(hard)

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

Example:

Input: [1,1,2]
Output:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

思路:参考网上的思路

(1)dfs 代码模板

void dfs(){
    // 边界,终止条件
    if(expression1) 
        do something

    for(int i=0;i<N;i++){
        // 剪枝条件
        if(expression2)
            do something
        dfs();
    }
}

expression1通常是终止条件。expression2通常是剪枝条件

(2)构建如图所示的结果空间树

红色叉的部分表示剪枝。从上述图来看,或者称为森林还恰当一些,先不管这个。这里关键要理解一点,如何去重?比如第三层的后面两个1是如何去掉的?

(1)保证不重复使用数字:使用visit数组

由于递归的for都是从0开始,难免会重复遍历到数字,而全排列不能重复使用数字,意思是每个nums中的数字在全排列中只能使用一次(当然这并不妨碍nums中存在重复数字)。不能重复使用数字就靠visited数组来保证,这就是第一个if剪枝的意义所在。

if(visit[i]) continue;

(2)去重

关键来看第二个if剪枝的意义,这里说当前数字和前一个数字相同,且前一个数字的visited值为0的时候,必须跳过。这里的前一个数visited值为0,并不代表前一个数字没有被处理过,而是递归结束后恢复状态时将visited值重置为0了

if(i > 0 && nums[i]==nums[i-1] && !visit[i-1]) continue;

完整代码:

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        vector<bool> visit(nums.size(),0);
        vector<int> out;
        vector<vector<int>> res;
        // out存放一次的结果,res存放总结果
        // visit用来记录哪些数被访问过
        dfs(nums,0,visit,out,res);
        return res;
    }
    
    void dfs(vector<int>& nums,int level,vector<bool>visit,vector<int> &out,vector<vector<int>> &res){
        if(level == nums.size()){ res.push_back(out); return;} 
        
        for(int i = 0; i < nums.size();i++){
            // 上一层中已被访问,剪枝跳过
            if(visit[i]) continue;
            // 这一层中已被访问,!visit[i-1]是表示i-1在这层被访问以后又置为0了,故这个数已经被访问过,剪枝
            if(i > 0 && nums[i]==nums[i-1] && !visit[i-1]) continue;
            visit[i] = 1;
            out.push_back(nums[i]);
            dfs(nums,level+1,visit,out,res);
            // 这里pop,置0,换别的节点作为这一层的节点,去递归,注意不要遗漏
            out.pop_back();
            visit[i] = 0;
        }
    }
};

 

Combinations(mid)

Given two integers n and k, return all possible combinations of k numbers out of 1 ... n.

Example:

Input: n = 4, k = 2
Output:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

思路:这里不需要visit数组,保证不重复只需要使得每个数字都只与它后面的数字去进行组合即可,同时也可防止组合的重复,如[1,2]和[2,1]

class Solution {
public:
    vector<vector<int>> combine(int n, int k) {
        vector<vector<int>> res;
        vector<int> out;
        if(k == 0) return res;
        dfs(n,k,0,out,res);
        return res;
    }
    
    void dfs(int n,int k,int level,vector<int> &out,vector<vector<int>> &res){
        if(level == k) {res.push_back(out);return;}
        
        int start = out.empty()? 1:out[out.size()-1]+1;
        for(int i = start;i <= n;i++){
            out.push_back(i);
            dfs(n,k,level+1,out,res);
            out.pop_back();
        }
    }
};

 

Letter Combinations of a Phone Number(mid)

Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent.

A mapping of digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.

Example:

Input: "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].

思路:这个难度比前面的低,只要顺序组合即可

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        if(digits.empty()) return {};
        string num2char[] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
        vector<string> res,out;
        res.push_back("");
        for(int i = 0;i < digits.size();i++){
            int idx = digits[i]-'0';
            string letters = num2char[idx];
            // 这里每次清空,因为只保留最后的结果,前面的组合不需要保留
            out = res;
            res = {};
            for(auto letter:letters){
                for(int j = 0;j < out.size();j++){
                    string cur = out[j];
                    cur += letter;
                    res.push_back(cur);
                }
            }
        }
        return res;
    }
};

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值