你不得不看的leetcode常见题(3月份每日一题)——Python

3月快刷了一半的题想起来应该记录一下…前几天有用JAVA写的有用C++写的,但后面没有标注的都是用Python写的。
小白一枚,有不对的或者需要改进的地方恳请各位大佬批评指正!

文章目录

  • 2020年三月的每日一题
    • 1号——225. 用队列实现栈
    • 2号——206. 反转链表
    • 3号——面试题 10.01. 合并排序的数组
    • 4号 ——994. 腐烂的橘子
    • 5号——1103. 分糖果 II
    • 6号——面试题57 - II. 和为s的连续正数序列
    • 7号——面试题59 - II. 队列的最大值
    • 8号——322. 零钱兑换
    • 9号——121. 买卖股票的最佳时机
      • 同类型的问题:股票问题
    • 10号——543. 二叉树的直径
    • 11号——1013. 将数组分成和相等的三个部分
    • 12号——1071. 字符串的最大公因子
    • 13号——169. 多数元素
    • 14号——300. 最长上升子序列
    • 15号——695. 岛屿的最大面积
      • 同类型的题目:1034. 边框着色
    • 16号——面试题 01.06. 字符串压缩
    • 17号——1160. 拼写单词
    • 18号——836. 矩形重叠
    • 19号——409. 最长回文串
    • 20号——面试题40. 最小的k个数
    • 21号——365. 水壶问题
    • 22号——945. 使数组唯一的最小增量
    • 23号——876. 链表的中间结点
    • 24号——面试题 17.16. 按摩师
      • 相关问题:leetcode动态规划问题
    • 25号——892. 三维形体的表面积
    • 26号——999. 车的可用捕获量
    • 27号——914. 卡牌分组
    • 28号——820. 单词的压缩编码
      • 相关问题:前缀树(Trie)
    • 29号——1162. 地图分析
    • 30号——面试题62. 圆圈中最后剩下的数字
    • 31号——912. 排序数组
      • 相关问题:几种要会书写的排序方法

2020年三月的每日一题

1号——225. 用队列实现栈

常规操作,模版题
1.Python版本

class MyStack:
    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.queue=collections.deque()

    def push(self, x: int) -> None:
        """
        Push element x onto stack.
        """
        self.queue.append(x)
        length=len(self.queue)
        while length>1:
            self.queue.append(self.queue.popleft())
            length-=1

    def pop(self) -> int:
        """
        Removes the element on top of the stack and returns that element.
        """
        return self.queue.popleft()
        

    def top(self) -> int:
        """
        Get the top element.
        """
        return self.queue[0]

    def empty(self) -> bool:
        """
        Returns whether the stack is empty.
        """
        return not bool(self.queue)

# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

C++版本

class MyStack {
private:
    queue<int> q;    
public:
    /** Initialize your data structure here. */
    MyStack() {        
    }    
    /** Push element x onto stack. */
    void push(int x) {
        int size=q.size();
        q.push(x);
        while(size--){
            int temp=q.front();
            q.pop();
            q.push(temp);
        }
    }   
    /** Removes the element on top of the stack and returns that element. */
    int pop() {
        int topEle=q.front();
        q.pop();
        return topEle;
    }  
    /** Get the top element. */
    int top() {
        return q.front();
    }   
    /** Returns whether the stack is empty. */
    bool empty() {
        return q.empty();
    }
};

2号——206. 反转链表

Java版本

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null) return head;
        ListNode p = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return p;

    }
}

3号——面试题 10.01. 合并排序的数组

从后往前进行,比较AB两个数组,那个大放进去哪个。
最后要考虑,如果最后只剩下B,则把B完全放到A的前面。
Python版本

class Solution:
    def merge(self, A: List[int], m: int, B: List[int], n: int) -> None:
        """
        Do not return anything, modify A in-place instead.
    
        """
        size=len(A)-1
        n-=1
        m-=1
        while(size and n>-1 and m>-1):
            if A[m]>=B[n]:
                A[size]=A[m]
                m-=1
            else:
                A[size]=B[n]
                n-=1
            
            size-=1
            
        if n>-1:
            for j in range(0,n+1):
                A[j]=B[j]
                j+=1

4号 ——994. 腐烂的橘子

这是一道BFS模版题。
你不得不看的leetcode常见题(3月份每日一题)——Python_第1张图片
Python版本:
广度优先搜索算法

class Solution:
    def orangesRotting(self, grid: List[List[int]]) -> int:
        row=len(grid)
        line=len(grid[0])
        time=0
        direction=[(0,1),(1,0),(0,-1),(-1,0)]
        queue=collections.deque()
        fresh=0
        
        for i in range(row):
            for j in range(line):
                if grid[i][j]==2:
                    queue.append((i,j,0))
                elif grid[i][j]==1:
                    fresh+=1
                    
                    
        while queue:
            i,j,time=queue.popleft()
            for di,dj in direction:
                if 0<=i+di<row and 0<=j+dj<line and grid[i+di][j+dj]==1:
                    grid[i+di][j+dj]=2
                    fresh-=1
                    queue.append((i+di,j+dj,time+1))
                    
        if fresh>0:
            return -1
        else:
            return time
            

C++版本:

//C++
class Solution {
    public int orangesRotting(int[][] grid) {
        int[] dr=new int[]{-1,0,1,0};
        int[] dc=new int[]{0,-1,0,1};
        
        int R=grid.length; //行数
        int C=grid[0].length; //列数
        int code=0;
        
        Queue<Integer> queue=new ArrayDeque();
        Map<Integer,Integer> depth=new HashMap();
        for(int r=0;r<R;++r){
            for(int c=0;c<C;++c){
                if(grid[r][c]==2){//找出腐烂的橙子
                    code=r*C+c;//获得橙子在数组中的位置
                    queue.add(code);//放入队列
                    depth.put(code,0);//将此时所有腐烂的橙子的时间节点记为0
                }
                
            }
        }
        
        int alltime=0;
        while(!queue.isEmpty()){ //开始进行BFS
            code=queue.remove();
            int r=code/C;
            int c=code%C;
            for(int k=0;k<4;++k){
                int nr=r+dr[k];
                int nc=c+dc[k]; 
                if(0<=nr&&nr<R&&0<=nc&&nc<C&&grid[nr][nc]==1){ //对左下右上几个方向进行判断
                    grid[nr][nc]=2;
                    int ncode=nr*C+nc;
                    queue.add(ncode); //将新变成的腐烂的橘子入队列
                    depth.put(ncode,depth.get(code)+1);
                    alltime=depth.get(ncode);
                }
            }
        }
        
        for(int[]row:grid)
            for(int v:row)
                if(v==1)
                    return -1;
        
        return alltime;
    }
}

