LeetCode数组简单和中等难度

  • 73
  • 75
  • 81
  • 152

2020.8.31

1 两数之和

看错输入样例,以为是排序的
map底层实现是红黑树,而unordered_map为哈希表
暴力解法,两层循环遍历找到两数

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> result;
        int len = nums.size();
        for(int i = 0; i < len - 1; i++){
            for(int j = i + 1; j < len; j++){
                if(nums[i] + nums[j] == target){
                    result.push_back(i);
                    result.push_back(j);
                    return result;
                }
            }
        }
        return result;
    }
};

map映射 把vector->map中,再遍历map,以找到符合target的两个数(对于两个相同的数,如[3,0,3]是在映射到map之前,判断map中有没有存在这个key,并且2 * key是不是为target,?那为什么不每次都查找map中有无目标值呢?
代码已改,两个for循环
hash映射 遍历vector,并插入到unorder_map中(即hash表),但在此之前判断unorder_map中有无目标值,若有则已找到结果;(这个对于两个相同的数比较好判断)

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> result;
        unordered_map<int, int> mymap;
        unordered_map<int, int>::iterator it;
        int len = nums.size();
        for(int i = 0; i < len; i++){
            it = mymap.find(target - nums[i]);
            if(it != mymap.end()){
                result.push_back(it->second);
                result.push_back(i);
                break;
            }
            
            else 
                mymap.insert(pair<int, int>(nums[i], i));
        }
        return result;
    }
};

2020.9.1

11 乘最多水的容器

最开始的想法是两层for循环遍历,找到每一种情况,但是这样时间复杂度过高。
tag中有个关键词是two-pointer就想到了首尾指针,并向中间靠拢。那到底移左边的还是右边的呢?
可以看到,每次只移动一步,那么宽度是都是减小1,指针移动后的容器面积增大=移动后的容器的高尽量大。如果left的height高于right的,短板在于right选择高度较小的指针移动,而保留left可能得到更优的结果。

class Solution {
public:
    int maxArea(vector<int>& height) {
        int l = 0, r = height.size() - 1;
        int area = 0, temp = 0;
        while(l < r){
            if(height[l] > height[r]){
                temp = height[r] * (r - l);
                r--;
            }
            else{
                temp = height[l] * (r - l);
                l++;
            }
            area = area > temp ? area : temp;
        }
        return area;
    }
};

2020.9.4

15 三数之和

三个数之和和两个数之和有相似之处,先确定第一个数字,然后求得所有的两数之和(第一个数字的相反数)的组合。
开始的思路很混乱,因为有重复的数字,并且有多个组合,所有利用到了排序,变成有序数组。

  • 1 确定第一个数字a,在之后剩下的数组中找到两数b、c组合;但是如果之后再有相同的数字a,可能会得到重复的组合。
  • 2利用双指针,若b+c的值大于则c往左移,小于b往右移,若相等还要判断之前是否出现过相同的值。
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        vector<vector<int>>::iterator it;
        sort(nums.begin(), nums.end());
        int l, r, len = nums.size() - 1;
        for(int i = 0; i < len - 1; i++){
            if(i == 0 || nums[i] != nums[i - 1]){
                l = i + 1;
                r = len;
                while(l < r){
                    if(nums[l] + nums[r] > - nums[i]){
                        r--;
                    }else if(nums[l] + nums[r] < -nums[i]){
                        l++;
                    }else{
                        if(l != i + 1 && nums[l] == nums[l - 1])
                            l++;
                        else if(r != len && nums[r] == nums[r + 1])
                            r--;
                        else{
                            vector<int>t {nums[i], nums[l], nums[r]};
                            result.push_back(t);
                            r--;
                            l++;
                        }
                        
                    }
                }
            }
        }
        return result;
    }
};

2020.9.5

16 最接近的三数之和

上一个题目就是三数之和,这个是最接近的三数之和,所有可以从target,target+1,····,target+n开始测试。
借用了上一题的代码,将排序提取出来,并且无需判断是否重复组合,当有符合条件的组合返回true。
复杂度为O(N3)

class Solution {
public:
    bool threeSum(vector<int>& nums, int t) {
        int l, r, len = nums.size() - 1;
        for(int i = 0; i < len - 1; i++){
            if(i == 0 || nums[i] != nums[i - 1]){
                l = i + 1;
                r = len;
                while(l < r){
                    if(nums[l] + nums[r] > t - nums[i]){
                        r--;
                    }else if(nums[l] + nums[r] < t -nums[i]){
                        l++;
                    }else{
                        return true;  
                    }
                }
            }
        }
        return false;
    }
    int threeSumClosest(vector<int>& nums, int target) {
        int i = 0;
        sort(nums.begin(), nums.end());
        while(1)
        {
            if(threeSum(nums, target + i) == true)
                return target + i;
            if(threeSum(nums, target - i) == true)
                return target - i;
            i++;
        }
        return 0;
    }
};

也可以直接双层遍历
当遇到更小的sum值就更新,复杂度为O(N2)

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        int l, r, len = nums.size() - 1, sum, best = target + 10000;
        for(int i = 0; i < len - 1; i++){
            if(i == 0 || nums[i] != nums[i - 1]){
                l = i + 1;
                r = len;
                while(l < r){
                    sum = nums[i] + nums[l] + nums[r];
                    if(abs(best - target) > abs(sum - target))
                        best = sum;
                    if(sum > target){
                        r--;
                    }else if(sum < target){
                        l++;
                    }else{
                        return target;  
                    }
                }
            }
        }
        return best;
    }

官方解法

18 四数之和

第一次for循环确定a的值
第二次for循环确定b的值
最内层用双指针,找到c和d的值
并且判断是否有重复的情况

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        int l, r, len = nums.size() - 1, sum;
        for(int i = 0; i < len - 2; i++){
            if(i == 0 || nums[i] != nums[i - 1]){
                for(int j = i + 1; j < len - 1; j ++){
                    if(j == i + 1 || nums[j] != nums[j - 1]){
                        l = j + 1;
                        r = len;
                        while(l < r){
                            sum = nums[l] + nums[r] + nums[i] + nums[j];
                            if(sum > target){
                                r--;
                            }else if(sum < target){
                                l++;
                            }else{
                                if(l != j + 1 && nums[l] == nums[l - 1])
                                    l++;
                                else if(r != len && nums[r] == nums[r + 1])
                                    r--;
                                else{
                                    vector<int>t {nums[i], nums[j], nums[l], nums[r]};
                                    result.push_back(t);
                                    r--;
                                    l++;
                                }
                            }                               
                        }
                    }
                    
                }
            }
        }
        return result;
    }
};

