top100题-后50题


2022/7/22


52 LRU缓存

考查知识点: 设计题  LRU   LinkedHashMap  HashMap  DLinkedList
思路1: 使用哈希表+双向链表  (推荐)
难度: hard   没有思路
难点在于 如果插入关键字导致缓存超出,要找到最长时间没有使用过的。
		get和put时间复杂度是O(1)

struct Node{
    int key;
    int value;
    Node* prev;
    Node* next;
    Node(): key(0), value(0), prev(nullptr), next(nullptr){}
    Node(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr){}

};
class LRUCache {
private:
    Node *dummyHead, *dummyTail;
    unordered_map<int , Node*> map;
    int capacity;
    int cur_size;
public:
    LRUCache(int capacity): capacity(capacity) {   // 形参和类变量重名了
        dummyTail=new Node();
        dummyHead= new Node();
        dummyHead->next=dummyTail;
        dummyTail->prev=dummyHead;

        cur_size=0;
    }
    
    int get(int key) {
        if(!map.count(key)){
            return -1;
        }
        Node* node=map[key];
        moveToHead(node);
        return node->value;
    }
    
    void put(int key, int value) {
        if(!map.count(key)){
            Node* node = new Node(key, value);
            map[key]=node;
            if(cur_size==capacity){
                Node* tail = removeTail();
                map.erase(tail->key);
                delete tail;
                --cur_size;
            }
            addToHead(node);
            ++cur_size;
            return;
        }
        Node* node = map[key];
        node->value=value;
        moveToHead(node);
        return;
    }
    void addToHead(Node* node){
        dummyHead->next->prev=node;
        node->next=dummyHead->next;
        node->prev=dummyHead;
        dummyHead->next=node;
    }
    void moveToHead(Node* node){
        removeNode(node);
        addToHead(node);
    }
    Node* removeNode(Node* node){   // 逻辑删除节点,一定要返回这个节点。
        node->next->prev=node->prev;
        node->prev->next=node->next;
        return node;
    }
    Node* removeTail(){
        if(dummyTail->prev==dummyHead){
            return nullptr;
        }
        Node* node = dummyTail->prev;
        return removeNode(node);
        
    }
};

53 排序链表

考查: 链表 排序
思路1: 插入排序
思路2:快排
思路3: 堆排
思路4:(推荐)时间复杂度在nlogn级别的排序算法有快排,归并,堆排。其中快排用一个vector,
 堆排用priority_queue这样的中间容器也可以实现排序。但是空间复杂度在O(N)。
 所以面试官最想看到的算法是归并。 如果空间复杂度要求在O(1)。 无疑是自底向上的归并排序。
 如果空间复杂度要求在O(logN), 可以是自顶向下的归并排序。
综上,采用自底向上的归并排序最佳。   否则回家等通知


class Solution {
public:
    ListNode* sortList(ListNode* head) {
        if(!head||!head->next) return head;
        ListNode* p=head;
        int len=0;
        while(p){
            ++len;
            p=p->next;
        }
        int sublength=1;
        ListNode* dummyHead=new ListNode();
        dummyHead->next=head;
        for(int sublength=1;sublength<len;sublength*=2){  // 枚举每轮归并排序
            ListNode* prev = dummyHead;
            ListNode* cur=dummyHead->next;
            while(cur){   // 遍历整个链表
                ListNode* head1 = cur;
                for(int i=1;i<sublength&&cur->next!=nullptr;++i){ // 找到第一个子链表尾结点
                    cur=cur->next;
                }
                ListNode* head2=cur->next;
                cur->next=nullptr;
                cur=head2;
                if(head2){  // 第二个链表如果存在, 找到第二个子链表尾结点
                    for(int i=1;i<sublength&&cur->next!=nullptr;++i){
                        cur=cur->next;
                    }
                }
                ListNode* next=nullptr;// 如果第二个子链表不存在,next=nullptr.否则就是第二个子链表尾结点的后继
                if(head2){
                    next=cur->next;
                    cur->next=NULL;
                }
                prev->next = merge(head1, head2);
                while(prev->next!=nullptr){
                    prev=prev->next;
                }   // prev指向每轮归并排序已经处理好的子链表最后一个节点。
                cur=next;
            }
        }
        return dummyHead->next;
    }
    ListNode* merge(ListNode* head1, ListNode* head2){
        ListNode *p1=head1, *p2=head2;
        ListNode* dummyHead = new ListNode();
        ListNode* tail=dummyHead;
        while(p1&&p2){
            if(p1->val<p2->val){
                tail->next=p1;
                tail=p1;
                p1=p1->next;
            }else{
                tail->next=p2;
                tail=p2;
                p2=p2->next;
            }
        }
        if(p1){
            tail->next=p1;
        }
        if(p2){
            tail->next=p2;
        }
        ListNode* p = dummyHead->next;
        return dummyHead->next;

    }
};

54 乘积最大子数组

题意: 求乘积最大的子数组
考查: dp

思路1: dp
状态: dp[i]表示以第i个数字结尾的最大乘积
状态转移方程: 我的考虑是这样的,如果当前数字是正数, 那么前面如果有偶数个负数,那么dp[i]可以是
从数组开头到当前元素的成绩。 如果前面有奇数个负数,那么dp[i]可以是从第一个负数之后到当前元素的乘积。
	如果当前数字是负数,那么前面如果有偶数个负数,那么从第一个负数之后到当前元素乘积。如果前面有
奇数的负数,那么dp[i]可以是从开头到当前元素的乘积。
	上述讨论时,没有考虑到0。如果出现,可以想象成0以及0左边的数组删去之后留下来的数组作为新数组。
	
思路2: 一开始,我猜测 dp[i] = max(nums[i], nums[i]*dp[i-1])。 但是这是错的。当nums[i]是负数。
会出现错误。 之后一直想不出来,看了题解才知道,维护了两个dp数组, 一个存最大乘积,一个存最小乘积。
正确的状态转移方程: max_dp[i] = max(nums[i], nums[i]*max_dp[i-1], nums[i]*min_dp[i-1])。
min_dp[i] = min(nums[i], nums[i]*max_dp[i-1], nums[i]*min_dp[i-1])。    (推荐)

难度: meduim

// 思路1实现
class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int n=nums.size();
        if(n==0) return 0;
        if(n==1) return nums[0];
        vector<int> dp(n, 0);
        dp[0]=nums[0];
        int negative_first=-1;
        int negative=0;
        int prefix1=1;  
        int prefix2=1;
        bool flag=false;
        if(nums[0]!=0){
            if(nums[0]<0){
                negative=1;
                flag=true;
            }
            prefix1*=nums[0];
        }
        for(int i=1;i<n;++i){
            if(nums[i]==0){
                dp[i]=0;
                negative=0;
                prefix1=1;  // 从开始到i-1的乘积
                prefix2=1;  // 从第一个负数之后到i-1的乘积
                flag=false;      
            }else if(nums[i]>0){  // 正数
                if(negative%2==0){
                    dp[i]=nums[i]*prefix1;
                }else{
                    dp[i]=nums[i]*prefix2;
                }
                prefix1*=nums[i];
                if(flag) prefix2*=nums[i];

            }else{  // 负数
                if(negative%2==0){
                    dp[i]=nums[i]*prefix2;
                }else{
                    dp[i]=nums[i]*prefix1;
                }
                if(flag) prefix2*=nums[i];
                flag=true;
                prefix1*=nums[i];
                ++negative;
            }
        }
        int ans=INT_MIN;
        for(int i=0;i<n;++i){
            ans = max(dp[i], ans);
        }
        return ans;
    }
};

