leetcode刷题笔记

leetcode刷题笔记

2019.01.11

905.Sort Array By Parity
描述:给一组数组,按先偶数后奇数的方式返回该数组。

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

高票代码:

    vector<int> sortArrayByParity(vector<int> &A) {
        for (int i = 0, j = 0; j < A.size(); j++)
            if (A[j] % 2 == 0) swap(A[i++], A[j]);
            //i代表数组中第一个奇数的位置,j代表第一个偶数的位置,全面互换之后即得目标数组
        return A;
    }

2019.01.13

150.Evaluate Reverse Polish Notation
描述:计算逆波兰表达式

Input: [“10”, “6”, “9”, “3”, “+”, “-11”, “", “/”, "”, “17”, “+”, “5”, “+”]
Output: 22
Explanation:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22

思路:由于每一个运算符号之前必定有两个数,并且先出现的符号先算。按这个特性,使用栈可以完美解决。遍历数组的同时,将每一个数都压入栈中。当出现运算符号时,弹出栈顶的两个数字进行运算,并将运算结果压入栈中。直到遍历结束,栈顶的数字就是答案。

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> ans;
        int a,b;
        
        for(int i=0;i<tokens.size();i++)
        {
            if(tokens[i]!="+" && tokens[i]!="-" && tokens[i]!="*" && tokens[i]!="/")
            {
                ans.push(stoi(tokens[i]));
            }
            else
            {
                b=ans.top();
                ans.pop();
                a=ans.top();
                ans.pop();
                if(tokens[i]=="+") ans.push(a+b);
                else if(tokens[i]=="-") ans.push(a-b);
                else if(tokens[i]=="*") ans.push(a*b);
                else if(tokens[i]=="/") ans.push(a/b);
            }
        }
        return ans.top();
    }
};

2019.01.14

287.Find the Duplicate Number
描述:给定一个包含n + 1个整数的数组,其中每一个整数均介于[1, n]之间,证明其中至少有一个重复元素存在。假设只有一个数字出现重复,找出这个重复的数字。

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

思路:将题目中数组与下标抽象为链表,即
input[0]=1;
input[1]=3;
input[3]=2;

根据题意可知,该链表必定存在一个环,而只有重复数才会映射到同一个位置。因此环的起点的值就是重复数,这就变成了Floyd算法中寻找环的起点的问题。
设定快慢指针,先找到相遇的位置之后,再找环的起点:

class Solution {
public:
    int findDuplicate(vector<int>& nums) {
        int slow=0,fast=0;
        
        do
        {
            slow=nums[slow];
            fast=nums[nums[fast]];
        }while(slow!=fast);
        
        fast=0;
        while(slow!=fast)
        {
            slow=nums[slow];
            fast=nums[fast];
        }
        return slow;
    }
};

2019.01.15

11.Container With Most Water
描述:给定n个非负整数a1,a2,…,an,其中每个代表一个点坐标(i,ai)。n个垂直线段例如线段的两个端点在(i,ai)和(i,0)。找到两个线段,与x轴形成一个容器,使其包含最多的水。
在这里插入图片描述

思路:除了暴力枚举没想出好的思路,百度了一下。使用双指针分别从左端和右端开始,每次移动高度较低的指针。
因为一开始的低是最长的,而水的多少由短边确定,因此如果向内移动长边没有意义,只会让水更少。因此每次都移动短边的指针,记下水的容量。直到两指针相遇,最多的水的容量即为返回值。

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

15.3Sum
描述:给一组整数,寻找和为0的三个数,求出符合条件的所有解。
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]