26 删除排序数组中的重复项

即利用双指针,若pos与pos1指向的值不相等,则可以赋值到pos+1;
但是忽略了数组长度为0的情况

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.size() == 0)
            return 0;
        int len = 1;
        for(int i = 0; i < nums.size(); i++){
            if(nums[len - 1] != nums[i])
                nums[len++] = nums[i];
        }
        return len;
    }
};

27 移除元素

将l从左至右指向等于val的值,将r从右至左指向非val的值,并且交换;
忽略了数组的值全为val的情况,即初始代码的终止情况可能l和r会指向同一个值,但若这个值为val也应该移除,那么应该while中判断条件为l<=r

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int l = 0, r = nums.size() - 1, temp;
        while(l <= r){
            if(nums[l] == val && nums[r] != val){
                temp = nums[l];
                nums[l] = nums[r];
                nums[r] = temp;
                l++;
                r--;
            }
            if(nums[l] != val)
                l++;
            if(nums[r] == val)
                r--;
        }
        return r + 1;
    }
};

2020.9.6

31 下一个排列

数组倒着查找nums[j] > nums[j-1]的情况

  • j = length-1时,数组完全正序,只要将最后两个数直接交换;
  • j = 0时,数组完全倒序,将数组翻转;
  • 其他情况,j ~ length是倒序,找到其中最接近并大于nums[j-1]的值,并与之交换;之后再将j ~ length翻转。
class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int len = nums.size();
        if(len == 1)
            return;
        
        int t, j;
        for(j = len - 1; j > 0; j--){
            if(nums[j] > nums[j - 1])
                break;
        }
        if(j == len - 1){
            t = nums[len - 1];
            nums[len - 1] = nums[len - 2];
            nums[len - 2] = t;
        }
        else if(j == 0){
            for(int i = 0; i < len / 2; i ++){
                t = nums[i];
                nums[i] = nums[len - i - 1];
                nums[len - i - 1] = t;
            }
        }
        else{
            for(int i = j; i < len; i ++){
                if((i == len -1 && nums[i] > nums[j - 1]) || (nums[i] > nums[j - 1] && nums[i + 1] <= nums[j - 1])){
                    t = nums[i];
                    nums[i] = nums[j - 1];
                    nums[j - 1] = t;
                    break;
                }
            }
            for(int i = j; i < j + (len - j) / 2; i++){
                t = nums[i];
                nums[i] = nums[len - i - 1 + j];
                nums[len - i - 1 + j] = t;
            }
        }
        return;
    }
};

33 搜索旋转排序数组

先找到是围绕哪个点旋转,即坐标i-1;
之后当成普通的二分搜索,但mid的坐标要加上i;
可是效率不高 是log(N)

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int len = nums.size(), i;
        int l = 0, r = len - 1, mid, m;
        for(i = 0; i < len - 1; i++){
            if(nums[i] > nums[i + 1])
                break;
        }
        i++;
        while(l <= r){
            mid = l + (r - l) / 2;
            m = (mid + i) % len;
            if(nums[m] == target)
                return m;
            else if(nums[m] > target)
                r = mid - 1;
            else
                l = mid + 1;
        }
        return -1;
    }
};

官方文档是用到了二分法,即分为排序和未排序的两段

  • 当nums[mid] > target 时,若nums[r] > nums[mid] 证明后半段时有序的,肯定不存在;若nums[r] < target,那么后半段无序,即从大与到小于;故存在在前半段
  • 当nums[mid] < target 时,若nums[l] < nums[mid] 证明前半段时有序的,肯定不存在;若nums[l] > target,那么前半段无序,即从大与到小于;故存在在前后段
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int len = nums.size();
        int l = 0, r = len - 1, mid;
        while(l <= r){
            mid = l + (r - l) / 2;
            if(nums[mid] == target)
                return mid;
            else if(nums[mid] > target){
                if(nums[r] >= nums[mid]|| nums[r] < target)
                    r = mid - 1;
                else
                    l = mid + 1;
            }
            else{
                if(nums[l] <= nums[mid] || nums[l] > target)
                    l = mid + 1;
                else
                    r = mid - 1; 
            }
         return -1;
      	}
    }
};

2020.9.7

34 在排序数组中查找元素的第一个和最后一个位置

题目要求时间复杂度为O(log n),所以肯定要用到二分法;
首先用普通的二分搜索找到nums[mid]=target,且记住l、r;
如果r<l那么不存在target;
否则,分为l ~ mid 以及mid ~ right;并且当mid处的值为target,mid-1处不为target,此时mid为其左边界;右边界同理;

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int len = nums.size() - 1, l = 0, r = len, mid;
        while(l <= r){
            mid = l + (r - l) / 2;
            if(target == nums[mid])
                break;
            else if(target > nums[mid])
                l = mid + 1;
            else
                r = mid - 1;
        }
        if(l > r)
            return vector<int> {-1, -1};
        int ml = mid, mr = mid;
        while(l <= ml){
            mid = l + (ml - l) / 2;
            if(nums[mid] == target){
                if(mid != 0 && nums[mid - 1] == target)
                    ml = mid - 1;
                else{
                    ml = mid;
                    break;
                }
            }
            else
                l = mid + 1; 
        }
        while(mr <= r){
            mid = mr + (r - mr) / 2;
            if(nums[mid] == target){
                if(mid != len && nums[mid + 1] == target)
                    mr = mid + 1;
                else{
                    mr = mid;
                    break;
                }
            }
            else
                r = mid - 1;
        }
        return vector<int> {ml, mr};
    }
};

官方解法
即直接寻找左右边界
如找左边界时,mid处为大于等于target,应继续向左找,r=mid-1;否则l=mid+1;当l大于r时,l为所求;右边同理;

2020.9.8

35 搜索插入位置