2022/7/23


55 最小栈

题意: 实现一个最小栈, 包含getMin()函数, 每次能返回栈中最小数。
思路1: 借助一个辅助栈, 这个栈栈顶存放当前数据栈中最小值。 官方有点trick,可以学学。 (推荐)
扩展: 实现最小队列  [有难度]  

难度: medium
// 我实现的代码
class MinStack {
public:
    MinStack() {
    }
    
    void push(int val) {
        stk.push(val);
        if(min_stk.empty()){
            min_stk.push(val);
        }else{
            min_stk.push(min(min_stk.top(), val));
        }
    }
    
    void pop() {
        stk.pop();
        min_stk.pop();
    }
    
    int top() {
        return stk.top();
    }
    
    int getMin() {
        return min_stk.top();
    }
private:
    stack<int> stk;
    stack<int> min_stk;
};

// 官方
class MinStack {
public:
    MinStack() {
        min_stk.push(INT_MAX);
    }
    
    void push(int val) {
        stk.push(val);
        min_stk.push(min(min_stk.top(), val));
    }
    
    void pop() {
        stk.pop();
        min_stk.pop();
    }
    
    int top() {
        return stk.top();
    }
    
    int getMin() {
        return min_stk.top();
    }
private:
    stack<int> stk;
    stack<int> min_stk;
};

56 相交链表

考查: 链表 双指针    
思路1: 快慢指针 (推荐)
难度: easy
// 思路1
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if(!headA||!headB) return nullptr;
        ListNode *p1=headA, *p2=headB;
        int len1=0, len2=0;
        while(p1){
            ++len1;
            p1=p1->next;
        }
        while(p2){
            ++len2;
            p2=p2->next;
        }
        p1=headA, p2=headB;
        if(len1<len2){
            swap(p1, p2);
            swap(len1, len2);
        }
        int k = len1-len2;
        while(k--){
            p1=p1->next;
        }
        while(p1&&p2&&p1!=p2){
            p1=p1->next;
            p2=p2->next;
        }
        if(p1&&p1==p2) return p1;
        return nullptr;

    }
};

57 多数元素

考查: 数组 多数元素 脑筋急转弯 抵消思想
思路1: 因为多数元素在数组中出现次数最多,在数量上占有优势,按照抵消的思想,对两个不同的元素进行
相消。遇到两个相同的元素, 暂时保留下来。 这样数组最后剩余的肯定是多数元素。 
比如[2,3,3,1,4,5,4,4,4,4,4]。
第一次 消去2,3
第二次 消去3 1
第三次消去 4 5
第四次 4和4 暂时保留。。。。最终剩下来4
 
 这种方法叫做摩尔投票法   (推荐)
class Solution {
public:
    int majorityElement(vector<int>& nums) {
        // 抵消思想  
        int cur=INT_MIN; // cur表示当前的候选者, 值为INT_MIN, 表示当前候选者为空
        int cnt=0;   // 表示投当前这个候选者的票数
        for(int i=0;i<nums.size();++i){
            if(cur==INT_MIN){
                cur=nums[i];
                ++cnt;
            }else if(nums[i]==cur){
                ++cnt;
            }else{
                --cnt;
                if(cnt==0) cur=INT_MIN;
            }
        }
        return cur;
    }
};

58 打家劫舍

考查: 回溯 dp
思路1: 回溯
思路2: 线性dp  dp[i]表示第i个屋子结尾的最大money。
思路3: 线性dp  dp[i]表示从0到i个屋子的最大money   (推荐)
// 思路1
class Solution {
public:
    int rob(vector<int>& nums) {
        int n=nums.size();
        if(n==0) return 0;
        int cur_money=0;
        for(int i=0;i<n;++i){
            dfs(nums,i, cur_money, n );
        }
        return ans;
    }
    void dfs(vector<int> &nums, int pos, int cur_money, int n){
        cur_money+=nums[pos];
        if(pos+2>=n){    // 判断当前状态是否是叶子结点
            ans=max(ans, cur_money);
            return;
        }
        for(int i=pos+2;i<n;++i){  // 枚举可能的下一状态
            dfs(nums, i, cur_money, n);
        }
    }
private:
    int  ans=0;
};
// 思路2
class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        if(n==0) return 0;
        if(n==1) return nums[0];
        vector<int> dp(n, 0);
        dp[0]=nums[0];   // dp[i] 以偷第i个屋子结尾的最大值
        dp[1]=nums[1];
        int cur_max=nums[0];  // 记录[0, i-2]前缀最大值
        for(int i=2;i<n;++i){
            dp[i]=nums[i]+cur_max;
            cur_max=max(cur_max, dp[i-1]);
        }
        int ans=0;
        ans=max(dp[n-1], dp[n-2]);
        return ans;
        
    }
};
// 思路3
class Solution {
public:
    int rob(vector<int>& nums) {
        int n = nums.size();
        if(n==0) return 0;
        if(n==1) return nums[0];
        vector<int> dp(n, 0);
        dp[0]=nums[0]; 
        dp[1]=max(dp[0], nums[1]);
        for(int i=2;i<n;++i){
            dp[i]=max(dp[i-1], dp[i-2]+nums[i]);
        }
        return dp[n-1];
    }
};

59 岛屿数量

考查: DFS
难度: easy
思路1: 每次进入的都是正确且合法状态
思路2: 不管合不合法,进入下一个可能状态。 在执行具体操作前,先判断合不合法。

60 反转链表

考查: 链表 头插法
难度: easy

61 课程表