思路:类似于寻找两数之和为0的思路,首先将数组排序。接着第一层循环确定第一个数,即将第一个数确定为target,之后使用双指针分别从target的右边和数组的末尾向中间移动。
如果两指针之和大于-target,右指针向左,反之左指针向右。如果相等则记录这三个数。
需要注意的是,移动指针时需判断移动后指针指向的数与现在是否相同,如果相同则跳过,否则结果中会出现重复解。

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        std::sort(nums.begin(),nums.end());
        vector<vector<int> > ans;
        vector<int> tmp;
        if(nums.size()<3)
            return ans;

        for(int i=0;i<nums.size()-2;)
        {
            int target=0-nums[i];
            int l=i+1,r=nums.size()-1;
                
            while(l<r)
            {
                if(nums[l]+nums[r]>target)
                    r--;
                else if(nums[l]+nums[r]<target)
                    l++;
                else
                {
                    tmp.clear();
                    tmp.push_back(-target);
                    tmp.push_back(nums[l]);
                    tmp.push_back(nums[r]);
                    ans.push_back(tmp);
                    while(nums[l]==nums[++l] && l<r);//避免出现重复解
                }
            }
            while(-target==nums[++i] && i<nums.size()-2);//避免出现重复解
        }
        return ans;
    }
};

16.3Sum Closest
描述:求出距离目标数最近的3个数的和。
Given array nums = [-1, 2, 1, -4], and target = 1.
The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).

思路:采取与上一题类似的思路,只需在对撞指针的循环内改判断条件即可,以最小的差值作为判断条件。
一开始写出来时,运算时间较长,只超过60%。后来发现只要判断差值为0时直接返回,就能避免后面没有必要的寻找。

class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        std::sort(nums.begin(),nums.end());
        int mintmp=99999,tmp,ans;
        
        if(nums.size()<3)
            return ans;
        

        for(int i=0;i<nums.size()-2;i++)
        {
            int target1=target-nums[i];
            int l=i+1,r=nums.size()-1;
                
            while(l<r)
            {
                tmp=nums[l]+nums[r]-target1;
                if(tmp>0)
                    r--;
                else
                    l++;
                if(tmp==0)//如果为0,则不必继续寻找
                    return tmp+target;
                if(abs(tmp)<mintmp)
                {
                    mintmp=abs(tmp);
                    ans=tmp+target;
                }
            }
        }
        return ans;
    }
};

2019.01.17

18.4Sum
描述:求出数组内4个数之和为target的全部组合。
Given array nums = [1, 0, -1, 0, -2, 2], and target = 0.
A solution set is:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]

思路:与之前的3sum类似,把3sum的代码再加一层遍历,先求出两个数的和。然后使用对撞指针寻找剩下的两个数,同样的是需要进行重复判断,避免出现重复解。

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        std::sort(nums.begin(),nums.end());
        vector<vector<int> > ans;
        vector<int> tmp;
        int tmptarget;
        if(nums.size()<4)
            return ans;

        for(int i=0;i<nums.size()-3;)
        {
            for(int j=i+1;j<nums.size()-2;)
            {
                tmptarget=target-nums[i]-nums[j];
                int l=j+1,r=nums.size()-1;

                while(l<r)
                {
                    if(nums[l]+nums[r]>tmptarget)
                        r--;
                    else if(nums[l]+nums[r]<tmptarget)
                        l++;
                    else
                    {
                        tmp.clear();
                        tmp.push_back(nums[i]);
                        tmp.push_back(nums[j]);
                        tmp.push_back(nums[l]);
                        tmp.push_back(nums[r]);
                        ans.push_back(tmp);
                        while(nums[l]==nums[++l] && l<r);//避免出现重复解
                    }
                }
                while(nums[j]==nums[++j] && i<nums.size()-2);//避免出现重复解
            }
            while(nums[i]==nums[++i] && i<nums.size()-3);//避免出现重复解

        }
        return ans;
    }
};

31.Next Permutation
描述:产生下一个序列,对给定序列进行重排,生成一个字母顺序比它更大的下一个序列。如果给定的序列已经是按字母顺序排列中最大的一个了,则进行逆序排列。算法在数组内进行,不要使用额外空间。
就是说在这几个数字构成的数进行排序,返回现有数的下一个数。如果已经是最大的,则返回最小的。

1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1