当存在这个值时,普通的二分搜索返回位置即可;
不存在的时候,nums[pos-1]<target<nums[pos]仍然返回pos;while循环结束的条件为l大于r,考虑最后一次循环移动的是r,那么mid处的值必然大于target,r=mid-1(l-1),此时mid=l;同理,即l为pos;

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

39 组合总和

回溯法,不合适就返回上一步,并且通过约束条件,剪枝;
遍历candidates数组,用temp记录当前选取的数值;当等于target时,记录该方案;大于target时,由于数组时有序的,之后的数字会更大,那么直接返回上一层,剪枝;否则,加入该candidates[i],递归到下一层,并且结束之后,可以回退到该层,继续选择candidates[i+1];这样实现全部遍历;

class Solution {
public:
    void combinationSumHelp(vector<vector<int>> & result, vector<int> & candidates, vector<int> temp, int target, int pos){
        for(int i = pos; i < candidates.size(); i++){
            if(candidates[i] == target){
                temp.push_back(candidates[i]);
                result.push_back(temp);
                return;
            }
            else if( candidates[i] < target){
                temp.push_back(candidates[i]);
                combinationSumHelp(result, candidates, temp, target - candidates[i], i);                
                temp.pop_back();
            }
            else
                return;
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<vector<int>> result;
        vector<int> temp;
        sort(candidates.begin(), candidates.end());
        combinationSumHelp(result, candidates, temp, target, 0);
        return result;
    }
};

2020.9.9

40 组合总和 II

与39题类似,每个数字只能使用一次,故选择第i个数字,之后递归只能用i+1及之后的数字;candidates里面有重复的数字,需要在递归之前先判断是否candidates[i]=candidates[i-1],若等于,result中肯定出现过该组合;

class Solution {
public:
    void combinationSum2Help(vector<vector<int>> & result, vector<int> & candidates, vector<int> temp, int target, int pos){
        for(int i = pos; i < candidates.size(); i++){
            if(i != pos && candidates[i] == candidates[i - 1])
                continue;
            if(candidates[i] == target){
                temp.push_back(candidates[i]);
                result.push_back(temp);
                return;
            }
            else if( candidates[i] < target){
                temp.push_back(candidates[i]);
                combinationSum2Help(result, candidates, temp, target - candidates[i], i + 1);                
                temp.pop_back();
            }
            else
                return;
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<vector<int>> result;
        vector<int> temp;
        sort(candidates.begin(), candidates.end());
        combinationSum2Help(result, candidates, temp, target, 0);
        return result;
    }
};

48 旋转图像

[i][j]->[j][n-1-i]->[n-1-i][n-1-j]->[n-1-j][i]->[i][j]

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int t, n = matrix[0].size();
        for(int i = 0; i < n / 2; i++){
            for(int j = i; j < n - 1 - i; j++){
                t = matrix[i][j];
                matrix[i][j] = matrix[n - 1 - j][i];
                matrix[n - 1 - j][i] = matrix[n - 1 - i][n - 1 - j];
                matrix[n - 1 - i][n - 1 - j] =  matrix[j][n - 1 - i];
                matrix[j][n - 1 - i] = t;
            }
        }
    }
};

53 最大子序和

动态规划 f(n) = max(f(n-1) + A[n], A[n])
将大问题分解为小问题,即当f(n-1)为0时,则不加入该子序列
class Solution {
public:
int maxSubArray(vector& nums) {
int best = nums[0], temp = nums[0];
for(int i = 1; i < nums.size(); i++){
temp = temp > 0 ? nums[i] + temp : nums[i];
best = best > temp ? best : temp;
}
return best;
}
};

54 螺旋矩阵

用turn控制移动方向,每个方向读取的数字长度num序列为n,m-1,n-1,m-2,n-2,···当num为0时则读取结束;num与turn相关,num = m(n)-turn/2-1;;
并且先对行row列col值自加自减,然后读取元素,因此初始值row=0,col=-1;

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> result;
        int m = matrix.size();
        if(m == 0)
            return result;
        int n = matrix[0].size();
        if(n == 0)
            return result;
        int num = n, turn = 0, row = 0, col = -1, i;
        while(num > 0){
            i = 0;
            if(turn % 4 == 0){
                while(i < num){
                    result.push_back(matrix[row][++col]);
                    i++;
                }
            }
            else if(turn % 4 == 1){
                while(i < num){
                    result.push_back(matrix[++row][col]);
                    i++;
                }
            }
            else if(turn % 4 == 2){
                while(i < num){
                    result.push_back(matrix[row][--col]);
                    i++;
                }
            }
            else{
                while(i < num){
                    result.push_back(matrix[--row][col]);
                    i++;
                }
            }
            if(turn % 2 != 0)
                num = n - turn / 2 - 1;
            else
                num = m - turn / 2 - 1;
            turn++;
        }
        return result;
    }
};

2020.9.12

55 跳跃游戏

错误思路:从后往前遍历数组(倒数第二个位置开始),只要能越过每个0即可到达终点;
错误版本1:在遇到下一个0之前,判断中间位置的位置能否越过之前的0;没有考虑连续0在一起的情况
错误版本2:若与之前的0相连,则不更新pos0的值;越过pos0才可以。

int canJump(vector<int>& nums) {
    int end2 = nums.size() - 2;
    int pos = end2, pos0 = pos;
    bool flag = true;
    while(pos >= 0){
        if(nums[pos] == 0){
            if(pos == end2 || nums[pos + 1] != 0){
                pos0 = pos;
                if(flag == false){
                    return false;
                }
                flag = false;
            }
        }
        else if(nums[pos] > pos0 - pos)
            flag = true;
        pos--;
    }
    return flag;
}

贪心算法,对于可达位置pos它能到达最远的位置为pos+nums[pos],若大于pos0,则在pos位置,pos0可达;

官方解法:我们依次遍历数组中的每一个位置,并实时维护最远可以到达的位置。对于当前遍历到的位置 x,如果它在最远可以到达的位置的范围内,那么我们就可以从起点通过若干次跳跃到达该位置,因此我们可以用 x+nums[x] 更 最远可以到达的位置。
在遍历的过程中,如果最远可以到达的位置 大于等于数组中的最后一个位置,那就说明最后一个位置可达,我们就可以直接返回 True 作为答案

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int end1 = nums.size() - 1, num = 0;
        for(int i = 0; i <= end1; i++){
            if(i <= num){
                num = i + nums[i] > num ? i + nums[i] : num;
                if(num >= end1)
                    return true;
            }
        }
        return false;
    }
};