题意: 有向图判环  
考查: 拓扑排序
思路1: BFS版本  (推荐)
思路2: DFS版本 (推荐)
难度: medium
// BFS版本
class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        int n = numCourses;
        if(n<=1) return true;
        vector<int> inDegree(n, 0);
        vector<vector<int> >G(n+1, vector<int>(n+1, 0));
        for(int i=0;i<prerequisites.size();++i){
            int cur=prerequisites[i][0];
            int prev=prerequisites[i][1];
            G[prev][cur]=1;
            inDegree[cur]++;
        }
        queue<int> q; // 只保存入度为0的顶点
        for(int i=0;i<n;++i){
            if(inDegree[i]==0){
                q.push(i);
            }
        }
        int cnt=0;
        while(!q.empty()){
            int cur = q.front();
            q.pop();
            ++cnt;
            for(int i=0;i<n;++i){
                if(G[cur][i]==1){
                    inDegree[i]--;
                    if(inDegree[i]==0){
                        q.push(i);
                    }
                }
            }
        }
        if(cnt<n) return false;
        return true;
    }
};
// DFS版本
class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        visit.resize(numCourses, 0);
        vector<vector<int> >edges(numCourses); // 邻接表
        for(int i=0;i<prerequisites.size();++i){
            int cur = prerequisites[i][0];
            int prev=prerequisites[i][1];
            edges[prev].push_back(cur);
        }
        flag=true;
        for(int i=0;i<numCourses;++i){
            if(flag&&visit[i]==0){
                dfs(i,edges);   // 判断当前连通块有无环
            }
        }
        return flag;


    }
    void dfs(int cur, vector<vector<int> > &edges){  // 每次访问的节点一定是未访问过的
        if(!flag) return; 
        visit[cur]=1;  
        for(int i=0;i<edges[cur].size();++i){
            int next=edges[cur][i];
            if(visit[next]==0){
                dfs(next, edges);
            }else if(visit[next]==1){  // 如果正在访问节点的邻点是在栈中。 那么一定存在环
                flag=false; return;
            }
        }
        visit[cur]=2; // 已经访问完
    }

private:
    vector<int> visit;  // 0表示未访问 1表示在栈中 2表示访问完
    bool flag;
};

62 实现Trie树

考查: 字典树
思路: 要形成自己的代码风格, 记忆 (推荐)
难度: medium
struct TrieNode{
    bool isLeaf;
    TrieNode* children[26];
    TrieNode(): isLeaf(false), children{nullptr}{}
};
class Trie {
private:
    TrieNode *root;
public:
    Trie() {
        root = new TrieNode();
    }
    
    void insert(string word) {
        TrieNode* p=root;
        for(auto &ch: word){
            int i=ch-'a';
            if(p->children[i]==nullptr){
                p->children[i] = new TrieNode();
            }
            p=p->children[i];
        }
        p->isLeaf=true;
        return;
    }
    
    bool search(string word) {
        TrieNode* p=root;
        for(auto &ch:word){
            int i=ch-'a';
            if(p->children[i]==nullptr) return false;
            p=p->children[i];
        }
        return p->isLeaf;
    }
    
    bool startsWith(string prefix) {
        TrieNode* p=root;
        for(auto &ch:prefix){
            int i=ch-'a';
            if(p->children[i]==nullptr) return false;
            p=p->children[i];
        }
        return true;
    }
};

63 数组中第K个最大元素

考查: 快排 堆排
思路1: 快排   (推荐)
思路2: 堆排 自己实现一个大顶堆    (推荐)

主要操作包括:
	建堆: 静态建堆, 此时数组已经固定下来。  动态建堆, 实际上就是插入操作
	调整:  向上调整 向下调整  堆特有的操作,为了保持堆的性质
	插入: 堆尾插入新元素
	删除: 删除堆顶元素

在很多语言中,都有优先队列或者堆的的容器可以直接使用,但是在面试中,面试官更倾向于让面试者自己实现一个堆。所以建议读者掌握这里大根堆的实现方法,在这道题中尤其要搞懂「建堆」、「调整」和「删除」的过程。

友情提醒:「堆排」在很多大公司的面试中都很常见,不了解的同学建议参考《算法导论》或者大家的数据结构教材,一定要学会这个知识点哦!_

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        int n = nums.size();
        k = n-k;  // 第k大, 就是第n-k+1小
        quickSort(nums, 0, n-1,k);
        return nums[k];
    }
    void quickSort(vector<int>&nums, int ll, int rr, int k){
        if(ll>=rr) return;
        int low=ll, high=rr;
        int idx = rand()%(rr+1-ll)+ll;  // 从ll...rr中选取一个随机数作为枢轴
        swap(nums[low], nums[idx]);
        int pivot=nums[low];
        while(low<high){
            while(low<high&&nums[high]>=pivot) --high;  // 至少有一个>=, 否则震荡
            nums[low]=nums[high];
            while(low<high&&nums[low]<pivot) ++low;
            nums[high]=nums[low];
        }
        int pos=low;
        nums[pos]=pivot;
        if(pos==k) return;
        if(pos<k) quickSort(nums, pos+1, rr, k);
        if(pos>k) quickSort(nums, ll, pos-1, k);
        return;
    }
};

/*
时间复杂度: O(NlogN)
空间复杂度: O(logN)

这里采用从ll...rr中选取一个随机数, 耗时从96ms下降到64ms.
*/

64 最大正方形*

考查: 矩阵 最值
思路1: 借鉴最大矩形
运行效率低, 想想没有更好的方法。
// 思路1实现
class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        int m=matrix.size(), n=matrix[0].size();
        vector<vector<int> > heights(m+1, vector<int>(n+1, 0));
        for(int i=0;i<m;++i){
            for(int j=0;j<n;++j){
                if(i==0) heights[i][j]=matrix[i][j]-'0';
                else{
                    if(matrix[i][j]!='0'){
                        heights[i][j]=heights[i-1][j]+1;
                    }
                }
            }
        }
        int max_area=0;
        for(int i=0;i<m;++i){
            for(int mid=0;mid<n;++mid){
                int cur_height=heights[i][mid];
                if(cur_height==0) continue;
                int left=mid, right=mid;
                while(left-1>=0&&heights[i][left-1]>=cur_height) left=left-1;
                while(right+1<=n-1&&heights[i][right+1]>=cur_height) right=right+1;
                int width = right+1-left;
                if(width>=cur_height){
                    int cur_area=cur_height*cur_height;
                    max_area=max(max_area, cur_area);
                }

            }
        }
        return max_area;
    }
};

65 翻转二叉树

考查: 树  树的遍历
思路1: 先序,后序都可以
难度: easy
扩展: 非递归怎么写
// 思路1实现
class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root==nullptr) return nullptr;
        swap(root->left,root->right);
        invertTree(root->left);
        invertTree(root->right);
        return root;
    }
};

66 回文链表

考查: 链表 链表分割
思路1: 用一个数组存储链表中的值, 之后双指针判断是否是回文串。  

此题可以练习 递归
// 思路1
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        vector<int> res;
        ListNode *p=head;
        while(p){
            res.push_back(p->val);
            p=p->next;
        }
        int i=0,j=res.size()-1;
        while(i<j){
            if(res[i]!=res[j]) return false;
            ++i;
            --j;
        }
        return true;
    }
};

67 二叉树的最近公共祖先