思路:从后向前遍历寻找第一个降序数字,找到之后将该数字之后的数组排序。在排序的部分中选一个比降序数字大的最小数,将将两者互换即为结果。

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int r=nums.size()-1,min=r;
        while(r>=0)
        {                
            if(r==0)//从后向前完全升序数列
            {
                r--;
                break;
            }
            if(nums[r]>nums[--r] )//寻找第一个降序数字
                 break;
        }
        
        if(r==-1)
            reverse(nums.begin(),nums.end());
        else
        {
            sort(nums.begin()+r+1,nums.end());
            int j=r+1;
            for(;nums[j]<=nums[r] && j<nums.size();j++);//寻找比降序数字大的最小数
            swap(nums[r],nums[j]);
        }
        
    }
};

2019.01.22

33.Search in Rotated Sorted Array
描述:假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。你可以假设数组中不存在重复的元素。

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

思路:由于该数组没有重复数字。且只在一个随机位置进行旋转。故可以使用二分法,先找到一组个有序的上升序列。二分之后,至少有一组是上升序列。判断目标是否在该组上升序列中,如果是则在该组中继续二分搜索。如果不在该上升序列中,则在另一组中继续二分,并搜索上升序列。直到找到目标,或者左右指针重合。

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

34.Find First and Last Position of Element in Sorted Array
描述:求 target 在有序数组 nums 中出现的最小下标和最大下标组成的数组。否则返回 {-1, -1}。时间复杂度为 O(log n)。
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]

思路:二分法搜索,此题关键在于可能存在找不到解的情况。此时二分法可能会陷入死循环,因此将循环跳出条件设为,tmp是否变化。如果tmp不变说明没有结果并进入死循环,跳出。

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int l=0,r=nums.size()-1;
        int tmp=r,tmptmp=-2;
        vector<int> ans;
        if(nums.empty())
        {
            ans.push_back(-1);
            ans.push_back(-1);
            return ans;
        }
            
        while(tmptmp!=tmp)
        {
            tmptmp=tmp;
            if(nums[tmp]==target)
            {
                int ansl=tmp,ansr=tmp;
                while(nums[--ansl]==target && ansl>=0);
                while(nums[++ansr]==target && ansr<=nums.size()-1);
                ans.push_back(ansl+1);
                ans.push_back(ansr-1);
                return ans;
            }
            if(nums[tmp]<target)
                l=tmp;
            else
                r=tmp;
            tmp=(l+r)/2;
        }
        ans.push_back(-1);
        ans.push_back(-1);
        return ans;
    }
};

2019.01.23

39.Combination Sum
描述:给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。

Input: candidates = [2,3,5], target = 8,
A solution set is:
[
[2,2,2,2],
[2,3,3],
[3,5]
]

思路:回溯算法,先将元素不断的放入tmp中,直到tmp中的和大于target。由于已经将数组排序了,因此当tmp中的和已经过大时,那么上一轮遍历中之后的数也不需要再放入tmp了,这样可以很大程度提高效率。

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> tmp;
    
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        sort(candidates.begin(),candidates.end());
        
        dfs(candidates,target,tmp,0);
        return ans;
    }
    
    bool dfs(vector<int> candidates, int target,vector<int>& tmp, int index)
    {
        if(target == 0)//返回标志位,上一轮迭代时之后的元素不需要遍历
        {
            ans.push_back(tmp);
            return 1;
        }
        if(target < 0)//返回标志位,上一轮迭代时之后的元素不需要遍历
            return 1;
        
        for(int i=index;i<candidates.size();i++)
        {
            tmp.push_back(candidates[i]);
            if(dfs(candidates,target-candidates[i],tmp,i))
            {
                tmp.pop_back();
                return 0;//结束本轮迭代
            }
            tmp.pop_back();
        }
        return 0;
    }
};

2019.01.24

40.Combination Sum II
描述:给定一个有重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字只能被选一次,且不能有重复解。

Input: candidates = [10,1,2,7,6,1,5], target = 8,
A solution set is:
[
[1, 7],
[1, 2, 5],
[2, 6],
[1, 1, 6]
]

