LeetCode刷题总结 period 1

11. Container With Most Water

在这里插入图片描述
思路:
最简单的使用暴力法破解,根据面积公式S=min(height[i],height[j])*(j-i);求解.
改进版本,使用双指针,题目这里有个证明,即:

线之间形成的区域总是受限于短线的高度。此外,线越远,所获得的区域就越多。 我们使用两个指针,一个在开头,一个在数组末尾,构成行长度。此外,我们还保留了一个变量max来存储到目前为止获得的最大面积。在每步中,我们找到它们之间形成的区域,更新max并将指针指向另一端的指针。因为木桶原理,容积取决于行长度和最短高度的积,所以,两个端点高度较低的需要移动,因为高度较高的移动不可能大于原来的两端点积。这样,每次都是高度低的移动,直到两指针相邻

int maxArea(vector<int>& height) {
    int water = 0;
    int i = 0, j = height.size() - 1;
    while (i < j) {
        int h = min(height[i], height[j]);
        water = max(water, (j - i) * h);
        while (height[i] <= h && i < j) i++;
        while (height[j] <= h && i < j) j--;
    }
    return water;
}

22. Generate Parentheses

在这里插入图片描述
典型的dfs模板题,需要注意dfs条件的控制,即左括号open必须在右括号close的前面产生,且在递归过程中右括号不能比左括号多。代码如下:

class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<string> ans;
        dfs(ans,"",0,0,n);
        return ans;
    }
    void dfs(vector<string>& ans,string cur,int open,int close,int len){
        if(cur.size()==len*2){
            ans.push_back(cur);
        }
        if(open<len){
            dfs(ans,cur+"(",open+1,close,len);
        }
        if(close<open)
            dfs(ans,cur+")",open,close+1,len);
    }
};

88. Merge Sorted Array

在这里插入图片描述
注意此题有个很简洁的写法,从末尾开始比较,详见代码:

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int i = m - 1, j = n - 1, tar = m + n - 1;
        while (j >= 0) {
            nums1[tar--] = i >= 0 && nums1[i] > nums2[j] ? nums1[i--] : nums2[j--];
        }
    }
};

123. Best Time to Buy and Sell Stock III

在这里插入图片描述
思路1:推导法
定义int buyFirst = INT_MIN, sellFirst = 0, buySecond = INT_MIN, sellSecond = 0;,分别表示第一次买入时的余额。第一次卖出的余额、第二次买入后的余额、第二次卖出后的余额,则题目转化为求:
sellSecond=max(sellSecond,buySecond +price[i])最大值,因而我们希望buySecond 也尽可能大,又buySecond = max(buySecond, sellFirst - price[i]),即要求sellFirst尽可能大,又sellFirst = max(sellFirst, buyFirst + price[i]),则为buyFirst求最大,又buyFirst = max(buyFirst, -price[i]),故代码如下:

class Solution {
public:
    int maxProfit(vector<int>& prices) {
        int buyFirst = INT_MIN, sellFirst = 0, buySecond = INT_MIN, sellSecond = 0;
        for (int p : prices) {
            buyFirst = max(buyFirst, -p);
            sellFirst = max(sellFirst, buyFirst + p);
            buySecond = max(buySecond, sellFirst - p);
            sellSecond = max(sellSecond, buySecond + p);
        }
        return sellSecond;
    }
};

思路二:dp
使用二维dp,其中dp[i][j]表示购买次数为i,当前价格为price[j]的时候的最大收益。
对于当前价格,最大收益只有两种情况:

  1. 要么已经用完了购买次数,此时dp[i,j]=dp[i,j-1],和当前价格无关
  2. 若还有购买机会,则要考虑当前价格是否能生成更大的增益,即考虑第k位到当前位的一次购买+第k为前的最大收益,则此时dp[i,j]=price[j]-price[k]+dp[i-1,k],其中k取值范围在[0,j-1]。
    dp[i,j]=max{dp[i,j-1],price[j]-price[k]+dp[i-1,k]}