考查: 树   树的遍历  递归
思路1:比较low的思路, 先找到root--->p的路径,再找到root--->q的路径。之后找两个路径中的相交点。
思路2: 用哈希表存储每个节点的父节点。
思路3: 递归   (推荐)
// 思路2实现
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(!root||!p||!q) return nullptr;
        dfs(root);
        hash[root]=nullptr;
        if(!hash.count(p)||!hash.count(q)) return nullptr;
        TreeNode* cur=p;
        while(cur){
            visit[cur]=true;
            cur=hash[cur];
        }
        cur=q;
        while(cur){
            if(visit.count(cur)) return cur;
            cur=hash[cur];
        }
        return nullptr;
        
    }
    void dfs(TreeNode* root){
        if(root==nullptr) return;
        if(root->left){
            hash[root->left]=root;
            dfs(root->left);
        }
        if(root->right){
            hash[root->right]=root;
            dfs(root->right);
        }
    }
private:
    unordered_map<TreeNode*, TreeNode*> hash;
    unordered_map<TreeNode*, bool> visit;
};

68 除自身以外数组的乘积

考查: 前缀积  左右扫描
思路1: 左右扫描
// 思路1实现
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n=nums.size();
        vector<int> left(n, 1);
        vector<int> right(n,1);
        for(int i=1;i<n;++i){
            left[i]=left[i-1]*nums[i-1];
        }
        for(int i=n-2;i>=0;--i){
            right[i]=right[i+1]*nums[i+1];
        }
        vector<int> ans;
        for(int i=0;i<n;++i){
            ans.push_back(left[i]*right[i]);
        }
        return ans;
    }
};

69滑动窗口最大值

考查: 滑动窗口
思路1: 使用单调递减队列实现。 注意自己的风格  (推荐)

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n=nums.size();
        q.resize(n+1);
        vector<int> ans;
        for(int i=0;i<n;++i){
            if(hh<=tt&&i+1-q[hh]>k) ++hh;
            while(hh<=tt&&nums[q[tt]]<=nums[i]) --tt;
            q[++tt]=i;
            if(i+1>=k) ans.push_back(nums[q[hh]]);
        }
        return ans;
    }
private:
    vector<int> q;
    int hh=0, tt=-1;
};

2022/7/24


70 搜索二维矩阵II

考查: 二分查找
思路1: 按行进行二分查找
思路2: Z字型查找  (推荐)    
由于至多走N+M步, 所以时间复杂度在O(M+N)
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m=matrix.size();
        for(int i=0;i<m;++i){
            int pos = binarySearch(matrix[i], 0, matrix[i].size()-1, target);
            if(pos!=-1) return true;
        }
        return false;
    }
    int binarySearch(vector<int> &nums, int left, int right, int x){
        if(left>right) return -1;
        while(left<=right){
            int mid = (left+right)/2;
            if(nums[mid]==x){
                return mid;
            }else if(nums[mid]>x){
                right=mid-1;
            }else if(nums[mid]<x){
                left=mid+1;
            }
        }
        return -1;
    }   
};
// 思路2实现
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m=matrix.size(), n=matrix[0].size();
        int x=m-1, y=0;
        while(x>=0&&y<=n-1){   // Z字型搜索, 每次排除一行或者一列
            if(matrix[x][y]==target) return true;
            else if(matrix[x][y]>target){
                --x;
            }else if(matrix[x][y]<target){
                ++y;
            }
        }
        return false; 
    }
};

71 会议室 II

需要会员
// 思路1   
/*
假设当前不重叠的区间集合为S, 遍历整个数组, 向这个S中加入区间,要去这个区间和这个集合不重叠。
计数器加1. 一轮迭代之后,如果当前数组中还有未处理的区间,那么重复上述步骤,否则程序结束。 

*/
class Solution {
public:
	static bool cmp(const vector<int> &a, const vector<int> &b){
		if(a[1]!=b[1]) return a[1]<b[1];
		else return a[0]<b[0];
	}
    int minMeetingRooms(vector<vector<int>>& intervals) {
    	sort(intervals.begin(), intervals.end(), cmp);
    	int n=intervals.size();
    	vector<bool> visit(n+10, false);
    	int cnt=0;
    	for(int i=0;i<n;++i){
    		if(!visit[i]){
    			++cnt;
    			int curId=i;
    			visit[curId]=true;
    			for(int j=i+1;j<n;++j){
    				if(intervals[curId][0]<=intervals[j][1]&&intervals[j][0]<=intervals[curId][1]) continue;
    				curId=j;
    				visit[curId]=true;
				}
			}
		}
		return cnt;
    }
}; 

72 完全平方数

没有思路
// 超时版
class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n+10, 0);
        dp[1]=1;dp[2]=2;dp[3]=3; dp[4]=1;
        for(int i=1;i*i<=n;++i){
            dp[i*i]=1;
        }
        for(int i=5;i<=n;++i){
            if(dp[i]==1) continue;
            int _min=INT_MAX;
            for(int j=i-1;j>=1;--j){      
                _min=min(_min, dp[j]+dp[i-j]);
                if(dp[j]==1&&i%j==0) break;  // 这句是猜测后加上的。
            }
            dp[i]=_min;
        }
        return dp[n];
    }
};

/*
输入8831, 执行用时是388ms
*/

执行用时

class Solution {
public:
    int numSquares(int n) {
        vector<int> dp(n+10, 0);
        dp[1]=1;
        for(int i=2;i<=n;++i){
            int _min=INT_MAX;
            for(int j=1;j<=sqrt(i);++j){
                _min = min(_min, dp[i-j*j]+1);
            }
            dp[i]=_min;
        }
        return dp[n];
    }
};
/*
输入8831, 执行用时4ms
*/

执行用时

73 移动零

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int numPos=0;  // 将非零数放好的下一个位置
        int n=nums.size();
        for(int i=0;i<n;++i){
            if(nums[i]) nums[numPos++]=nums[i];
        }
        for(int i=numPos;i<n;++i){
            nums[i]=0;
        }
        return;
    }
};

2022/7/25


74 寻找重复数

没有思路
想到之前做过的多数元素, 只出现一次的数字。 对这道题没啥用。
又想到通用解法使用哈希表存储每个值出现的次数, 但是本题要求空间复杂度是O(1)
看了题解,发现技巧性极强。  之前居然做过类似思想的题。 如swap(0, *)。
就是将数组看出一个有向图。

如果一个数组不重复并且在下标范围内,那么这个有向图由自环,环组成。
如果一个数组重复并且在下标范围内,那么这个有向图可以由自环, 环, 含环的连通块(至少是弱连通块,
可以是单向连通块,强连通块)。  
如果一个数组数字超出下标范围, 那么这个有向图可以由自环, 环, 含环的连通块, 不含环的连通块组成。

// trick题
class Solution {
public:
    int findDuplicate(vector<int>& nums) {
//  1...n   
        int n=nums.size();
        int slow=0, fast=0;
        while(slow==0||slow!=fast){
            slow=nums[slow];
            fast=nums[nums[fast]];
        }
        slow=0;
        while(slow!=fast){
            slow=nums[slow];
            fast=nums[fast];
        }
        return slow;
    }
};

75 二叉树的序列化与反序列化