思路:与上一题非常类似,区别在于每个数只能被拿一次,且数组有重复数。因此只需在循环中增加约束条件,使放入tmp的数不得与之前重复即可。

class Solution {
public:
    vector<vector<int>> ans;
    
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        vector<int> tmp;
        sort(candidates.begin(),candidates.end());
        
        dfs(candidates,target,tmp,-1);
        return ans;
    }
    
    bool dfs(vector<int> candidates, int target,vector<int>& tmp, int index)
    {
        if(target == 0)//返回标志位,上一轮迭代时之后的元素不需要遍历
        {
            ans.push_back(tmp);
            return 1;
        }
        if(target < 0)//返回标志位,上一轮迭代时之后的元素不需要遍历
            return 1;
        
        int i=index+1;
        while(i<candidates.size())
        {
            tmp.push_back(candidates[i]);
            if(dfs(candidates,target-candidates[i],tmp,i))
            {
                tmp.pop_back();
                return 0;//结束本轮迭代
            }
            tmp.pop_back();
            while(candidates[i]==candidates[++i]);//由于不允许出现重复解,因此如果有相同的数字跳过
        }
        return 0;
    }
};

48.Rotate Image
描述:将给定的二维数组顺时针旋转90度,并且只能在数组内操作,不得另外开辟数组。

Given input matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],

rotate the input matrix in-place such that it becomes:
[
[7,4,1],
[8,5,2],
[9,6,3]
]

思路:找规律,将每一层的正方形边上的数挨个互换。一层结束之后,就向内走一层,注意内层的起点和终点受约束。

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n=matrix.size()/2;
        int m=matrix.size()-1;
        for(int i=0;i<n;i++)//按行遍历正方形的边,只遍历一半的行数即可
            for(int j=i;j<m-i;j++)//按列遍历正方形的边,起点和终点受行数的约束
            {
                swap(matrix[i][j],matrix[m-j][i]);
                swap(matrix[m-j][i],matrix[m-i][m-j]);
                swap(matrix[m-i][m-j],matrix[j][m-i]);
            }
    }
};

2019.01.25

55.Jump Game
描述:给定一个数组,每个数字代表当前可向后走的最大步数。从第一个数字开始,求是否能走到最后一个数字位置,若能,返回true,否则返回false。

Input: [2,3,1,1,4]
Output: true
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.

思路:刚看到这一题,觉的回溯法肯定可以解。于是写了一个,大概思路就是尝试每个位置所有种可能,如果抵达终点就返回正确,否则继续递归。这种方法对于正常数组可能会比较快的判断,但是对于某些极端倒序的数组效率极低。最终结果只超过1.5%。代码如下:

class Solution {
public:
    bool canJump(vector<int>& nums) {
        bool flag[nums.size()];
        for(int i=0;i<nums.size();i++)
            flag[i]=0;
        return dfs(nums,0,flag);
    }
    
    bool dfs(vector<int>& nums,int index,bool *flag)
    {
        int n=nums[index];
        if(index>=nums.size()-1)
            return 1;
        if(n==0 || flag[index])
            return 0;
        for(int i=n;i>0;i--)
        {
            if(dfs(nums,index+i,flag))
                return 1;
        }
        flag[index]=1;
        return 0;
    }
};

之后看了高票答案,对于所有情况时间复杂度都是O(n),并且空间复杂度常数级。思路就是从前至后遍历整个数组,同时记下当前所知的能到达的最远位置reach。同时把这个reach作为约束条件。如果在遍历到结尾之前,退出循环,说明reach未到终点。反之则说明可以抵达终点,代码如下:

class Solution {
public:
    bool canJump(vector<int>& nums) {
        int n=nums.size();
        
        int i=0;
        for(int reach=0;i<n && i<=reach;i++)
            reach=max(i+nums[i],reach);
        return i==n;
    }
};

2019.02.14

56.Merge Intervals
描述:给定一个数组,将数组内重叠的区间提取出来。
Input: [[1,3],[2,6],[8,10],[15,18]]
Output: [[1,6],[8,10],[15,18]]
Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6].