class Solution {
public:
    int maxProfit(vector<int> &prices) {
        /*
        dp[i,j]表示购买i次,价格为price[j]时的最大收益
        dp[i,j]=max{dp[i,j-1],price[j]-price[k]+dp[i-1,k]},其中k in range of [0,j-1]
               =max{dp[i,j-1],max{dp[i-1,k]-price[k]}+price[j]}
               
        dp[0,j]=0; // 0 times transation makes 0 profit
        dp[i,0]=0  //only one price data point
        */
        if (prices.size() <= 1) return 0;
        else {
            int K = 2; // number of max transation allowed
            int maxProf = 0;
            vector<vector<int>> f(K+1, vector<int>(prices.size(), 0));
            for (int kk = 1; kk <= K; kk++) {
                int tmpMax = f[kk-1][0] - prices[0];
                for (int ii = 1; ii < prices.size(); ii++) {
                    f[kk][ii] = max(f[kk][ii-1], prices[ii] + tmpMax);
                    tmpMax = max(tmpMax, f[kk-1][ii] - prices[ii]);
                    maxProf = max(f[kk][ii], maxProf);
                }
            }
            return maxProf;
        }
    }
};

219. Contains Duplicate II

在这里插入图片描述

此题一开始用两层for循环来做,结果超时了。
我们改用one pass的思路,使用unordered_map来做,一次遍历依次加入即可,碰到重复的元素则比较下标差是否在k之间。
另外,注意此处选用unordered_map而不选用map的原因,两者的区别如下:

map: map内部实现了一个红黑树(红黑树是非严格平衡二叉搜索树,而AVL是严格平衡二叉搜索树),红黑树具有自动排序的功能,因此map内部的所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素。因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行的操作。map中的元素是按照二叉搜索树(又名二叉查找树、二叉排序树,特点就是左子树上所有节点的键值都小于根节点的键值,右子树所有节点的键值都大于根节点的键值)存储的,使用中序遍历可将键值按照从小到大遍历出来。
unordered_map: unordered_map内部实现了一个哈希表(也叫散列表,通过把关键码值映射到Hash表中一个位置来访问记录,查找的时间复杂度可达到O(1),其在海量数据处理中有着广泛应用)。因此,其元素的排列顺序是无序的
对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map。

class Solution {
public:
    bool containsNearbyDuplicate(vector<int>& nums, int k) {
        if(k <= 0) return false;
        if(nums.size() == 0 || nums.size() == 1) return false;
        //如果nums[i]已经在map中,判断其value和i的距离是否小于等于k,是,则返回true。否,则将nums[i]对应的value修改为当前的i值
        unordered_map<int, int> mapArr;
        for(int i = 0; i < nums.size(); i ++){
            if(mapArr.find(nums[i]) == mapArr.end())
                mapArr.insert({nums[i], i});
            else{
                if(i - mapArr[nums[i]] <= k)
                    return true;
                else
                    mapArr[nums[i]] = i;
            }
        }
        return false;
    }
};

258. Add Digits

在这里插入图片描述
此题涉及到余九定理,即
在这里插入图片描述
dr(n)表示对于数字n的digit root之和。
若a%n=b%n,则记为:
在这里插入图片描述
也即(a-b)/n为一整数,则代码如下:

class Solution {
public:
    int addDigits(int num) {
        return 1+(num-1)%9;
    }
};

268. Missing Number

在这里插入图片描述
两个方法,一种是利用高斯求和减去数组累加的结果;
一种为异或操作。异或操作思路如下:
异或规则有:a^0=a,数组值为0~n,数组下标为0~n-1我们只需要初始化异或n,将下标和数值累积异或即可,最终结果即为缺失的数字。

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int n=nums.size();
        int sum=n;
        for(int i=0;i<n;i++){
            sum =sum^i^nums[i];
        }
        return sum;
    }
};

278. First Bad Version

在这里插入图片描述
该题暴力超时了,考虑二分查找,即问题可简化成在数组中查找第一个比n大的数,相比于传统二分法,需要处理好条件–“第一个”,代码如下:

// Forward declaration of isBadVersion API.
bool isBadVersion(int version);

class Solution {
public:
    int firstBadVersion(int n) {
        long left=1,right=n;
        while(left<right){
            long mid=(left+right)/2;
            if(isBadVersion(mid)){ // 为true,即在左边
                if(mid>=2&&isBadVersion(mid-1)==false) // 正好为当前点
                    return mid;
                right=mid-1; // 不是则右指针往mid-1挪
            }
            else{  // false,则在右边
                 left=mid+1;
            }
        }
        return left;
    }
};

283. Move Zeroes