考查: 树 树的遍历 字符串
思路1: 之前实现过一次
//  自己实现的版本
class Codec {
public:

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        string ans="";
        if(root==nullptr) return ans;
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            int _size=q.size();
            for(int i=0;i<_size;++i){
                TreeNode* cur=q.front();
                q.pop();
                if(cur!=root) ans+=",";
                if(cur==nullptr){
                    ans+="#";
                }else{
                    ans+=to_string(cur->val);
                    q.push(cur->left);
                    q.push(cur->right);
                }
            }
        }
        int pos=ans.size()-1;
        while(pos>=0&&(ans[pos]==','||ans[pos]=='#')) --pos;
        ans = ans.substr(0, pos+1);
        return ans;
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        if(data=="") return nullptr;
        string str;
        int lchild=0;
        queue<TreeNode*> q;
        TreeNode *root, *cur, *fa;
        bool first=true;
        for(auto &ch: data){
            if(ch==','){
                if(!first) fa = q.front();
                cur=nullptr;
                if(str!="#"){
                    int num = 1;
                    cur=new TreeNode(stoi(str));
                    q.push(cur);
                    if(first){
                        root=cur;
                    }
                }
                str="";
                if(first){
                    first=false; continue;
                }
                if(lchild==0){
                    fa->left=cur;
                    ++lchild;
                }else{
                    fa->right=cur;
                    lchild=0;
                    q.pop();
                }
                continue;
            }
            str+=ch;
        }
        cur = new TreeNode(stoi(str));
        if(first) return cur;
        fa  = q.front();
        if(lchild==0){
            fa->left=cur;
        }else{
            fa->right=cur;
        }
        return root;
    }
};

76 最长递增子序列 (需要优化)

无思路 之前做过一道和子序列相关的题, 求s中能覆盖t的子串
思路1: dp  无优化  没想到过了。。。。。
思路2: 重新定了一个dp状态, 加上二分查找   (推荐)
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int  n= nums.size();
        if(n<=1)  return n;
        vector<int> dp(n+10, 0);
        dp[0]=1;
        int ans=1;
        for(int i=1;i<n;++i){
            int _max=0;
            for(int j=0;j<i;++j){
                if(nums[j]<nums[i]&&dp[j]>_max){
                    _max=dp[j];
                }
            }
            dp[i]=_max+1;
            ans=max(ans, dp[i]);
        }
        return ans;
    }
};
class Solution {
public:
    int lengthOfLIS(vector<int>& nums) {
        int n=nums.size();
        if(n<=1) return n;
        vector<int> dp(n+1, 0);
        dp[1]=nums[0];
        int len=1;
        for(int i=1;i<n;++i){
            if(nums[i]>dp[len]){
                dp[++len]=nums[i];
            }else{
                int left=1, right=len+1;  // 找到大于或者等于nums[i]的第一个元素位置   
                while(left<right){
                    int mid=(left+right)/2;
                    if(dp[mid]>nums[i]){
                        right=mid;
                    }else if(dp[mid]<nums[i]){
                        left=mid+1;
                    }else if(dp[mid]==nums[i]){
                        right=mid;
                    }
                }
                if(dp[left]>nums[i]){  // 如[0,1]  0这种就不需要更新
                    dp[left]=nums[i];
                }

            }
        }
        return len;
    }
};

77 删除无效括号

无思路

思路1: 看到数据规模n<=25, 直接DFS暴力搜索   超时!!!
// DFS
class Solution {
public:
    vector<string> removeInvalidParentheses(string s) {
        int n=s.size();
        if(n==0) return ans;
        string cur;
        dfs(s,cur, 0, n );
        return ans;
    }
    void dfs(string &s, string cur, int pos, int &n){
        if(pos==n){
            if(isValid(cur)){
                if(ans.empty()){
                    ans.push_back(cur);
                }else if(cur.size()>ans[0].size()){
                    ans.clear();
                    ans.push_back(cur);
                }else if(cur.size()==ans[0].size()){
                    for(int i=0;i<ans.size();++i){
                        if(ans[i]==cur) return;
                    }
                    ans.push_back(cur);
                }
            }
            return;
        }
        if(s[pos]<='z'&&s[pos]>='a'){
            dfs(s, cur+s[pos], pos+1, n);
        }else{
            dfs(s,cur, pos+1, n);
            dfs(s, cur+s[pos], pos+1, n);
        }
        
    }
    bool isValid(string str){
        stack<char> stk;
        for(int i=0;i<str.size();++i){
            if(str[i]=='('){
                stk.push('(');
            }else if(str[i]==')'){
                while(!stk.empty()&&stk.top()!='('&&stk.top()!=')') stk.pop();
                if(!stk.empty()&&stk.top()=='('){
                    stk.pop();
                }else{
                    return false;
                }
            }
        }
        return stk.empty();
    }
private: 
    vector<string> ans;
};
// 写于2022/7/28
class Solution {
public:
    vector<string> removeInvalidParentheses(string s) {
        int len=s.size();
        int lremove=0,rremove=0;
        for(int i=0;i<len;++i){
            if(s[i]=='('){
                ++lremove;
            }else if(s[i]==')'){
                if(lremove!=0) --lremove;
                else ++rremove;
            }
        }
        dfs(s, lremove, rremove, 0);
        return ans;

    };
    void dfs(string s, int lremove, int rremove, int start){
        if(lremove+rremove==0){
            if(isValid(s)&&!dict.count(s)){
                dict[s]=1;
                ans.push_back(s);
            }
            return ;
        }
        if(lremove){
            for(int i=start;i<s.size();++i){   // 这里i将0改为start  优化1
                if(i!=0&&s[i]==s[i-1]) continue;  // 优化2   防止这种 (((((() 
                if(s[i]=='('){
                    string next = s.substr(0, i)+s.substr(i+1);
                    dfs(next, lremove-1, rremove, i);
                }
            }
        }
        if(rremove){
            for(int i=start;i<s.size();++i){
                if(i!=0&&s[i]==s[i-1]) continue;
                if(s[i]==')'){
                    string next = s.substr(0, i)+s.substr(i+1);
                    dfs(next, lremove, rremove-1, i);
                }
            }
        }
    }
    bool isValid(string s){
        int cnt=0;
        for(int i=0;i<s.size();++i){
            if(s[i]=='(') ++cnt;
            else if(s[i]==')'){
                --cnt;
                if(cnt<0) return false;
            }
        }
        return cnt==0;
    }
private:
    unordered_map<string, int> dict;
    vector<string> ans;
};

78 最佳买卖股票时机含冷冻期

class Solution {
public:
    int maxProfit(vector<int>& prices) {
       //  dp[i] days from 0 to i to get maximum profit
       //  dp[i] = max{ dp[i-1], max[0<=j<i-1&&prices[j]<prices[i]] {dp[j-2]+prices[i]-prices[j]} }
       int  n =prices.size();
       if(n<=1) return 0;
       vector<int> dp(n+10, 0);
       for(int i=1;i<n;++i){
          int _max=0;
          for(int j=0;j<i;++j){
              if(prices[j]<prices[i]){
                  int cur = prices[i]-prices[j]+ (j-2>=0?dp[j-2]:0);
                  if(cur>_max) _max=cur;
              }
          }
          dp[i]=max(dp[i-1],_max);
       }
       return dp[n-1];
    }
};