思路:观察给定数组可知,需要先将数组排序。接着维护一个临时重叠区间,通过对数组内接下来的区间进行遍历。由于数组已经排序,故后续区间的左端点一定比临时重叠区间大。如果该区间的左端点比临时重叠区间的右端点小,那么可以将临时区间的右端点取:当前区间右端点 和 临时重叠区间右端点 的大值。
而如果后续区间的左端点已经比临时重叠区间右端点大了,那么将该临时重叠区间保存,并重新开辟新的临时重叠区间,继续遍历。
由于数组是有序的,所以ans数组内的最后一个区间就是临时区间,可以节省空间。

/**
 * Definition for an interval.
 * struct Interval {
 *     int start;
 *     int end;
 *     Interval() : start(0), end(0) {}
 *     Interval(int s, int e) : start(s), end(e) {}
 * };
 */
class Solution {
public:
    static bool comp(const Interval &a,const Interval &b)
    {
        return a.start<b.start;
    }
    vector<Interval> merge(vector<Interval>& intervals) {
        sort(intervals.begin(), intervals.end(), comp);
        if(intervals.empty())
            return intervals;
        
        vector<Interval> ans;
        ans.push_back(intervals[0]);
        for(int i=1;i<intervals.size();i++)
        {
            if(intervals[i].start<=ans.back().end)
                ans.back().end=max(ans.back().end,intervals[i].end);
            else
                ans.push_back(intervals[i]);
        }
        return ans;
    }
};

62.Unique Paths
描述:输入一个m×n的栅格,只能往下和右走,求出从[0,0]到[m-1,n-1]有多少种走法。
Input: m = 3, n = 2
Output: 3

思路:采用动态规划思想,首先我们可以看出栅格地图的第一行和第一列只有一种走法。然后由于每个格子只能从它的左边或上边的格子过来,故可得出:
path[ i ] [ j ] =path[ i-1 ] [ j ] + path[ i -1] [ j ]
因此通过初始条件,遍历计算栅格地图每个格子即可求出走到右下角的走法总数。

class Solution {
public:
    int uniquePaths(int m, int n) {
        int map[m][n];
        
        for(int i=0;i<n;)//第一行和第一列可知只有一种走法
            map[0][i++]=1;
        for(int i=0;i<m;)
            map[i++][0]=1;
        
        for(int i=1;i<m;i++)
            for(int j=1;j<n;j++)
                map[i][j]=map[i-1][j]+map[i][j-1];//动态规划
        return map[m-1][n-1];
    }
};

2019.02.15

63.Unique Paths II
描述:输入一个m×n的栅格,只能往下和右走,中间有障碍物,求出从[0,0]到[m-1,n-1]有多少种走法。
Input:
[
[0,0,0],
[0,1,0],
[0,0,0]
]
Output: 2

思路:与上一题类似,只是中间增加了障碍物。可以采取类似的思路,第一行和第一列在障碍物之前只有一种走法,在障碍物之后则无法到达,即为0种走法。之后遍历剩下的栅格,没有障碍物的栅格计算方法相同,有障碍物的栅格走法为0 。

class Solution {
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m=obstacleGrid.size();
        int n=obstacleGrid[0].size();
        unsigned int map[m][n];
        
        int i=0;
        for(;i<n;i++)
            if(obstacleGrid[0][i]==0)
                map[0][i]=1;
            else
                break;
        if(i<n)
            for(;i<n;i++)
                map[0][i]=0;
        
        i=0;
        for(;i<m;i++)
            if(obstacleGrid[i][0]==0)
                map[i][0]=1;
            else
                break;
        if(i<m)
            for(;i<m;i++)
                map[i][0]=0;
        
        for(int i=1;i<m;i++)
            for(int j=1;j<n;j++)
                if(obstacleGrid[i][j]==0)
                    map[i][j]=map[i-1][j]+map[i][j-1];//动态规划
                else
                    map[i][j]=0;
        return map[m-1][n-1];
    }
};