没有找到正确的思路,太难了

56 合并区间

对每个区间以最左区间排序,如果相邻两个区间的l2小于r,说明有重叠部分,r的值更新为r和r2中的较大值;如果l2大于r,那就是一个新的区间,把当前的l和r存入result中的一个区间;
最后区间l和r需要for循环之后再加入

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        vector<vector<int>> result;
        int s = intervals.size();
        if(s == 0)
            return result;
        sort(intervals.begin(), intervals.end());
        int l = intervals[0][0], r= intervals[0][1];
        for(int i = 1; i < s; i++){
            if(intervals[i][0] <= r)
                r = intervals[i][1] > r ? intervals[i][1] : r;
            else{
                vector<int> t {l, r};
                result.push_back(t);
                l = intervals[i][0];
                r = intervals[i][1];
            }
        }
        vector<int> t {l, r};
        result.push_back(t);
        return result;
    }
};

2020.9.13

59 螺旋矩阵-ii

引用某题友的思路

对于这种螺旋遍历的方法,重要的是要确定上下左右四条边的位置,那么初始化的时候,上边up就是0,下边down就是m-1,左边left是0,右边right是n-1。然后我们进行while循环,先遍历上边,将所有元素加入结果res,然后上边下移一位,如果此时上边大于下边,说明此时已经遍历完成了,直接break。

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> result (n, vector<int>(n, 0));
        int i = 1, left = 0, right = n - 1, up = 0, down = n - 1;
        while(1){
            for(int j = left; j <= right; j++)
                result[up][j] = i++;
            if(++up > down)
                break;
            for(int j = up; j <= down; j++)
                result[j][right] = i++;
            if(--right < left)
                break;
            for(int j = right; j >= left; j--)
                result[down][j] = i++;
            if(--down < up)
                break;
            for(int j = down; j>= up; j--)
                result[j][left] = i++;
            if(++left > right)
                break;
        }
        return result;
    }
};

62 路径之和

动态规划:dp[i][j] = dp[i-1][j] + dp[i][j-1],时间复杂度O(n*m),空间复杂度O(n)

class Solution {
public:
    int uniquePaths(int m, int n) {
        vector<int> t1 (n, 1);
        vector<int> t2 (n, 1);
        for(int i = m - 2; i >= 0; i--){
            for(int j = n - 2; j >= 0; j--){
                t1[j] = t2[j] + t1[j + 1];
                t2[j] = t1[j];
            }
        }
        return t1[0];
    }
};

可以利用数学排列来解
Cm−1m+n+2

2020.9.14

63 路径之和-ii

依旧是动态规划算法,如果此处有障碍,则方法数为0;dp[i][j] = dp[i-1][j] + dp[i][j-1]
注意两点:

  • 由于最后一排只能通过右侧到达目的地,如果右侧有障碍物,方法数为0,那么此处的方法数也为0,即障碍物左侧所有位置都不能道道目的地;
  • 虽然返回值为int,求解过程中可能出现溢出的情况,故为long long类型
class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();
        if(m == 0)
            return 1;
        vector<long long> pre(n, 1);
        vector<long long> cur(n, 1);
        for(int i = n - 1; i >= 0; i--){
            if((obstacleGrid[m - 1][i] == 1) || (i != n - 1 && cur[i + 1] == 0)){
                cur[i] = 0;
                pre[i] = 0;
            }
        }
        for(int i = m - 2; i >= 0; i--){
            if(obstacleGrid[i][n - 1] == 1){
                cur[n - 1] = 0;
                pre[n - 1] = 0;
            }
            for(int j = n - 2; j >= 0; j--){
                if(obstacleGrid[i][j] == 1)
                    cur[j] = 0;
                else
                    cur[j] = cur[j + 1] + pre[j];
                pre[j] = cur[j];
            }
        }
        return (int)cur[0];
    }
};

64 最小路径和

还是用动态规划法做,与62、63类似
没做

65 加一

应该是从右到左看有无进制,到了最前面,再插入1

2020.9.15

73 数组置零

只想到了额外空间O(m+n)的方法,利用位图,判断行列中是否有0,时间和空间复杂度都为O(m*n)

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size(), t = m + n;
        int flag[t];
        for(int i = 0; i < t; i++)
            flag[i] = 0;
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(matrix[i][j] == 0){
                    flag[i] = 1;
                    flag[m + j] = 1;
                }
            }
        }
        for(int i = 0; i < m; i++){
            if(flag[i] == 1){
                for(int j = 0; j < n; j++){
                    matrix[i][j] = 0;
                }
            }
        }
        for(int i = 0; i < n; i++){
            if(flag[i + m] == 1){
                for(int j = 0; j < m; j++){
                    matrix[j][i] = 0;
                }
            }
        }
    }
};

参考O(1)解法

空间复杂度 O(2) ,用两个布尔变量就可以解决。方法就是利用数组的首行和首列来记录 0 值。从数组下标的 A[1][1] 开始遍历,若A[i][j]值为0,那么将[i][0]和[0][j]置为0,表明i行和j列该置为0,而两个布尔值记录首行首列是否需要置0。

74 搜索二维矩阵

二分搜索,当成有序一维数组,mid求得行列值做判断;
有个案例是[] 0,没有考虑到

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size();
        if(m == 0)
            return false;
        int n = matrix[0].size();
        int l = 0, r = (m - 1) * n + n - 1, mid, mr, mc;
        while(l <= r){
            mid = l + (r - l) / 2;
            mr = mid / n;
            mc = mid % n;
            if(matrix[mr][mc] > target)
                r = mid - 1;
            else if(matrix[mr][mc] < target)
                l = mid + 1;
            else
                return true;
        }
        return false;
    }
};

2020.9.16

75 颜色分类

没有想到一次遍历的方法
三指针法,p0和p2表示0和2的边界,curr遍历搜索;