2022/7/26


79 戳气球

没有思路

80 零钱兑换

题意: 背包问题
思路1: dp  自己实现
// 思路1 
class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        vector<int> dp(amount+1, -1);  // dp[i]表示凑成i元所需的最少硬币数
        dp[0]=0;  
        int n=coins.size();   // dp[i] =  min [0<=j<i] dp[i-coins[j]]+1
        for(int i=1;i<=amount;++i){
            int _min=INT_MAX;
            for(int j=0;j<n;++j){
                if(i-coins[j]>=0&&dp[i-coins[j]]!=-1){
                    int cur = dp[i-coins[j]]+1;
                    _min=min(_min, cur);
                }
            }
            if(_min!=INT_MAX) dp[i]=_min;
        }
        return dp[amount];
    }
};

81 打家劫舍III

一开始觉得自己有思路, dp[cur]表示从根到当前结点抢到的最高金额, 
dp[cur] = max(cur->val+dp[cur's grandfather], dp[cur' father])。 但是提交答案后,发现自己计算的实际上是从
根到叶子结点路径上可以获得去的最高金额。 但实际上整个一棵树都可以偷窃, 而不是仅仅其中一条路径。
如果对叶子结点累加,会重复计算。

82 比特位计数

思路1: 右移操作,  居然过了
在c++中 ~取反操作对包含符号位在内的32个位数都取反。 这点和书上不一致。
假设a=128。 取反之后是-129。 如果要获取a的相反数, ~a+1
class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> ans;
        for(int i=0;i<=n;++i){
            int cnt=0;
            int vv=i;
            for(int j=0;j<=31;++j){
                if(vv&1){
                    ++cnt;
                }
                vv=vv>>1;
            }
            ans.push_back(cnt);
        }
        return ans;
    }
};

83 前K个高频元素

struct cmp{
    bool operator()(const pair<int,int> &a, const pair<int,int> &b) const{
        if(a.second!=b.second) return a.second>b.second;   // 这3个const必须要加, 并且这里两个判断句都要写, 否则set会漏元素
        return a.first<b.first;
    }
};
class Solution {
private:
    unordered_map<int, int> cnt;
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        set<pair<int, int>, cmp> st;
        for(auto &num: nums){
            cnt[num]++;
        }

        for(auto &num:nums){
             st.insert(make_pair(num, cnt[num]));
        }
        vector<int> ans;
        int cur=0;
        for(auto &p: st){
            ++cur;
            if(cur>k) break;
            ans.push_back(p.first);
        }
        return ans;
    }
};

2022/7/28 写了14道题


84 字符串解码

// 栈
class Solution {
public:
    string decodeString(string s) {
        string ans;
        stack<char> stk;
        for(int i=0;i<s.size();++i){
            if(stk.empty()){
                if(s[i]>='a'&&s[i]<='z'){
                    ans+=s[i];
                    continue;
                }
                stk.push(s[i]);
            }else{
                if(s[i]==']'){
                    string tmp;
                    while(!stk.empty()&&stk.top()>='a'&&stk.top()<='z'){
                        tmp+=stk.top();
                        stk.pop();
                    }
                    if(!stk.empty()&&stk.top()=='[') stk.pop();
                    string num;
                    while(!stk.empty()&&stk.top()>='0'&&stk.top()<='9'){
                        num+=stk.top();
                        stk.pop();
                    }
                    reverse(tmp.begin(), tmp.end());
                    reverse(num.begin(), num.end());
                    int _num=stoi(num);
                    string tmpStr;
                    while(_num--){
                        tmpStr+=tmp;
                    }
                    if(stk.empty()){
                        ans+=tmpStr;
                    }else{
                        for(int j=0;j<tmpStr.size();++j){
                            stk.push(tmpStr[j]);
                        }
                    }

                }else{
                    stk.push(s[i]);
                }
            }
        }
        return ans;
    }
};

85 除法求值

class Solution {
private:
    unordered_map<string, string> father;
public:
    string findfather(string x){
        string a=x;
        while(father[x]!=x) x=father[x];
        while(a!=father[a]){
            string z=a;
            a=father[a];
            father[z]=x;
        }
        return x;
    }
    void _union(string a, string b){
        string fa=findfather(a);
        string fb=findfather(b);
        if(fa!=fb){
            father[fa]=fb;
        }
    }
    vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
        for(int i=0;i<equations.size();++i){
            vector<string> cur=equations[i];
            father[cur[0]]=cur[0];
            father[cur[1]]=cur[1];
        }
        for(int i=0;i<equations.size();++i){
            vector<string> cur = equations[i];
            _union(cur[0], cur[1]);
        }
        unordered_map<string, double> dict;
        bool flag=false;
        for(int i=0;i<equations.size();++i){  // 多次迭代
            flag=false;
            for(int j=0;j<equations.size();++j){
                vector<string> cur = equations[j];
                if(!dict.count(cur[0])){
                    if(dict.count(cur[1])){
                        dict[cur[0]]=values[j]*dict[cur[1]];
                        flag=true;
                    }
                }else{
                    if(!dict.count(cur[1])){
                        dict[cur[1]]=dict[cur[0]]/values[j];
                        flag=true;
                    }
                }
            }
            if(!flag){  // 没有发生更新, 至多更新一个
                bool isEnd=true;
                for(int j=0;j<equations.size();++j){
                    vector<string> cur = equations[j];
                    if(!dict.count(cur[0])&&!dict.count(cur[1])){
                        isEnd=false;
                        dict[cur[1]]=1.0;
                        dict[cur[0]]=values[j];
                        break;
                    }
                }
                if(isEnd) break;
            }


        }
        vector<double> ans;
        for(int i=0;i<queries.size();++i){
            string a=queries[i][0];
            string b=queries[i][1];
            if(!father.count(a)||!father.count(b)){
                ans.push_back(-1);
                continue;
            }
            if(findfather(a)!=findfather(b)){    //  如果说两个数没有比例关系, 那么使用并查集
                ans.push_back(-1);
                continue;
            }
            ans.push_back(dict[a]/dict[b]);
        }
        return ans;
    }
};

86 根据身高重建队列

智力题
class Solution {
public:
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        vector<vector<int> > ans;
        int n=people.size();
        if(n==0) return ans;
        sort(people.begin(), people.end(), cmp);
        vector<int> q(n+1, 0);
        int hh=0, tt=-1;
        for(int i=0;i<people.size();++i){  // 找到这个元素在当前队列的相对位置
            vector<int> cur=people[i];  // cur[1]有范围,  0<=cur[1]<=(tt-hh+1)
            if((tt-hh+1)==cur[1]){
                q[++tt]=i;
            }else{
                int remove = tt-hh+1-cur[1]; // 需要搬动的元素个数
                int j=tt;
                while(remove--){
                    q[j+1]=q[j];
                    --j;
                }
               // q[j]=i;
                q[j+1]=i;
                ++tt;
            }
        }
        