64.Minimum Path Sum
描述:求在一个全为正整数的 m X n 的矩阵中, 取一条从左上为起点, 走到右下为重点的路径, (前进方向只能向左或者向右),求一条所经过元素和最小的一条路径。
Input:
[
[1,3,1],
[1,5,1],
[4,2,1]
]
Output: 7
Explanation: Because the path 1→3→1→1→1 minimizes the sum.

思路:仍旧是动态规划的思路,求出递推表达式。由于只能往下或往右走,所以每个栅格仍然只能从上或者左过来,那么该栅格的最小值只能从这个两个值中取小值。因此仍旧是遍历剩下的栅格,计算最小值。

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        for(int i=1;i<grid.size();i++)
            grid[i][0]+=grid[i-1][0];
        for(int i=1;i<grid[0].size();i++)
            grid[0][i]+=grid[0][i-1];
        for(int i=1;i<grid.size();i++)
            for(int j=1;j<grid[0].size();j++)
                grid[i][j]+=min(grid[i-1][j],grid[i][j-1]);
        return grid[grid.size()-1][grid[0].size()-1];
    }
};

73.Set Matrix Zeroes
描述:在一个全为正整数的 m X n 的矩阵中,将每个为0元素所在的行和列全部置为0。要求在原始矩阵内操作,空间复杂度为o(1)。
Input:
[
[1,1,1],
[1,0,1],
[1,1,1]
]
Output:
[
[1,0,1],
[0,0,0],
[1,0,1]
]

思路:一开始想建立一个数组记录所有0元素的行和列,但是题目主要考察空间复杂度的极小。看了高票答案,思路是只用一个变量记录第一列是否存在0。接着遍历剩下的元素,并用矩阵的第一行和第一列来记录该行/列是否需要置0。不同的是,接下来的循环对剩余矩阵的元素挨个进行置0操作,而不是整列操作。这样能节省一个空间,从而不需要在设置一个标志位来记录第一行是否存在0元素。

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        int col0 = 1, rows = matrix.size(), cols = matrix[0].size();

    for (int i = 0; i < rows; i++) {
        if (matrix[i][0] == 0) col0 = 0;
        for (int j = 1; j < cols; j++)
            if (matrix[i][j] == 0)
                matrix[i][0] = matrix[0][j] = 0;
    }

    for (int i = rows - 1; i >= 0; i--) {
        for (int j = cols - 1; j >= 1; j--)
            if (matrix[i][0] == 0 || matrix[0][j] == 0)
                matrix[i][j] = 0;
        if (col0 == 0) matrix[i][0] = 0;
    }
    }
};

74.Search a 2D Matrix
描述:在一个排好序的二维数组内查找目标值,并返回是否找到。
Input:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
Output: true

思路:肯定是使用二分搜索,不过该题关键在于将二维数组看成一维已排序数组处理,会大大降低时间复杂度。

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

2019.02.19

75.Sort Colors
描述:将输入的数组按0,1,2的顺序分类放在一起。
Input: [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]

思路:自己想的思路,面对各种特殊情况有bug。看了高票代码,思路大概是遍历数组,碰到2时,往数组右端换;碰到1时,往数组左端换。这里的关键在于边界条件的设定,l代表第一个非0数,r代表倒数第一个非2数。发生交换之前需要判断i是否在l和r之间,因为之外的数是不需要发生交换的。而循环的终止条件为i大于r,代表所有的数都已经处理完毕,1就会留在中间。

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int l=0,r=nums.size()-1,i=0;
        for(;i<=r;i++)
        {
            while(nums[i]==2 && i<r) 
                swap(nums[i],nums[r--]);
            while(nums[i]==0 && i>l) 
                swap(nums[i],nums[l++]);
        }
    }
};

2019.02.20