在这里插入图片描述
思路:注意题目要求不改变非0数字的相对顺序,只能使用swap实现。
使用指针,一次遍历数组,利用指针j记录0出现的下标,碰到非0数字则往前交换

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int j=0;
        for(int i=0;i<nums.size();i++){
            if(nums[i]!=0)
                swap(nums[j++],nums[i]);
        }
    }
};

289. Game of Life

在这里插入图片描述

该题难在要求原地操作,也就是O(1)的时间复杂度。此处我们使用位操作,用两位二进制来表示四个状态,即00、01、11、10,两个位置分别代表下一状态 当前状态,因为我们只需两次遍历操作,第一次遍历更改整个地图,第二次遍历使用board[i][j] = board[i][j] >> 1来对整张图原地更新,其中board[newx][newy]&1即为当前状态,做到了原地更新的效果又不更改状态。代码如下:

class Solution {
public:
    void gameOfLife(vector<vector<int>>& board) {
        int rows = board.size();
        int cols = board[0].size();
        for(int i = 0; i < rows; ++i){
            for(int j = 0; j < cols; ++j){
                int neighbors = getNeighbors(board,i,j);
                // 二进制法: 下一状态 当前 ,因而 当前状态=board[i][j]&1,即为低位的值
                //如果当前细胞是活的
                if(board[i][j]== 1){                
                    //如果活细胞周围邻居有两或三个为活细胞,则下一代继续存活
                    if(neighbors == 2  || neighbors == 3){
                        board[i][j] = 3; // 1 1
                    }
                    //如果是01,则在更新时,死掉
                    //如果小于2个或者大于3个,都是导致死亡
                }
                else{             
                    //如果当前细胞是死的,其相邻有三个活的邻居。则其变成一个活细胞
                    if(neighbors == 3){
                        board[i][j] = 2; // 1 0
                    }
                }
            }
        }
        for(int i = 0; i < rows; ++i){
            for(int j = 0; j < cols;++j){
                board[i][j] = board[i][j] >> 1;
            }
        }
    }
    
    //统计为1的邻居个数
    int getNeighbors(vector<vector<int> > &board,int x,int y){
        int rows = board.size();  // 行
        int cols = board[0].size();  //列
        int res = 0; // 统计值
        int dx[]={-1,1,0,0,-1,-1,1,1}; // 8个方向
        int dy[]={0,0,-1,1,-1,1,1,-1};
        for(int i = 0; i <8;++i){
            int newx=x+dx[i]; //更新坐标
            int newy=y+dy[i];
            if(newx>=0&&newx<rows&&newy>=0&&newy<cols){
                res += board[newx][newy]&1;
            }
        }
        return res;
    }
};

292. Nim Game

在这里插入图片描述
该题为较简单的博弈题,即若能被4整除那么怎么拿都一定输。

class Solution {
public:
    bool canWinNim(int n) {
        if(n<=3)
            return true;
        else if(n%4==0)
            return false;
        else
            return true;
        return false;
    }
};

位操作

231. Power of Two

在这里插入图片描述

使用位操作,我们注意到
在这里插入图片描述
即对于二进制数来说,n若为2的i幂次,则总是第i+1位上为1,其余均为0,而对于n-1来说,第i+1位后的数均为1,该位为0,故有(n&(n-1))==0恒成立。

class Solution {
public:
    bool isPowerOfTwo(int n) {
        return n>0&&((n&(n-1))==0);
    }
};

389. Find the Difference

在这里插入图片描述

我们使用异或的方法,方法同例题 268. Missing Number ,代码如下:

class Solution {
public:
    char findTheDifference(string s, string t) {
        int len=s.size();
        int ans=0;
        for(int i=0;i<len;i++){ 
            ans=ans^s[i]^t[i];
        }
        int tp=t[len];
        return (char)ans^tp;
    }
};

367. Valid Perfect Square

在这里插入图片描述

二分查找

class Solution {
public:
    bool isPerfectSquare(int num) {
        if(num < 1) return false;
        if(num == 1) return true;
        int left = 0, right = num/2;
        while(left <= right)
        {
            long mid = (left+right)/2;
            long val = mid*mid;
            if(val == num) return true;
            else if(val > num) right = mid-1;
            else left = mid+1;
        }
        return false;
    }
};

牛顿法:(关于牛顿法的介绍
在这里插入图片描述

class Solution {
public:
    bool isPerfectSquare(int num) {
        long r = num;
        while (r*r > num)
            r = (r + num/r) / 2;
        
        return r*r == num;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值