若 nums[curr] = 0 :交换第 curr个 和 第p0个 元素,并将指针都向右移。换过来的数一定是0或者1,且为0时一定是最开始的时候;
若 nums[curr] = 2 :交换第 curr个和第 p2个元素,并将 p2指针左移 。由于不确定换过来的数是否为2;
若 nums[curr] = 1 :将指针curr右移。

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int l = 0, c = 0, r = nums.size() - 1, t;
        while(c <= r){
            if(nums[c] == 0){
                t = nums[l];
                nums[l] = nums[c];
                nums[c] = t;
                l++;
                c++;
            }
            else if(nums[c] == 1)
                c++;
            else{
                t = nums[r];
                nums[r] = nums[c];
                nums[c] = t;
                r--;
            }
        }
    }
};

78 子集

  • 遍历数组,对于每个数当前的结果是不包含该数的子集,那么加上该数组成新的子集,即所有的子集,
    提醒我内存限制是因为内存循环的终止条件为result.size(),由于会每次都会push新的子集,导致无限循环
vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> result;
        vector<int> r;
        result.push_back(r);
        for(int i = 0; i < nums.size(); i++){
            int size = result.size();
            for(int j = 0; j < size; j++){
                vector<int> t (result[j]);
                t.push_back(nums[i]);
                result.push_back(t);
                //result.push_back(result[j]);
                //result[result.size() - 1].push_back(nums[i]);
            }
        }
        return result;
    }
};
  • 位运算法
    遍历0~2size-1,然后对用二进制表达该数,若某pos位为1,则添加对应pos位的数;
  • 回溯法
class Solution {
public:
    void subsetshelp(vector<vector<int>> &result, vector<int>& nums, int pos, vector<int> t){
        if(pos == nums.size()){
            result.push_back(t);
            return;
        }

        subsetshelp(result, nums, pos + 1, t);
        t.push_back(nums[pos]);
        subsetshelp(result, nums, pos + 1, t);
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<vector<int>> result;
        vector<int> t;
        subsetshelp(result, nums, 0, t);
        return result;
    }
};

2020.9.17

79 单词搜索

回溯搜索,flag标记数组

class Solution {
public:
    bool existhelp(vector<vector<char>>& board, vector<vector<bool>>& flag, string word, int row, int col, int pos){
        if(pos == word.length())
            return true;
        int m = board.size(), n = board[0].size();
        if(row == m || col == n || row == -1 || col == -1)
            return false;
        if(flag[row][col] == 1 || board[row][col] != word[pos])
            return false;
        
        flag[row][col] = 1;
        if(existhelp(board, flag, word, row, col + 1, pos + 1) == true
            || existhelp(board, flag, word, row + 1, col, pos + 1) == true
            || existhelp(board, flag, word, row - 1, col, pos + 1) == true
            || existhelp(board, flag, word, row, col - 1, pos + 1) == true)
            return true;
        flag[row][col] = 0;
        return false;
    }
    bool exist(vector<vector<char>>& board, string word) {
        int m = board.size(), n = board[0].size();
        vector<vector<bool>> flag (m, vector<bool> (n, 0));
        for(int i = 0; i < m; i++){
            for(int j = 0; j < n; j++){
                if(existhelp(board, flag, word, i, j, 0) == true)
                    return true;
            }
        }
        return false;
    }
};

88 合并两个有序数组

从两个数组的尾部开始遍历,依次插入较大值;当某个数组遍历完之后,只用插入另一个数组;
开始用JAVA写代码了,可阅读性会差很多吧

class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int pos = m + n - 1, i = m - 1, j = n - 1;
        while(i >= 0 || j >= 0){
            if(i >= 0 && j >= 0){
                if(nums1[i] > nums2[j])
                    nums1[pos--] = nums1[i--];
                else
                    nums1[pos--] = nums2[j--];
            }
            else if(i >= 0)
                nums1[pos--] = nums1[i--];
            else
                nums1[pos--] = nums2[j--];
        }
    }
}

2020.9.18

80 删除排序数组中的重复项-ii

首先判断数组的长度,0或1就是其本身;大于等于2,前两个是不用判断,肯定能保留,之前遍历数组,若不是同时等于pos-1和pos-2处数字,则保留;

class Solution {
    public int removeDuplicates(int[] nums) {
        if(nums == null)
            return 0;
        int n = nums.length;
        if(n == 1)
            return n;
        int pos = 2;
        for(int i = 2; i < n; i++){
            if(nums[i] != nums[pos - 1] || nums[i] != nums[pos - 2])
                nums[pos++] = nums[i];
        }
        return pos;
    }
}

2020.9.19

81 搜索旋转排序数组-ii

之前有过类似的题目,但是没有想出来,这次还是有点问题,这里直接引用别人的思路
数组旋转之后,必定存在前半段或者后半段是有序;
若前半段有序left<mid处的值,left<=target<mid,说明在前半段,否则就在后半段;
后半段有序mid<target处的值,mid<target<=right,说明在后半段,否则就在前半段;
由于会出现left=mid处值的情况,则需要跳过该left直到找到和mid不一样的值为止!

class Solution {
    public boolean search(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return false;
        }
        int start = 0;
        int end = nums.length - 1;
        int mid;
        while (start <= end) {
            mid = start + (end - start) / 2;
            if (nums[mid] == target) {
                return true;
            }
            if (nums[start] == nums[mid]) {
                start++;
                continue;
            }
            //前半部分有序
            if (nums[start] < nums[mid]) {
                //target在前半部分
                if (nums[mid] > target && nums[start] <= target) {
                    end = mid - 1;
                } else {  //否则,去后半部分找
                    start = mid + 1;
                }
            } else {
                //后半部分有序
                //target在后半部分
                if (nums[mid] < target && nums[end] >= target) {
                    start = mid + 1;
                } else {  //否则,去后半部分找
                    end = mid - 1;

                }
            }
        }
        //一直没找到,返回false
        return false;
    }
}

90 子集-ii