78.Subsets
描述:输入一个数组,返回该数组内数组的所有种可能的组合。
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<int> tmp;
        vector<vector<int>> ans;
        ans.push_back(tmp);
        if(nums.empty())
            return ans;
        
        rec(ans,nums,tmp,0);
        return ans;
    }
    
    void rec(vector<vector<int>> &ans,vector<int>& nums,vector<int>& tmp,int index)
    {
        for(int i=index;i<nums.size();i++)
        {
            tmp.push_back(nums[i]);
            ans.push_back(tmp);
            rec(ans,nums,tmp,i+1);
            tmp.pop_back();
        }
    }
};

2019.02.28

2.Add Two Numbers
描述:输入两个以链表形式表达的反数,返回两者相加之和。
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807.

思路:这题难度不大,关键是特殊情况的处理。主要是当两个输入的长度不一样时,设置val值为0.

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
       ListNode* ans=new ListNode(0),*ans1=ans,*tmp;
       int val1,val2;

        while(l1 || l2)
        {
            val1=l1?l1->val:0;
            val2=l2?l2->val:0;
            ans->val+=val1+val2;
            ans->next = new ListNode(ans->val / 10);
            ans->val %= 10;
            l1=l1?l1->next:nullptr;
            l2=l2?l2->next:nullptr;
            tmp=ans;
            ans=ans->next;
        }
        if(ans->val ==0)
            tmp->next=nullptr;
        return ans1;
    }
};

2019.03.06

3.Longest Substring Without Repeating Characters
描述:给一个字符串,返回不包含重复字母的最长子串长度
Input: “abcabcbb”
Output: 3
Explanation: The answer is “abc”, with the length of 3.

思路:建立一个哈希表,映射每一个出现的字母最后出现的位置。在遍历字符串的时候,首先通过查询哈希表得到该字母出现的最后位置。如果出现的最后位置大于start起始位置,则说明该字母已经出现过,则把该字母位置赋给start。接着把该位置更新为新出现的位置,并更新最大距离。
本质上是维护一个动态的窗口,由于右边界一直在向右探索,关键在于左边界的维护。

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        vector<int> hash(256,-1);
        int maxlen=0,start=-1;
        for(int i=0;i<s.length();i++)
        {
            if(hash[s[i]]>start)
                start=hash[s[i]];
            hash[s[i]]=i;
            maxlen=max(maxlen,i-start);
        }
        return maxlen;
    }
};
资源下载链接为: https://pan.quark.cn/s/9648a1f24758 这个HTML文件是一个专门设计的网页,适合在告白或纪念日这样的特殊时刻送给女朋友,给她带来惊喜。它通过HTML技术,将普通文字转化为富有情感和创意的表达方式,让数字媒体也能传递深情。HTML(HyperText Markup Language)是构建网页的基础语言,通过标签描述网页结构和内容,让浏览器正确展示页面。在这个特效网页中,开发者可能使用了HTML5的新特性,比如音频、视频、Canvas画布或WebGL图形,来提升视觉效果和交互体验。 原本这个文件可能是基于ASP.NET技术构建的,其扩展名是“.aspx”。ASP.NET是微软开发的一个服务器端Web应用程序框架,支持多种编程语言(如C#或VB.NET)来编写动态网页。但为了在本地直接运行,不依赖服务器,开发者将其转换为纯静态的HTML格式,只需浏览器即可打开查看。 在使用这个HTML特效页时,建议使用Internet Explorer(IE)浏览器,因为一些老的或特定的网页特效可能只在IE上表现正常,尤其是那些依赖ActiveX控件或IE特有功能的页面。不过,由于IE逐渐被淘汰,现代网页可能不再对其进行优化,因此在其他现代浏览器上运行可能会出现问。 压缩包内的文件“yangyisen0713-7561403-biaobai(html版本)_1598430618”是经过压缩的HTML文件,可能包含图片、CSS样式表和JavaScript脚本等资源。用户需要先解压,然后在浏览器中打开HTML文件,就能看到预设的告白或纪念日特效。 这个项目展示了HTML作为动态和互动内容载体的强大能力,也提醒我们,尽管技术在进步,但有时复古的方式(如使用IE浏览器)仍能唤起怀旧之情。在准备类似的个性化礼物时,掌握基本的HTML和网页制作技巧非常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值