        for(int i=hh;i<=tt;++i){
            ans.push_back(people[q[i]]);
        }
        return ans;

    }
    static bool cmp(vector<int> &a, vector<int> &b){
        if(a[0]!=b[0]) return a[0]>b[0];
        else return a[1]<b[1];   // 不可能存在两对序偶完全一样
    }
};

87 分割等和子集

88 路径总和 III

89找到字符串中所有字母异位词

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        int len=p.size();
        int pFreq[26]={0};  // 这里必须初始化, 否则会初始化任意数字
        int sFreq[26]={0};
        int distance=0;
        vector<int> ans;
        queue<int> q;
        for(auto &ch: p){
            int i=ch-'a';
            pFreq[i]++;
        }
        for(int i=0;i<s.size();++i){
            q.push(i);
            int pos=s[i]-'a';      
            if(sFreq[pos]<pFreq[pos]){
                ++distance;
            }
            sFreq[pos]++;
            while(distance==len){
                int _sz = q.size();
                int head = q.front();
                q.pop();
                if(_sz==len){
                    ans.push_back(head);
                }
                pos=s[head]-'a';
                sFreq[pos]--;
                if(sFreq[pos]<pFreq[pos]){
                    --distance;
                }
            }
        }
        return ans;
    }
};

90 找到所有数组中消失的数字

class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) {
        // 将数组想象成有向图
        vector<int> ans;
        int n=nums.size();
        if(n<=1) return ans;
        for(int i=0;i<n;++i){
            int j=i;
            if(nums[j]==0) continue;
            j=nums[j]-1;
            while(nums[j]!=0){
                int tmp=nums[j]-1;
                nums[j]=0;
                j=tmp;
            }
        }
        for(int i=0;i<n;++i){
            if(nums[i]!=0){
                ans.push_back(i+1);
            }
        }
        return ans;
    }
};

91 汉明距离

class Solution {
public:
    int hammingDistance(int x, int y) {
        // 位运算
        int distance=0;
        for(int i=0;i<32;++i){
            if((x&1)!=(y&1)){
                distance++;
            }
            x>>=1;
            y>>=1;
        }
        return distance;
    }
};

92 目标和

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum=0, pos=0;
        int n=nums.size();
        dfs(nums, sum, target, 0, n);
        return ans;
    }
    void dfs(vector<int> &nums, int sum, int target, int pos, int &n){
        if(pos==n){
            if(sum==target) ++ans;
            return;
        }
        dfs(nums, sum+nums[pos], target, pos+1, n);
        dfs(nums, sum-nums[pos], target, pos+1, n);

    }
private:
    int ans=0;
};

93 把二叉搜索树转换为累加树

class Solution {
public:
    TreeNode* convertBST(TreeNode* root) {
        // 利用BST树中序遍历有序这一性质
        if(!root) return nullptr;
        inOrder(root);
        int sum=0;
        for(int i=nums.size()-1;i>=0;--i){
            TreeNode* cur = nums[i];
            sum+=cur->val;
            cur->val=sum;
        }
        return root;
    }
    void inOrder(TreeNode* cur){
        if(!cur) return;
        inOrder(cur->left);
        nums.push_back(cur);
        inOrder(cur->right);
    }
private:
    vector<TreeNode*> nums;
};

94 二叉树的直径

class Solution {
public:
    int diameterOfBinaryTree(TreeNode* root) {
        if(!root) return 0;
        postOrder(root);
        return ans;
    }
    int postOrder(TreeNode* cur){
        if(!cur) return 0;
        int left = postOrder(cur->left);
        int right=postOrder(cur->right);

        ans=max(left+right, ans);   // 求出穿过当前结点的最大长度, 并和之前保存的最大值比较
        return max(left, right)+1;


    }
private: 
    int ans=INT_MIN;
};

95 和为 K 的子数组

96 最短无序连续子数组

class Solution {
public:
    int findUnsortedSubarray(vector<int>& nums) {
        int n=nums.size();
        vector<int> nums1=nums;
        sort(nums.begin(), nums.end());
        int left=0;
        while(left<n&&nums[left]==nums1[left]) ++left;
        if(left==n) return 0;
        int right=n-1;
        while(right>=0&&nums[right]==nums1[right]) --right;
        return right-left+1;
    }
};

97 合并二叉树

class Solution {
public:
    TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
        if(!root1&&!root2) return nullptr;
        if(!root1) return root2;
        if(!root2) return root1;
        root1->val=root1->val + root2->val;
        root1->left=mergeTrees(root1->left, root2->left);
        root1->right=mergeTrees(root1->right, root2->right);
        return root1;

    }
};

98 任务调度器

99 回文子串

class Solution {
public:
    int countSubstrings(string s) {
        int len=s.size();
        vector<vector<bool> > dp(len+1, vector<bool> (len+1, false));
        // dp[i][j] = dp[i+1][j-1]&&s[i]==s[j]
        for(int curLen=0;curLen<=len;++curLen){  
            for(int j=0;j<len;++j){
                int i=j+1-curLen;
                if(i>j||i<0) continue;
                if(i+1>j-1){
                    dp[i][j]=(s[i]==s[j]);
                }else{
                    dp[i][j]=(dp[i+1][j-1])&&(s[i]==s[j]);
                }

            }
        }
        int cnt=0;
        for(int i=0;i<len;++i){
            for(int j=0;j<len;++j){
                if(i>j) continue;
                if(dp[i][j]){
                   ++cnt;
                }
            }
        }
        return cnt;
    }
};

100 每日温度

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n=temperatures.size();
        vector<int> ans(n, 0);
        vector<int> stk(n+10, 0);
        int hh=0, tt=-1;
        for(int i=0;i<n;++i){
            while(hh<=tt&&temperatures[stk[tt]]<temperatures[i]){
                ans[stk[tt]]=i-stk[tt];
                --tt;
            }
            stk[++tt]=i;
        }
        return ans;
    }
};
没写出来的题目:
312. 戳气球
313. 打家劫舍 III

2022/7/28
315. 分割等和子集 
316. 路径总和 III 
317. 和为 K 的子数组
318. 任务调度器


2022/7/29 做了五道题

	313. 打家劫舍 III  用到之前做过的题的思想
	315. 分割等和子集    经典问题
	316. 路径总和 III   写的不够优雅
	317. 和为 K 的子数组  技巧性很强  这道题看了题解!!
	318. 任务调度器  这道题做了一下午, 一直在找bug。 map删除有陷阱