跟之前的子集一样的思路,但这里存在重复的元素,因此需要用map来存储元素及其出现的次数,元素出现次数不同则子集不同
由于用JAVA编写,API实在不熟悉,出现了很多问题。

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        result.add(new ArrayList<>());
        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>();
        for(int i = 0; i < nums.length; i++){
            if(map.containsKey(nums[i]))
                map.put(nums[i], map.get(nums[i]) + 1);
            else
                map.put(nums[i], 1);
        }
        for(HashMap.Entry<Integer, Integer> it : map.entrySet()){
            int size = result.size();
            for(int i = 0; i < size; i++){
                List<Integer> temp = new ArrayList<>(result.get(i));
                for(int j = 0; j < it.getValue(); j++){
                    temp.add(it.getKey());
                    List<Integer> temp2 = new ArrayList<>(temp);
                    result.add(temp2);
                }
            }
        }
        return result;
    }
}

2020.10.14

105 从前序与中序遍历序列构造二叉树

[ 根节点, [左子树的前序遍历结果], [右子树的前序遍历结果] ]
[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]
preorder数组第一个数字为root,然后在inorder遍历找到root的index,左边为左子树,右边为右子树;分别得到左右子树的长度,将preorder分段;递归;

class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return buildTreeHelp(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1);
    }
    private TreeNode buildTreeHelp(int[] preorder, int pl, int pr, int[] inorder, int il, int ir){
        if(pr < pl)
            return null;
        if(pr == pl)
            return new TreeNode(preorder[pl]);
        int temp = preorder[pl], index = -1;
        for(int i = il; i <= ir; i++){
            if(inorder[i] == temp){
                index = i;
                break;
            }
        }
        if(index == -1)
            throw new RuntimeException("!!!");
        TreeNode root = new TreeNode(temp);
        root.left = buildTreeHelp(preorder, pl + 1, pl + (index - il), inorder, il, index - 1);
        root.right = buildTreeHelp(preorder, pl + (index - il) + 1, pr, inorder, index + 1, ir);
        return root;
    }
}

106 从中序与后序遍历序列构造二叉树

[ [左子树的中序遍历结果], 根节点, [右子树的中序遍历结果] ]
[ [左子树的后序遍历结果], [右子树的后序遍历结果], 根节点 ]

class Solution {
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        return buildTreeHelp(inorder, 0, inorder.length - 1, postorder, 0, postorder.length - 1);
    }
    private TreeNode buildTreeHelp(int[] inorder, int il, int ir, int[] postorder, int pl, int pr) {
        if(pr < pl)
            return null;
        if(pr == pl)
            return new TreeNode(postorder[pr]);
        int temp = postorder[pr], index = -1;
        for(int i = il; i <= ir; i++) {
            if(inorder[i] == temp){
                index = i;
                break;
            }
        }
        TreeNode root = new TreeNode(postorder[pr]);
        root.left = buildTreeHelp(inorder, il, index - 1, postorder, pl, pl + (index - il) - 1);
        root.right = buildTreeHelp(inorder, index + 1, ir, postorder, pl + (index - il) , pr - 1);
        return root;
    }
}

2020.10.15

JAVA的语法不一样;
List只能迭代,使用set和get,多层List只能取每一层List直到操作最内层List;

118 杨辉三角

class Solution {
    public List<List<Integer>> generate(int numRows) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if(numRows < 1)
            return result;
        result.add(new ArrayList<Integer>());
        result.get(0).add(1);
        for(int i = 2; i <= numRows; i++){
            List<Integer> temp = new ArrayList<Integer> ();
            temp.add(1);
            List<Integer> temp1 = result.get(i - 2);
            for(int j = 2; j <= i - 1; j++){
                temp.add(temp1.get(j - 2) + temp1.get(j - 1));
            }
            temp.add(1);
            result.add(new ArrayList<Integer>(temp));
        }
        return result;
    }
}

119 杨辉三角-ii

用两个list

class Solution {
    public List<Integer> getRow(int rowIndex) {
        List<Integer> l1 = new ArrayList<Integer>();
        l1.add(1);
        int t;
        for(int i = 1; i <= rowIndex; i++){
            List<Integer> l2 = new ArrayList<Integer>(l1);
            for(int j = 1; j < i; j++){
                t = l2.get(j - 1) + l2.get(j);
                l1.set(j, t);
            }
            l1.add(1);
        }
        return l1;
    }
}

更新当前j的时候,就把之前j的信息覆盖掉了。而更新 j + 1 的时候又需要之前j的信息,所以在更新前,我们需要一个变量把之前j的信息保存起来

2020.10.16

120 三角形最小路径和

动态规划算法
从最底端往上走,第i层第j个元素会经过第i+1层第j或j+1个元素中的较小值;
那么数组的第一个元素为所求;

class Solution {
    public int minimumTotal(List<List<Integer>> triangle) {
        int len = triangle.size(), t;
        if(len == 0)
            return 0;
        List<Integer>temp = new ArrayList<Integer>(triangle.get(len - 1));
        for(int i = len - 2; i >= 0; i--){
            List<Integer> temp1 = new ArrayList<Integer>(triangle.get(i));
            for(int j = 0; j <= i; j++){
                t = temp.get(j) < temp.get(j + 1) ? temp.get(j) : temp.get(j + 1);
                temp.set(j, t + temp1.get(j));
            }
        }
        return temp.get(0);
    }
}

也可以从顶端往底端走,用数组保存每一层从顶端到该层的最短路径;第i层第0/size()-1个元素只能经过第i-1层第0/size()-1个元素,但其他元素经过第i-1层第j-1/j个元素;
最后遍历数组,最小值为所求;

152 乘积最大子数组

用动态规划来做,数组t[i][j]保存第i个元素到第j个元素的乘积,遍历得到最大值,但是内存错误;

class Solution {
    public int maxProduct(int[] nums) {
        int n = nums.length;
        int [][] t = new int [n][n];
        for(int i = 0; i < n; i++){
            for(int j = 0; j < i; j++){
                t[i][j] = t[i - 1][j] * nums[i];
            }
            t[i][i] = nums[i];
        }
        int temp = nums[0];
        for(int i = 1; i < n; i++){
            for(int j = 0; j <= i; j++){
                if(t[i][j] > temp)
                    temp = t[i][j];
            }
        }
        return temp;
    }
}

开始的想法有过判断负数的个数,如果是偶数那么全乘,奇数则对左边或者右边做取舍;但是又涉及到0;没有想到像官方解法一样的思路,当有正负时,需要同时开了最大最小;