你不得不看的leetcode常见题(3月份每日一题)——Python_第2张图片
学会的知识点:
1.Queue queue=new ArrayDeque();——ArrayDeque
队列的:
你不得不看的leetcode常见题(3月份每日一题)——Python_第3张图片
栈:
你不得不看的leetcode常见题(3月份每日一题)——Python_第4张图片(参考:https://www.cnblogs.com/loveLands/articles/9708717.html)

2.Map depth=new HashMap();——Map map = new HashMap():
其实就是一个键值对接口。
函数原型为:Map map = new HashMap();
在这个声明中,map就是一个容器,主要调用put()、get()方法;

例如:map.put(“user”, obj);
obj是之前声明的一个类的对象,比如为user1,那么就是把user这个对象放到了map容器中了,其中user只是对象的代号,在调用的时候取出,代码为map.get(user);
参数是前面写的代号,得到的是user1这个对象。

HashMap就是一个散列表,它是通过“拉链法”解决哈希冲突。
(参考:https://blog.csdn.net/Dreamlzd/article/details/77827058)

5号——1103. 分糖果 II

题目:
你不得不看的leetcode常见题(3月份每日一题)——Python_第5张图片
解题:

class Solution {
public:
    vector<int> distributeCandies(int candies, int num_people) {
        vector<int> ans(num_people,0);
        int i = 0;
        while (candies != 0) {
            ans[i % num_people] += min(candies, i + 1);
            candies -= min(candies, i + 1);
            ++i;
        }
        return ans;
    }
};

学习点:
对于一个需要循环操作的数组:
ans[i % num_people] += min(candies, i + 1); //数组中i % num_people这个写的方法很巧妙。

6号——面试题57 - II. 和为s的连续正数序列

C++版本

class Solution {
public:
    vector<vector<int>> findContinuousSequence(int target) {
        vector<vector<int>> result;
        vector<int> line;
        int sum=0;
        int count=(target-1)/2; //等效于target/2取下整
        int i,j;
        
        for(i=1;i<=count;i++){
            for(j=i;;j++){
                sum+=j;
                if(sum==target){
                    line.clear();
                    for(int k=i;k<=j;k++)
                        line.emplace_back(k); //存放和为目标值的这几个数
                    
                    result.emplace_back(line); //存放这个数组
                    sum=0;
                    break;
                } 
                
                if(sum>target){
                    sum=0;
                    break;
                }
            }
        }
        return result;
    }
};

7号——面试题59 - II. 队列的最大值

你不得不看的leetcode常见题(3月份每日一题)——Python_第6张图片
C++算法

class MaxQueue {
private:
    queue<int> q; //用来记录插入的值
    deque<int> max;//用来维护最大值
public:
    MaxQueue() {

    }   
    int max_value() {
        if(max.empty()) return -1;
        return max.front();
    }
    
    void push_back(int value) {
        while(!max.empty()&&value>max.back()){
            max.pop_back();      
        }
        max.push_back(value);
        q.push(value);

    }
    
    int pop_front() {
        if(q.empty()) return -1;
        int num=q.front();
        if(num==max.front())
            max.pop_front();
        q.pop();
        return num;

    }
};

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue* obj = new MaxQueue();
 * int param_1 = obj->max_value();
 * obj->push_back(value);
 * int param_3 = obj->pop_front();
 */

8号——322. 零钱兑换

大佬总结,模版,常看常背默https://leetcode-cn.com/problems/coin-change/solution/dong-tai-gui-hua-tao-lu-xiang-jie-by-wei-lai-bu-ke/
你不得不看的leetcode常见题(3月份每日一题)——Python_第7张图片

常规的动态解题方法:

class Solution {
    vector<int> count;
    
int dp(vector<int>& coins,int amount){
    if(amount<0) return -1;
    if(amount==0) return 0;
    if(count[amount-1]!=0) return count[amount-1];
    int Min=INT_MAX;
    for(int coin:coins){
        int res=dp(coins,amount-coin);
        if(res>=0&&res<Min)
            Min=res+1;
    }
    count[amount-1]=Min==INT_MAX?-1:Min;
    return count[amount-1];       
    }
    
public:
    int coinChange(vector<int>& coins, int amount) {
        if(amount<1) return 0;
        count.resize(amount);//设置count大小
        return dp(coins,amount);
    }
};

可以看到解题效率并不高
你不得不看的leetcode常见题(3月份每日一题)——Python_第8张图片

方法二:贪心+dfs思路:

void coinChange(vector<int>& coins, int amount, int c_index, int count, int& ans)
{
    if (amount == 0)
    {
        ans = min(ans, count);
        return;
    }
    if (c_index == coins.size()) return;

    for (int k = amount / coins[c_index]; k >= 0 && k + count < ans; k--)
    {
        coinChange(coins, amount - k * coins[c_index], c_index + 1, count + k, ans);
    }
}

int coinChange(vector<int>& coins, int amount)
{
    if (amount == 0) return 0;
    sort(coins.rbegin(), coins.rend()); //反向迭代器,从大到小排序
    int ans = INT_MAX;
    coinChange(coins, amount, 0, 0, ans);
    return ans == INT_MAX ? -1 : ans;
}

9号——121. 买卖股票的最佳时机

C++解法

class Solution {
public:
    int maxProfit(vector<int>& prices) {
       /* int last = 0, profit = 0;
        for (int i = 0; i < (int)prices.size() - 1; ++i) {
        last = max(0, last + prices[i+1] - prices[i]);
        profit = max(profit, last);
        }
        return profit;*/
        
        int dp_i_0=0,dp_i_1=INT_MIN;
        for(int i=0;i<prices.size();i++){
            dp_i_0=max(dp_i_0,dp_i_1+prices[i]);
            dp_i_1=max(dp_i_1,-prices[i]);
        }
        return dp_i_0;
        
    }
    
};

同类型的问题:股票问题

这是一连串的dp模版题,其他同样的买股票问题我单写了一篇:
https://blog.csdn.net/chuxuezheerer/article/details/104848202

10号——543. 二叉树的直径

其实就是求左右子树的最大深度。
C++:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
    int res=1;
    
     int depth(TreeNode* root){
        if(root==NULL) return 0;
        int left=depth(root->left);
        int right=depth(root->right);      
        res=max(res,left+right+1);
        
        return max(left,right)+1;
        
    }
   
public:
    int diameterOfBinaryTree(TreeNode* root) {
       depth(root);
        return res-1;
        
            
    }
};

11号——1013. 将数组分成和相等的三个部分

你不得不看的leetcode常见题(3月份每日一题)——Python_第9张图片
C++

class Solution {
public:
    bool canThreePartsEqualSum(vector<int>& A) {
        int sum=accumulate(A.begin(),A.end(),0);
        if(sum%3!=0) return false;//首先要求数组长度必须是3的倍数才能分成三堆
        
        int target=sum/3;
        int i=0;
        int length=A.size();
        int res=0;
        
        while(i<length){
            res=res+A[i];
            if(res==target)
                break;
            i++;            
        }
        if (res!=target) return false;
        
        int j=i+1;
        while(j<length-1){
            res+=A[j];
            if(res==2*target)
                return true;
            j++;
        }
        return false;

    }
};

12号——1071. 字符串的最大公因子

C++

class Solution {
    bool check(string t,string str){
        int lens=(int)str.length()/(int)t.length();
        string res="";
        for(int i=1;i<=lens;i++)
            res+=t;
        return res==str;
    }
    
    
public:
    string gcdOfStrings(string str1, string str2) {
        int len1=(int)str1.length();
        int len2=(int)str2.length();
        
        string T=str1.substr(0,__gcd(len1,len2));
        if(check(T,str1)&&check(T,str2)) 
            return T;
        
        return "";                
    }
};

13号——169. 多数元素

你不得不看的leetcode常见题(3月份每日一题)——Python_第10张图片
这道题最简单的是一种数学方法叫摩尔投票法。
方法二是常规的哈希方法。

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        s=len(nums)
        count=0
        for i in range(s):
            if count==0:
                target=nums[i]
            if nums[i]==target:
                count+=1
            else:
                count-=1
        return target
    
# 方法二:
#        count=len(nums)//2
#       dict={}
#        for num in nums:
#            if num in dict:
#                dict[num]+=1
                
#            else:
#                dict[num]=1
                
#           if dict[num]>count:
#                    return num;

14号——300. 最长上升子序列

你不得不看的leetcode常见题(3月份每日一题)——Python_第11张图片

你不得不看的leetcode常见题(3月份每日一题)——Python_第12张图片

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        dp=[0]*len(nums) #从小到大存放nums数组
        res=0
        for num in nums:
            i,j=0,res
            while i<j:
                mid=(i+j)//2 #其实相当于在dp数组查找中运用二分法
                if dp[mid]<num :
                    i=mid+1
                else:
                    j=mid
                    
            dp[i]=num 
            if j==res:res+=1
        return res

方法二,相当于存放每个数组的逆序数

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if not nums: return 0
        dp = [1] * len(nums)
        for i in range(len(nums)):
            for j in range(i):
                if nums[j] < nums[i]: 
                    dp[i] = max(dp[i], dp[j] + 1)
        return max(dp)

15号——695. 岛屿的最大面积

你不得不看的leetcode常见题(3月份每日一题)——Python_第13张图片

方法一:深度优先遍历BFS

#Python3 
class Solution:
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
        m=len(grid)
        if m==0:return 0
        n=len(grid[0])
    
        
        
        def dfs(i,j):
            if i<0 or i>=m or j<0 or j>=n:
              return 0;
            if grid[i][j]==0:
                return 0
            grid[i][j]=0
            top=dfs(i,j-1)
            bottom=dfs(i,j+1)
            left=dfs(i-1,j)
            right=dfs(i+1,j)
            res=1+sum([top,bottom,left,right])
            return res
    
    
        ans=0
        for i in range(m):
            for j in range(n):
                ans=max(ans,dfs(i,j))
            
        return ans

可以看到这个算法时间和空间的需求都蛮大的:
你不得不看的leetcode常见题(3月份每日一题)——Python_第14张图片

方法二:用广度优先遍历

#Python
class Solution:
    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
        row=len(grid)
        line=len(grid[0])
        #官网答案写法:
         #for i, l in enumerate(grid):
        #enumerate()函数:讲一个可遍历的数据对象组合成一个索引序列,同时列出数据和数据下标。
            #for j, n in enumerate(l):

        res=0
      
        
        for i in range(row):
            for j in range(line):
                queue=collections.deque([(i,j)])
                cur=0
                
                while queue:
                    cur_i,cur_j=queue.popleft()
                    if cur_i<0 or cur_j<0 or cur_i>=row or cur_j>=line or grid[cur_i][cur_j]!=1:
                        continue
                        
                    cur+=1
                    grid[cur_i][cur_j]=0
                    for di,dj in [[1,0],[0,1],[0,-1],[-1,0]]:
                        next_i=cur_i+di
                        next_j=cur_j+dj
                        queue.append((next_i,next_j))
                    res=max(res,cur)
        return res
                        
                

其实就是把递归用一个队列来进行维护,
你不得不看的leetcode常见题(3月份每日一题)——Python_第15张图片

同类型的题目:1034. 边框着色

你不得不看的leetcode常见题(3月份每日一题)——Python_第16张图片

注意是求连通的边界值!

class Solution:
    def colorBorder(self, grid: List[List[int]], r0: int, c0: int, color: int) -> List[List[int]]:
        if not grid or len(grid[0])==0:
            return grid
        
        row=len(grid)
        line=len(grid[0])

        queue=collections.deque([(r0,c0)])
        visit={(r0,c0)} #记录被访问的位置
        res=grid[r0][c0] #记录最开始的颜色
        
        while queue:
            i,j=queue.popleft()
            
            for di,dj in [[0,1],[-1,0],[0,-1],[1,0]]:
                cur_i=i+di
                cur_j=j+dj
                if (cur_i,cur_j) in visit:
                    continue #如果已经被访问过,则跳过后面步骤
                
                if cur_i<0 or cur_j<0 or cur_i>=row or cur_j>=line or grid[cur_i][cur_j]!=res:
                    grid[i][j]=color #边界判断,如果为边界值,则原i,j位置的color变
                elif 0<=cur_i<row and 0<=cur_j<line and grid[cur_i][cur_j]==res:
                    queue.append((cur_i,cur_j))
                    visit.add((cur_i,cur_j))     #将连通的值入栈,入访问序列  
                
        return grid
        
    

你不得不看的leetcode常见题(3月份每日一题)——Python_第17张图片

16号——面试题 01.06. 字符串压缩

你不得不看的leetcode常见题(3月份每日一题)——Python_第18张图片

class Solution:
    def compressString(self, S: str) -> str:
        if not S:
            return S
        ch=S[0]
        res=''
        count=0
        for num in S:
            if num==ch:
                count+=1
            else:
                res+=ch+str(count)
                count=1
                ch=num
        res+=ch+str(count)
        
        return res if len(res)<len(S) else S

17号——1160. 拼写单词

你不得不看的leetcode常见题(3月份每日一题)——Python_第19张图片
在Python中用一个字典统计,遍历每一个单词,如果这个单词每个字母对应的数量小于整个字典统计每个字母的数量,则可以构成这个单词。

class Solution:
    def countCharacters(self, words: List[str], chars: str) -> int:
        char_count=collections.Counter(chars)
        res=0
        
        for word in words:
            word_count=collections.Counter(word)
            
            if all(word_count[i]<=char_count[i] for i in word_count):
                res+=len(word)
              
        return res

18号——836. 矩形重叠

你不得不看的leetcode常见题(3月份每日一题)——Python_第20张图片
这种空间问题转换为对横纵坐标的操作。
你不得不看的leetcode常见题(3月份每日一题)——Python_第21张图片

class Solution:
    def isRectangleOverlap(self, rec1: List[int], rec2: List[int]) -> bool:
        
        x_left=max(rec1[0],rec2[0])
        x_right=min(rec1[2],rec2[2])
        
        y_top=min(rec1[3],rec2[3])
        y_bottom=max(rec1[1],rec2[1])
        #右边最小的>左边最大的,则横坐标交叉;
        #上边最小的>下边最大的,则纵坐标交叉
        if x_right-x_left>0 and y_top-y_bottom>0:
            return True
        else:
            return False

19号——409. 最长回文串

你不得不看的leetcode常见题(3月份每日一题)——Python_第22张图片
还是要考虑数学特性,所有数量为偶数的可以直接要,数量为奇数的,可以-1变成偶数加入,最后结果如果原来含有奇数要在+1,因为可以以中心为原点形成对称。

class Solution:
    def longestPalindrome(self, s: str) -> int:
        length=len(s)
        num=collections.Counter(s)
        res=0
        max_odd=0
        for key in num:
            if(num[key]%2==0):
                res+=num[key]
                
            else:
                res+=num[key]-1
                    
        return res if length==res else res+1

20号——面试题40. 最小的k个数

你不得不看的leetcode常见题(3月份每日一题)——Python_第23张图片
如果不考虑用Python原函数直接排序(如果面试中用的话会被打死吧~~~),就用堆排序或者快排
快排思想排序:

class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        length=len(arr)
        if k<=0 or k>length:
            return list()
        
        left,right=0,length-1
        while left<right:
            res=self.partition(arr,left,right)
            if res==k-1:
                return
            elif res<k-1:
                res=self.partition(arr,res+1,right)
            else: 
                res=self.partition(arr,left,res-1)    
        return arr[:k]
         
    def partition(self,arr,left,right):
        cur_res=arr[left]
        while left<right:
            while left<right and arr[right]>=cur_res:
                right-=1
            arr[left]=arr[right]  
            
            while left<right and arr[left]<=cur_res:
                left+=1
            arr[right]=arr[left]          
        arr[left]=cur_res        
        return left

堆排序,这个要注意,求前K小,用的是大根堆排序。先对前K个数字进行排序,然后再从K+1个数开始,如果小于堆顶,则把堆顶推出(说明堆顶肯定不是前K小的数字),将这个数放到堆顶,在进行调整。

class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        length=len(arr)  
        begin=k//2-1
        while begin>=0:
            self.adjust_heap(arr,k,begin)
            begin-=1
        for i in range(k,length):
            if arr[i]<arr[0]:#比较这个元素和堆顶元素,若小,
                arr[0]=arr[i]#则代替堆顶
                self.adjust_heap(arr,k,0)#进行大根堆排序
            
        return arr[:k]#最后返回前K个数即为所求,但不一定有顺序
 
    def adjust_heap(self,arr,k,begin):
        num=begin
        child=num*2+1
        while num<k and child<k:
            if child+1<k and arr[child]<arr[child+1]:
                child=child+1
                
            if arr[child]>arr[num]:
                arr[num],arr[child]=arr[child],arr[num]
                
            num=child
            child=child*2+1

21号——365. 水壶问题

你不得不看的leetcode常见题(3月份每日一题)——Python_第24张图片

方法一:水壶倒水一共有几种状态,将这些状态全部遍历,然后用一个集合记录经历的状态(防止反复经历,造成无尽的循环)。

class Solution:
    def canMeasureWater(self, x: int, y: int, z: int) -> bool:
        stack = [(0, 0)]
        self.seen = set()
        while stack:
            remain_x, remain_y = stack.pop()
            if remain_x == z or remain_y == z or remain_x + remain_y == z:
                return True
            if (remain_x, remain_y) in self.seen:
                continue
            self.seen.add((remain_x, remain_y))
            # 把 X 壶灌满。
            stack.append((x, remain_y))
            # 把 Y 壶灌满。
            stack.append((remain_x, y))
            # 把 X 壶倒空。
            stack.append((0, remain_y))
            # 把 Y 壶倒空。
            stack.append((remain_x, 0))
            # 把 X 壶的水灌进 Y 壶,直至灌满或倒空。
            stack.append((remain_x - min(remain_x, y - remain_y), remain_y + min(remain_x, y - remain_y)))
            # 把 Y 壶的水灌进 X 壶,直至灌满或倒空。
            stack.append((remain_x + min(remain_y, x - remain_x), remain_y - min(remain_y, x - remain_x)))
        return False

方法二:数学方法,这个要积累了,应用裴蜀定理

裴蜀定理(或贝祖定理):对任何整数a、b和它们的最大公约数d,关于未知数x和y的线性不定方程(称为裴蜀等式):若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by都一定是d的倍数,特别地,一定存在整数x,y,使ax+by=d成立。

class Solution:
    def canMeasureWater(self, x: int, y: int, z: int) -> bool:
        if x + y < z:
            return False
        if x == 0 or y == 0:
            return z == 0 or x + y == z
        return z % math.gcd(x, y) == 0

你不得不看的leetcode常见题(3月份每日一题)——Python_第25张图片

22号——945. 使数组唯一的最小增量

你不得不看的leetcode常见题(3月份每日一题)——Python_第26张图片

先把原来的list排序,让每后一个元素均大于前一个元素。每个元素值各不相同,增加最少的次数即位所有。有点贪心算法的意思。

class Solution:
    def minIncrementForUnique(self, A: List[int]) -> int:
        A.sort()
        count=0
        for i in range(1,len(A)):
            if A[i-1]>=A[i]:
                count+=A[i-1]-A[i]+1
                A[i]=A[i-1]+1
                
        return count      
         

你不得不看的leetcode常见题(3月份每日一题)——Python_第27张图片

23号——876. 链表的中间结点

自己的解题思路:设计一个right指针,首先遍历一边链表,获得链表长度,计算中心点的位置,然后再移动head指针到最终位置上

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None


class Solution:
    def middleNode(self, head: ListNode) -> ListNode:
        if head==None:
            return
        right=head 
        res=1
        
        while right.next!=None:
            right=right.next
            res+=1
                
        for i in range(res//2):
            head=head.next
            
        return head
        

你不得不看的leetcode常见题(3月份每日一题)——Python_第28张图片

看官方解题答案后,觉得使用快慢指针这个思想很好,使用slow,fast两个指针,fast移动速度是slow的两倍,那么等到fast移动到末尾的时候,slow正好在中间。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def middleNode(self, head: ListNode) -> ListNode:
        slow=fast=head
        
        while fast and fast.next:
            slow=slow.next
            fast=fast.next.next
            
        return slow
        

你不得不看的leetcode常见题(3月份每日一题)——Python_第29张图片

24号——面试题 17.16. 按摩师

典型的动态规划问题。
分状态回答,设两个状态,
dp[i][0]=max(dp[i-1][0],dp[i-1][1]) #此时没有按摩状态=max(昨天没有按摩状态,昨天有按摩状态)
dp[i][1]=dp[i-1][0]+nums[i] #今天有按摩状态=昨天没有按摩状态+今天按摩了

class Solution:
    def massage(self, nums: List[int]) -> int:
        
        length=len(nums)
        if length==0:
            return 0
        dp_0=tdp_0=0
        dp_1=nums[0]
        
        
        for i in range(1,length):
            tdp_0=max(dp_0,dp_1)
            dp_1=dp_0+nums[i]
            dp_0=tdp_0
            
            
        return max(dp_0,dp_1) 
            

你不得不看的leetcode常见题(3月份每日一题)——Python_第30张图片

相关问题:leetcode动态规划问题

25号——892. 三维形体的表面积

你不得不看的leetcode常见题(3月份每日一题)——Python_第31张图片

首先一个正方体有6个面,在同一个格子上的,1.要减去摞起来的面,设一个格子内有n个正方体,则要减去2(n-1)个面。2.要减去和这个相邻格子内的立方体的重叠数。

class Solution:
    def surfaceArea(self, grid: List[List[int]]) -> int:
        row=len(grid)
        line=len(grid[0])
        res=0
        
        for i in range(row):
            for j in range(line):
                if grid[i][j]==0:#如果这个格子没有立方体,则不做任何操作
                    continue
                res+=6*grid[i][j]-2*(grid[i][j]-1) #在一个格子内叠起来的立方体
                for di,dj in [[1,0],[0,-1],[-1,0],[0,1]]:#类似DFS遍历
                    if 0<=i+di<row and 0<=j+dj<line:
                        res-=min(grid[i][j],grid[i+di][j+dj])#减去两个相邻格子间最小数的面
                        
        return res

26号——999. 车的可用捕获量

你不得不看的leetcode常见题(3月份每日一题)——Python_第32张图片

这个题有点深度优先遍历的算法思想,但这里没有用递归也没有用栈来辅助,反而用了四次循环的思想。

class Solution:
    def numRookCaptures(self, board: List[List[str]]) -> int:
        row=len(board)
        line=len(board[0])
        si,sj=0,0
        dx,dy=[0,1,0,-1],[1,0,-1,0]
        
        for i in range(row):
            for j in range(line):
                if board[i][j]=="R":
                    si,sj=i,j
                    break #先遍历整个数组,找到白色车所在的位置                  
        res=0     
        for i in range(4):#四个方向进行
            step=0
            while True:
                new_i=si+dx[i]*step
                new_j=sj+dy[i]*step #新的位置              
                
                if new_i<0 or new_i>=8 or new_j<0 or new_j>=8 or board[new_i][new_j]=="B":#若碰到边界或者碰到白色的象
                    break                    
                if board[new_i][new_j]=="p":#若碰到黑色的车
                    res+=1
                    break
                             
                step+=1 #如果什么都没碰到,那继续向前走        
        return res              
                

27号——914. 卡牌分组

你不得不看的leetcode常见题(3月份每日一题)——Python_第33张图片

先统计每个数字出现的次数,然后取各个出现次数的公约数,如果最大公约数大于2,则可以分组。

class Solution:
    def hasGroupsSizeX(self, deck: List[int]) -> bool:
        from fractions import gcd
        count=collections.Counter(deck).values()
        
        return reduce(gcd,count)>=2

28号——820. 单词的压缩编码

这个题的内容对于我来说是一种崭新的内容。官方题解中用了两种方法:
方法一:存储后缀

1.discard()函数:discard() 方法用于移除指定的集合元素。 该方法不同于 remove() 方法,因为 remove() 方法在移除一个不存在的元素时会发生错误,而 discard() 方法不会。

class Solution:
    def minimumLengthEncoding(self, words: List[str]) -> int:
        good = set(words)
        for word in words:
            for k in range(1, len(word)):
                good.discard(word[k:]) #例如good={time,me},那么操作过程是,移除"ime"(没有),移除"me"(有,移除me)

        return sum(len(word) + 1 for word in good)
        

你不得不看的leetcode常见题(3月份每日一题)——Python_第34张图片

方法二:字典树
借鉴这位大佬的代码:https://leetcode-cn.com/problems/short-encoding-of-words/solution/python-zi-dian-shu-by-amir-6/
构造字典树是将字符串从后往前处理。最后的答案是叶子的层数+1.

这里借鉴两个大佬所写的
1.用递归

class Solution:
    def minimumLengthEncoding(self, words: List[str]) -> int:
        class Node:
            def __init__(self, l):  
                self.l = l # 层数
                self.children = {}
        
        root = Node(0)
        def build(t, w): # 递归构造字典树
            if not w:
                return
            if w[-1] not in t.children:
                t.children[w[-1]] = Node(t.l + 1)
            build(t.children[w[-1]], w[:-1])
        for w in words:
            build(root, w)
        ans = [0] # 相当于全局变量,以便在递归中累加

        def vis(t):  # 计算答案
            if len(t.children) == 0: # 是叶子节点
                if t.l > 0:
                    ans[0] += t.l + 1 # 累加
            for c in t.children.values():
                vis(c)
        vis(root)
        return ans[0]

你不得不看的leetcode常见题(3月份每日一题)——Python_第35张图片
2.不用递归

class Trie:

    def __init__(self): # 初始化
        """
        Initialize your data structure here.
        """
        self.Trie = {}

    def insert(self, word): # 插入一个单词 word
        """
        Inserts a word into the trie.
        :type word: str
        :rtype: void
        """
        curr = self.Trie  # 将字典赋给curr
        for w in word:  # 循环每个字母
            if w not in curr: # 如果字母不在字典中
                curr[w] = {}  # 新建一个字典,用于存储这个w之后的字母
            curr = curr[w]  # 赋值给curr,这样下一个字母就会继续嵌套在字典的字典里
        curr['#'] = 1 # 最后一个字母作为key,它的value本来是个{},现在使value='#'

    def isTail(self, word): # 看word在不在trie中
        """
        Returns if the word is in the trie.
        :type word: str
        :rtype: bool
        """
        curr = self.Trie
        for w in word:
            curr = curr[w]  # 随着字母的循环,一直走入嵌套的字典中
        return len(curr) == 1 # 因为最后字母的value是#,
        # 所以长度是1.如果Trie中没有这个词,那就没有最后的#
      
class Solution:
    def minimumLengthEncoding(self, words: List[str]) -> int:
        trie = Trie()  #  初始化一个字典树
        cnt = 0  # 记录长度
        words = set(words)  # 将words转为set,去掉重复的单词
        for word in words:
            trie.insert(word[::-1])  # 倒序插入字典树中,因为是要判断后缀
            # 看trie.insert的函数构造,可以看出,如果这个单词是另一个单词的后缀,
            # 那它是不会成为一条新的分支的,也就是说 判断它isTail是false的,因为要len==1
            
        for word in words:
            if trie.isTail(word[::-1]): # 看这个单词在不在字典树中
                cnt += len(word) + 1
        return cnt

参考链接:https://leetcode-cn.com/problems/short-encoding-of-words/solution/python3-hou-zhui-shu-mo-ban-ti-820-dan-ci-de-ya-su/

相关问题:前缀树(Trie)

29号——1162. 地图分析

你不得不看的leetcode常见题(3月份每日一题)——Python_第36张图片

这道题还是DFSorBFS做法的变形
这里我用DFS来进行

class Solution:
    def maxDistance(self, grid: List[List[int]]) -> int:
        row=len(grid)
        line=len(grid[0])
        #dist用来存放距离陆地的距离
        dist=[[float('inf') for _ in range(row)]for _ in range(line)]
        #visit用来判断这个位置是否访问过
        visit=[[False for _ in range(row)] for _ in range(line)]
       
        queue=collections.deque()
        land_num=0
        res=0
        total=row*line
        #先将整个表格进行遍历,将陆地位置存入队列
        for i in range(row):
            for j in range(line):
                if grid[i][j]==1:
                    dist[i][j]=0
                    visit[i][j]=True
                    queue.append((i,j))
                    land_num+=1
         #记录陆地数,若全是陆地或者全是海洋,则返回-1           
        if land_num==total or land_num==0:
            return -1
        #然后开始广度优先遍历
        while queue:
            x,y=queue.popleft()
            for i,j in [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]:
                if 0<=i<row and 0<=j<line and visit[i][j]==False:
                    dist[i][j]=min(dist[i][j],dist[x][y]+1)
                    res=max(res,dist[i][j])
                    visit[i][j]=True
                    queue.append((i,j))
                    
        return res
                    
     

你不得不看的leetcode常见题(3月份每日一题)——Python_第37张图片

30号——面试题62. 圆圈中最后剩下的数字

首先先贴一个不是数学方法的方法(毕竟如果是第一次遇见不知道这个数学方法怎么办!)
其实就是用数组记录,如果读取到这个位置就pop出来。

class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        arr=[i for i in range(n)]
        
        num=(m-1)%len(arr)
        
        while len(arr)!=1:
            arr.pop(num)
            num=(num+(m-1))%len(arr)
            
        return arr[0]

因为借助了系统已经写好的pop方法,但很明显这样做时间效率很低。
你不得不看的leetcode常见题(3月份每日一题)——Python_第38张图片
方法二:数学方法
写了半天也没弄清楚,看过答案才知道要用约瑟夫环(??什么东西),然后去补习一波,
借鉴大佬写的,这是我看能看懂的一篇!
你不得不看的leetcode常见题(3月份每日一题)——Python_第39张图片

class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        num=0
        for i in range(2,n+1):
            num=(num+m)%i
        return num

你不得不看的leetcode常见题(3月份每日一题)——Python_第40张图片

31号——912. 排序数组

你不得不看的leetcode常见题(3月份每日一题)——Python_第41张图片
这道题没什么难的,主要是练习各种排序问题的写法。这些模版需要好好理解背过。

相关问题:几种要会书写的排序方法

1.快排

class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        left=0
        right=len(nums)-1
        self.quickSort(nums,left,right)
        return nums
    
    def quickSort(self,nums,left,right):
        if left<right:
            pi=self.partition(nums,left,right)
            self.quickSort(nums,left,pi)
            self.quickSort(nums,pi+1,right)     
        
    def partition(self,nums,left,right):
        num=nums[left]
        while left<right:
            while left<right and nums[right]>=num:
                right-=1
            nums[left]=nums[right]
            while left<right and nums[left]<=num:
                left+=1
            nums[right]=nums[left]
            
        nums[left]=num
        
        return left
        

你不得不看的leetcode常见题(3月份每日一题)——Python_第42张图片
2.归并排序

class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        nums=self.merge_sort(nums)
        return nums
        
    def merge_sort(self,nums):
        if len(nums)==1:
            return nums
        mid=len(nums)//2
        left=nums[:mid]
        right=nums[mid:] #从第m+1个元素取到最后
        return self.merge(self.merge_sort(left),self.merge_sort(right))
    
    def merge(self,left,right):
        res=[]
        i,j=0,0
        while i<len(left) and j<len(right):
            if left[i]<right[j]:
                res.append(left[i])
                i+=1
            else:
                res.append(right[j])
                j+=1
        res+=left[i:]
        res+=right[j:]
        return res
               
        

你不得不看的leetcode常见题(3月份每日一题)——Python_第43张图片

3.堆排序
原理感觉这个视频讲的很好:
https://www.bilibili.com/video/av18980178?from=search&seid=3518072115040122033

class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        nums=self.heapSort(nums)
        return nums
    
    def heapSort(self,nums):
        n=len(nums)
        #首先是建堆,建成大根堆的模样
        for i in range(n//2-1,-1,-1):
            self.adjustHeap(nums,i,n)
        #开始调整堆,因为堆顶现在是最大元素,所以把堆顶和最后一个元素交换,再对除去最后一个元素的堆进行调整。
        for i in range(n-1,-1,-1):
            nums[0],nums[i]=nums[i],nums[0]
            self.adjustHeap(nums,0,i)
        return nums
                   
    def adjustHeap(self,nums,start,end):
        num=nums[start]
        pos=start
        childpos=pos*2+1
        while childpos<end:
            rightpos=childpos+1
            if rightpos<end and nums[childpos]<nums[rightpos]:
                childpos=rightpos #比较两个孩子结点的大小,让指针指向大一点的那个
            if num<nums[childpos]:
                nums[pos]=nums[childpos]
                pos=childpos
                childpos=pos*2+1 #开始进行结点调整,让大的结点往上走
            else:
                break
        nums[pos]=num
             

在这里插入图片描述

4.计数排序
有点哈希思想,统计每个数字出现的次数,然后从小到大输出。
你不得不看的leetcode常见题(3月份每日一题)——Python_第44张图片

class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        if not nums:
            return []
        
        n=len(nums)
        min_num=min(nums)
        max_num=max(nums)
        arr=[0]*(max_num-min_num+1)
        for num in nums:
            arr[num-min_num]+=1 #arr用来统计原来每个num出现的次数
        
        j=0
        for i in range(n):
            while arr[j]==0:
                j+=1
                
            nums[i]=j+min_num
            arr[j]-=1
        return nums
        

可以看到时间是最快的了吧
在这里插入图片描述
5.桶排序
这道题桶排序不明显(有点类似于计数排序的加强版,现根据桶的要求归类,再把每个桶内进行排序),这里贴一下大佬写的代码吧。
https://leetcode-cn.com/problems/sort-an-array/solution/python-shi-xian-de-shi-da-jing-dian-pai-xu-suan-fa/

def bucket_sort(nums, bucketSize):
    if len(nums) < 2:
        return nums
    _min = min(nums)
    _max = max(nums)
    # 需要桶个数
    bucketNum = (_max - _min) // bucketSize + 1
    buckets = [[] for _ in range(bucketNum)]
    for num in nums:
        # 放入相应的桶中
        buckets[(num - _min) // bucketSize].append(num)
    res = []

    for bucket in buckets:
        if not bucket: continue
        if bucketSize == 1:
            res.extend(bucket)
        else:
            # 当都装在一个桶里,说明桶容量大了
            if bucketNum == 1:
                bucketSize -= 1
            res.extend(bucket_sort(bucket, bucketSize))
    return res

你可能感兴趣的:(LeetCode)