//  313. 打家劫舍 III
class Solution {
public:
    int rob(TreeNode* root) {
        pair<int,int> cur=dfs(root);
        return max(cur.first, cur.second);
    }
    pair<int, int> dfs(TreeNode* cur){
        if(!cur) return make_pair(0, 0);
        pair<int, int> left = dfs(cur->left);
        pair<int, int>right = dfs(cur->right);
        int walk = cur->val + left.second+right.second;
        int notwalk = max(left.first, left.second)+max(right.first, right.second);
        return make_pair(walk, notwalk);

    }
};

//315. 分割等和子集
class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int n=nums.size();
        int sum=0;
        for(const auto&x :nums){
            sum+=x;
        }
        if(sum&1) return false;
        int target=sum/2;
        vector<vector<bool> > dp(n+10, vector<bool>(target, false)); 
        //  dp[i][j] = dp[i-1][j] || dp[i-1][j-nums[i]]  
        for(int i=0;i<=n;++i){
            for(int j=0;j<=target;++j){
                if(j==0) dp[i][j]=true;
                else{
                    if(i==0) dp[i][j]=false;
                    else{
                        if(j-nums[i-1]<0) dp[i][j]=dp[i-1][j];
                        else{
                            dp[i][j]=(dp[i-1][j])||(dp[i-1][j-nums[i-1]]);
                        }
                    }
                }
            }
        }
        return dp[n][target];

        
    }
};
//316. 路径总和 III
class Solution {
public:
    int pathSum(TreeNode* root, int targetSum) {
        if(!root) return 0;
        dfs(root, targetSum);
        return ans;
    }
    void dfs(TreeNode* cur, int targetSum){
        if(!cur) return;
        dfs(cur->left, targetSum);
        dfs(cur->right, targetSum);
        ll cur_sum=0;
        preOrder(cur, cur_sum, targetSum);
    }
    void preOrder(TreeNode* cur, ll cur_sum, int targetSum){
        if(!cur) return;
        cur_sum=cur_sum+cur->val;
        if(cur_sum==targetSum){
            ++ans;
        }
        preOrder(cur->left, cur_sum, targetSum);
        preOrder(cur->right, cur_sum, targetSum);
    }
private:
    int ans=0;

};

//317. 和为 K 的子数组
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int cnt=0;
        unordered_map<int, int> dict;
        dict[0]=1;   // 空前缀
        int cur_sum=0;
        for(int i=0;i<nums.size();++i){   // prefix[i]-k=prefix[j]
            cur_sum+=nums[i];
            int x=cur_sum-k;
            if(dict.count(x)){  // 先问后存
                cnt+=dict[x];
            }
            dict[cur_sum]++;
        }
        return cnt;

    }
};
//318. 任务调度器
class Solution {
public:
    struct cmp {
        bool operator()(const pair<char, int> &a, const pair<char, int> &b) const{
            return a.second<b.second;
        }
    };
    int leastInterval(vector<char>& tasks, int n) {
        priority_queue<pair<char, int>, vector<pair<char, int> >, cmp>pq;
        map<char, int> dict;  // dict存放 每个任务需要完成的次数
        for(int i=0;i<tasks.size();++i){
            char ch = tasks[i];
            dict[ch]++;
        }
        for(map<char,int>::iterator it=dict.begin();it!=dict.end();++it){
            char ch = it->first;
            int cnt = it->second;
            pq.push(make_pair(ch, cnt));
        }
        map<char, int> cold; //cold里存放没有完成并且冻结的任务
        int cur_time=0;
        int cur_num=0;
        int _sz = tasks.size();
        while(true){
            if(!pq.empty()){  // 当前有无可执行的任务
                pair<char, int> cur = pq.top();
                pq.pop();
                dict[cur.first]-=1;
                cold[cur.first]=n+1;  // 这里赋值n+1是为了统一减去。 初始状态->状态1->状态2->.....  状态1看到n, 状态2看到n-1.
                ++cur_num;      // 第一次状态变化, 如果是n,不应该减1
            }
            ++cur_time;
            if(cur_num==_sz) break;
            for(map<char, int>::iterator it=cold.begin();it!=cold.end();){  // 更新cold和队列
                char task = it->first;  
                cold[task]-=1;
                if(dict[task]==0) cold.erase(it++);
                else{                   
                    if(cold[task]==0){
                        pq.push(make_pair(task, dict[task]));
                        cold.erase(it++);
                    }
                    else{
                        ++it;                      
                    }
                }
            }
        }
        return cur_time;
    }
};


2022/7/30

戳气球

// 戳气球
class Solution {
public:
    int maxCoins(vector<int>& nums) {
        vector<int> nums1;
        nums1.push_back(1);
        for(int i=0;i<nums.size();++i){
            nums1.push_back(nums[i]);
        }
        nums1.push_back(1);
        int n=nums1.size();
        vector<vector<int> >dp(n+10, vector<int> (n+10, 0));
        for(int len=0;len<=n; ++len){
            for(int j=0;j<n;++j){
                int i=j+1-len;
                if(i>=0&&i+1<j){
                    int _max=INT_MIN;
                    for(int k=i+1; k<j;++k){
                        int cur = dp[i][k]+dp[k][j]+nums1[k]*nums1[i]*nums1[j];
                        _max = max(_max, cur);
                    }
                    if(_max!=INT_MIN) dp[i][j]=_max;
                }
            }
        }
        return dp[0][n-1];
       
    }
};
终于结束了。
从2022/7/2 -   2022/7/30。做完了hot 100题。
一刷结束。
前50道题从2022/7/2 - 2022/7/21  用了20天
后50道题从2022/7/22 - 2022/7/30  用了9天

优点:做完了HOT100题。多数题目自己解决的。 有些总结了模版。
从别人的新颖的解决方法和简约有效的代码风格中,给人启发。

缺点:
	 首先一刷, 掌握的不很牢靠。 
	同类型的题目没有归类,进一步扩展,进一步深入思考, 进一步练习。
	线段树, 记忆化搜索, 简洁的递归写法要去多试试。
	有些题目,没有尝试多种方法,或者推荐解法。
	最后,戒骄戒躁。 像ACMer, 可能都做了1000-2000道左右的题,甚至更多。你题量和见过的题,敲代码
的熟练水平,思路, 编码知识体系肯定不如的。 所以戒骄戒躁, 才100题量,你能玩出什么花来呢?但是
不要妄自菲薄,觉得自己不行, 保持自信。找工作而已,生活和工作是两件事。
	
建议:
	多刷几遍,提高时间和准确度, 重点在熟练, 知识点就那么多
	要尝试多种解法,最佳解法  看到题要立马想到推荐解法,因为面试可能只有10多分钟时间,否则可能回家等通知。
提高自己思路,代码实践能力。
	自己尝试口头表达这个题解法思路。 可以看看求s中能覆盖t的最短子串。 官方题解小哥哥表达的言简意赅。
	对于重点,难点,常考点, 要集中总结,思考,练习, 突破。 比如二分查找细节, dp类型, 链表的归并排序。 思考也有深度,自己出题。
	要尝试新题,扩展思路和视野
	场景设计题,要多练练, 是一类题型


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值