我们可以根据正负性进行分类讨论。
考虑当前位置如果是一个负数的话,那么我们希望以它前一个位置结尾的某个段的积也是个负数,这样就可以负负得正,并且我们希望这个积尽可能「负得更多」,即尽可能小。如果当前位置是一个正数的话,我们更希望以它前一个位置结尾的某个段的积也是个正数,并且希望它尽可能地大。于是这里我们可以再维护一个 fmin,它表示以第i个元素结尾的乘积最小子数组的乘积,那么我们可以得到这样的动态规划转移方程:
在这里插入图片描述

class Solution {
    public int maxProduct(int[] nums) {
        int n = nums.length, timax, timin;
        int [] tmax = new int [n];
        int [] tmin = new int [n];
        tmax[0] = nums[0];
        tmin[0] = nums[0];
        for(int i = 1; i < n; i++){
            timax = nums[i] * tmax[i - 1];
            timin = nums[i] * tmin[i - 1];
            tmax[i] = Math.max(timax, Math.max(nums[i], timin));
            tmin[i] = Math.min(timax, Math.min(nums[i], timin));
        }
        timax = tmax[0];
        for(int i = 1; i < n; i++){
            timax = Math.max(timax, tmax[i]);
        }
        return timax;
    }
}

153 寻找旋转排序数组中的最小值

想法有点复杂,分多种情况讨论

  • 下标l的值小于等于下标r所在的值,表明该子段有序;
  • 下标l的值小于等于mid的值,说明前端有序,且最小值在后段,l=mid+1;
  • 否则,下标mid的值小于mid-1的值,表面mid为最小(不需要考虑mid = 0的情况,在第一种情况下考虑到了;
  • 如果不小于,说明最小值在前段,r=mid-1;
class Solution {
    public int findMin(int[] nums) {
        int l = 0, r = nums.length - 1, mid = 0;
        while(l <= r){
            mid = l + (r - l) / 2;
            if(nums[l] <= nums[r])
                return nums[l];
            else if(nums[l] <= nums[mid])
                l = mid + 1;
            else{
                if(nums[mid] > nums[mid - 1])
                    r = mid - 1;
                else
                    return nums[mid];
            }
        }
        return nums[mid];
    }
}

官方的解法直接考虑mid与r的值比较,不断的向右移;
若mid小于r的值,r=mid;
否则l=mid+1;

  • 162
  • 229
  • 287

2020.10.20

162 寻找峰值

虽然知道是二分法,但是没有思路
由于-1和len所在位置的值为负无穷大,若mid不是峰值,选择mid-1和mid+1之中较大值的那一段(必定有峰值)即mid+1一直是递增,总会找到极大值(len为负无穷);

class Solution {
    public int findPeakElement(int[] nums) {
        int len = nums.length;
        if(len == 1)
            return 0;
        if(nums[0] > nums[1])
            return 0;
        if(nums[len -2] < nums[len - 1])
            return len - 1;
        int l = 1, r = len - 2, mid;
        while(l <= r){
            mid = l + (r - l) / 2;
            if(nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1])
                return mid;
            else if(nums[mid + 1] > nums[mid - 1])
                l = mid + 1;
            else
                r = mid - 1;
        }
        return -1;
    }
}

2020.10.22

209 长度最小的子数组

二分搜索,用数组保存前缀和,将寻找包括当前元素的最短子数组的时间复杂度变为O(logn),遍历整个数组;
或许可以用提供的API binarySearch

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int len = nums.length, best = len + 1;
        if(len == 0)
            return 0;
        if(nums[0] >= s)
            return 1;
        int [] temp = new int [len];
        temp[0] = nums[0];
        for(int i = 1; i < len; i++){
            temp[i] = temp[i - 1] + nums[i];
            if(temp[i] >= s){
                int l = 0, r = i - 1, mid;
                best = i + 1 < best ? i + 1 : best;
                while(l <= r){
                    mid = l + (r - l) / 2;
                    if(temp[i] - temp[mid] >= s){
                        best = i - mid < best ? i - mid : best;
                        l = mid + 1;
                    }
                    else
                        r = mid - 1;
                }
            }
        }
        if(best == len + 1)
            return 0;
        return best;
    }
}

双指针法
开始想到了这个方法,误以为时间复杂度为O(n^2)就没继续做下去,发现其实时间复杂度为O(2n)
定义pos和pos1两个指针,分别指向子数组的首尾,如果sum满足要求,更新最优解。然后sum减去pos位置的值,并pos向右移动,直到pos=pos1或者s大于sum,此过程一直更新最优解。

class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        int len = nums.length, best = len + 1;
        int pos = 0, pos1 = 0, sum = 0;
        while(pos1 < len){
            sum += nums[pos1];
            if(sum >= s){
                best = best < pos1 - pos + 1 ? best : pos1 - pos + 1;
                while(pos < pos1){
                    sum -= nums[pos++];
                    if(sum >= s)
                        best = best < pos1 - pos + 1 ? best : pos1 - pos + 1;
                    else
                        break;
                }
            }
            pos1++;
        }
        if(best == len + 1)
            return 0;
        return best;
    }
}

2020.10.23

216 组合总和iii

回溯法

class Solution {
    public void combinationSum3Help(List<List<Integer>> result, List<Integer>t, int k, int n, int s){
        if(k == 0){
            if(n == 0)
                result.add(new ArrayList<Integer>(t));
            return;
        }
        if(n < 0)
            return;
        for(int i = s; i <= 9; i++){
            t.add(i);
            combinationSum3Help(result, t, k - 1, n - i, i + 1);
            t.remove(t.size() - 1);
        }
        return;
    }
    public List<List<Integer>> combinationSum3(int k, int n) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        List<Integer> temp = new ArrayList<Integer>();
        combinationSum3Help(result, temp, k, n, 1);
        return result;
    }
}

228 汇总区间

就遍历数组,用s和e两个指针指向子数组的首尾

class Solution {
    public List<String> summaryRanges(int[] nums) {
        List<String> result = new ArrayList<String>();
        int len = nums.length;
        if(len == 0)
            return result;
        else if(len == 1){
            result.add(String.valueOf(nums[0]));
            return result;
        }
        int s = nums[0], e = nums[0];
        for(int i = 1; i < len; i++){
            if(nums[i] == nums[i - 1] + 1)
                e = nums[i];
            else{
                if(e == s)
                    result.add(String.valueOf(s));
                else 
                    result.add(String.valueOf(s) + "->" + String.valueOf(e));
                e = s = nums[i];
            }
        }
        if(e == s)
            result.add(String.valueOf(s));
        else 
            result.add(String.valueOf(s) + "->" + String.valueOf(e));
        return result;
    }
}

2020.10.24

169 多数元素

摩尔投票法,即以“对拼消耗“的形式;

首先请考虑最基本的摩尔投票问题,找出一组数字序列中出现次数大于总数1/2的数字(并且假设这个数字一定存在)。显然这个数字只可能有一个。摩尔投票算法是基于这个事实:每次从序列里选择两个不相同的数字删除掉(或称为“抵消”),最后剩下一个数字或几个相同的数字,就是出现次数大于总数一半的那个。

基于此得到如下代码,majorty保存当前剩余的数字,count表示当前majorty的个数,若为0需要选择另一个数字;

	class Solution {
    public int majorityElement(int[] nums) {
        int majorty = -1, count = 0;
		for(int i = 0; i < nums.length; i++) {
			if(count == 0) {
				majorty = nums[i];
				count = 1;
			}
			else {
				if(nums[i] == majorty)
					count++;
				else
					count--;
			}
		}
		count = 0;
		for(int i = 0; i < nums.length; i++) {
			if(nums[i] == majorty) {
				count++;
			}
		}
		return majorty;
    }
}

229 求众数ii

摩尔投票算法的改进,利用m1,m2,c1,c2分别记录两个数及剩余的次数;但是要避免m1=m2的情况;
自己写的代码if判断很多,看了别人的思路发现可以总结到一起,第一个判断中的nums[i] != m2是为了避免c1=0且nums[i] == m2这种情况下,c2会少加1

class Solution {
    public List<Integer> majorityElement(int[] nums) {
        int m1 = -1, m2 = -1, c1 = 0, c2 = 0, len = nums.length;
        for(int i = 0; i < len; i++){
            if((c1 == 0 || m1 == nums[i]) && nums[i] != m2){
                c1++;
                m1 = nums[i];
            }
            else if(c2 == 0 || m2 == nums[i]){
                c2++;
                m2 = nums[i];
            }
            else{
                c1--;
                c2--;
            }
        }
        c1 = 0;
        c2 = 0;
        for(int i = 0; i < len; i++){
            if(nums[i] == m1)
                c1++;
            else if(nums[i] == m2)
                c2++;
        }
        List<Integer> result = new ArrayList<Integer>();
        if(c1 > len / 3)
            result.add(m1);
        if(c2 > len / 3)
            result.add(m2);
        return result;
    }
}

2020.10.25

238 除自身以外的数组乘积

题目要求不能用除法,那考虑pos为i的数字的前缀和和后缀和,再相乘即可;
解法一:时间空间复杂度为O(n)用两个数组

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int len = nums.length;
        int [] result = new int [len];
        if(len == 0)
            return result;
        int [] left = new int [len];
        int [] right = new int [len];
        left[0] = 1;
        right[len - 1] = 1;
        for(int i = 1; i < len; i++){
            left[i] = left[i - 1] * nums[i - 1];
            right[len - 1 - i] = right[len - i] * nums[len - i];
        }
        result[0] = right[0];
        result[len - 1] = left[len - 1];
        for(int i = 1; i < len - 1; i++){
            result[i] = right[i] * left[i];
        }
        return result;
    }
}

解法二:时间复杂度为O(n),空间复杂度为O(1),用result数组保存前缀和,用变量num保存后缀和,即从右到左,并更新result数组的值;result[i] *= num;num *= nums[i];

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int len = nums.length;
        int [] result = new int [len];
        if(len == 0)
            return result;
        result[0] = 1;
        for(int i = 1; i < len; i++)
            result[i] = result[i - 1] * nums[i - 1];
        int num = nums[len - 1];
        for(int i = len - 2; i >= 0; i--){
            result[i] *= num;
            num *= nums[i];
        }
        return result;
    }
}

268 缺失数字

题目要求找到0~n中缺少的数字

  1. 排序之后遍历,时间复杂度O(nlongn)空间复杂度O(1)
  2. 哈希的方式,构建长度n+1的flag数组,遍历数组出现数字i就置索引为i的数组元素为1,则为0的是未出现数字,时间复杂度O(n)空间复杂度O(n)
  3. 数学方式,计算0~n的和并减去原数组的和(考虑溢出),时间复杂度O(n)空间复杂度O(1)
  4. 位运算,异或,0~n的异或并异或原数组(i^i=0),时间复杂度O(n)空间复杂度O(1)

283 移动零

用index表示非0的个数,遍历数组,当遇到非0的数字就前移,最后补充0;

2020.10.26

287 寻找重复数

https://blog.csdn.net/plokmju88/article/details/103675872

快慢指针,一个时间复杂度为O(N)的算法。
其一,对于链表问题,使用快慢指针可以判断是否有环。
其二,本题可以使用数组配合下标,抽象成链表问题。但是难点是要定位环的入口位置。
举个例子:nums = [2,5, 9 ,6,9,3,8, 9 ,7,1],构造成链表就是:2->[9]->1->5->3->6->8->7->[9],也就是在[9]处循环。
其三,快慢指针问题,会在环内的[9]->1->5->3->6->8->7->[9]任何一个节点追上,不一定是在[9]处相碰,事实上会在7处碰上。
其四,必须另起一个for循环定位环入口位置[9]。这里需要数学证明。
对“其四”简单说明一下,既然快慢指针在环内的某处已经相碰了。那么,第二个for循环遍历时,res指针还是在不停的绕环走,但是必定和i指针在环入口处相碰。

int findDuplicate(vector<int>& nums) {
    int res = 0;
    for (int fast = 0; res != fast || fast == 0;){
        res = nums[res];
        fast = nums[nums[fast]];
    }
    cout << res;
    for (int i = 0; res != i; i = nums[i]){
        res = nums[res];
    }
    return res;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值