leetcode

141 环形链表

class Solution {
public:
    bool hasCycle(ListNode *head) {
        auto fast = head;
        auto slow = head;
        while(fast && fast->next){
            slow = slow->next;
            fast = fast->next->next;
            if(slow == fast) return true;
        }
        return false;
    }
};

142 环形链表

https://leetcode.cn/problems/linked-list-cycle-ii/?favorite=2cktkvj
有一个链表,如果有环,返回环的入口处,没有则返回
方法一:哈希表
哈希表第一个重复的值,就是入口处
方法二:快慢指针
如果有环,快慢指针将会再某一点相遇,此时的慢指针和 head 与环入口点距离相等

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        if(head == nullptr) return nullptr;
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast && fast->next){
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow){
                auto res = head;
                while(res!=slow){
                    res = res->next;
                    slow = slow->next;
                }
                return res;
            }
        }
        return nullptr;
    }
};

146 LRU 缓存

https://leetcode.cn/problems/lru-cache/

请你设计一个Last Recent Used Cache
使用一个双向指针(带有 head 和 tail 节点,同时节点中包含 key 和 value 值)以及一个 unordered_map 储存,实现两个函数,moveToHead()和 removeTail()

class LRUCache {
struct ListNode{
    int key;
    int val;
    ListNode* next;
    ListNode* prev;
    ListNode(): key(0), val(0){}
    ListNode(int key, int val):key(key), val(val){}
};
private:
    int capacity;
    unordered_map<int, ListNode*> cache;
    ListNode* head;
    ListNode* tail;
public:
    LRUCache(int capacity) {
        this->capacity = capacity;
        head = new ListNode();
        tail = new ListNode();
        head->next = tail;
        tail->next = head;
        head->prev = tail;
        tail->prev = head;
    }

    int get(int key) {
        if(cache.count(key) != 0){
            moveToHead(key);
            return cache[key]->val;
        }
        return -1;
    }

    void put(int key, int value) {
        if(cache.count(key)==0){
            addToHead(key, value);
        }else{
            cache[key]->val = value;
            moveToHead(key);
        }
        if(cache.size() > this->capacity){
            removeTail();
        }
    }
    void addToHead(int key, int value){

        auto node = new ListNode(key, value);
        cache[key] = node;

        //
        auto tp = head->next;
        head->next = node;
        node->prev = head;
        node->next = tp;
        tp->prev = node;
    }
    void moveToHead(int key){
        auto node = cache[key];
        node->prev->next = node->next;
        node->next->prev = node->prev;
        auto tp = head->next;
        //
        head->next = node;
        node->prev = head;
        node->next = tp;
        tp->prev = node;
    }
    void removeTail(){
        auto last = tail->prev;
        cache.erase(last->key);
        tail->prev = last->prev;
        last->prev->next = tail;
        delete last;
    }
};

148 排序链表

https://leetcode.cn/problems/sort-list/?favorite=2cktkvj
merge_sort 排序对链表进行排序:
1、如果 merge_sort 传入的头尾链表相距一个单位,则切断链表,返回 head
2、快慢链表找到中间值
3、合并链表
注意遍历快慢链表的方法,fast != tail,然后在循环中判断是否需要 fast 进两步

class Solution {
public:
    ListNode* merge_sort(ListNode* head, ListNode* tail){
        if(!head ){
            return head;
        }
        if(head->next == tail){
            head->next = nullptr;
            return head;
        }
        auto fast = head;
        auto slow = head;
        while(fast!=tail && fast->next!=tail){
            fast = fast->next->next;
            slow = slow->next;
        }
        return merge(merge_sort(head, slow), merge_sort(slow, tail));
    }
    ListNode* merge(ListNode* l1, ListNode*l2){
        auto dummy = new ListNode();
        auto cur = dummy;
        while(l1 && l2){
            if(l1->val < l2->val){
                cur->next = l1;
                l1 = l1->next;
            }else{
                cur->next = l2;
                l2 = l2->next;
            }
            cur = cur->next;
        }
        if(l1){
            cur->next = l1;
        }else{
            cur->next = l2;
        }
        return dummy->next;
    }
    ListNode* sortList(ListNode* head) {
        return merge_sort(head, nullptr);
    }
};

152.乘积最大子数组

https://leetcode.cn/problems/maximum-product-subarray/

动态规划,因为数组中可能有负数,所以不仅仅要维护一个当前最大动态数组,还要维护当前最小数组,防止负负得正变成最大值

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        if( nums.size()==0 ) return 0;
        vector<int> curMax(nums.size());
        vector<int> curMin(nums.size());
        int res = nums[0];
        curMax[0] = nums[0];
        curMin[0] = nums[0];
        for(int i=1;i<nums.size();i++){
            int cur = nums[i];
            curMax[i] = max(max(curMax[i-1]*cur, curMin[i-1]*cur), cur);
            curMin[i] = min(min(curMin[i-1]*cur, curMax[i-1]*cur), cur);
            res = max(res, curMax[i]);
        }
        return res;
    }
};

155 最小栈

https://leetcode.cn/problems/min-stack/

维护两个栈,除了基础的栈外,维护一个储存最小值的栈

class MinStack {
private:
    stack<int> min_st;
    stack<int> st;
public:
    MinStack() {

    }
    void push(int val) {
        st.push(val);
        if(min_st.empty() || min_st.top() >= val){
            min_st.push(val);
        }
    }
    void pop() {
        int top = st.top();
        st.pop();
        if(min_st.top() == top){
            min_st.pop();
        }
    }
    int top() {
        return st.top();
    }
    int getMin() {
        return min_st.top();
    }
};

160 相交链表

https://leetcode.cn/problems/intersection-of-two-linked-lists/

判断两个链表是否相交
便利 两个链表,在达到尾端(注意要达到 nullptr)如果遇到相等的情况,就是相交,否则他们将会在 nullptr 处相交:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if(!headA || !headB) return nullptr;
        auto tpa = headA;
        auto tpb = headB;
        while(tpa!=tpb){
          tpa = tpa? tpa->next : headB;
          tpb = tpb? tpb->next : headA;
        }
        return tpa;
    }
};

169 多数元素

https://leetcode.cn/problems/majority-element/

1、排序取中间值
2、摩尔投票算法

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int cnt = 0;
        int candidate;
        for(int num: nums){
            if(cnt==0){
                candidate = num;
                cnt++;
            }else{
                if(candidate==num){
                    cnt++;
                }else{
                    cnt--;
                }
            }
        }
        return candidate;
    }
};

198 打家劫舍

打家劫舍 i

https://leetcode.cn/problems/house-robber/

动态规划
max(f[i-1], nums[i]+f[i-2])
i-2家和i家或者偷i-1并且不偷i

class Solution {
public:
    int rob(vector<int>& nums) {
        vector<int> f(nums.size(), 0);
        if(f.size()==1) return nums[0];
        f[0] = nums[0];
        f[1] = max(nums[0], nums[1]);
        for(int i=2;i<nums.size();i++){
            f[i] = max(f[i-1], nums[i]+f[i-2]);
        }
        return f[nums.size()-1];
    }
};

优化空间之后

class  Solution {
public:
	int  rob(vector<int>&  nums) {
		int prev = 0;
		int cur = nums[0];
		for(int i=1;i<nums.size();i++)
		{
			int tp = cur;
			cur = max(cur, prev+nums[i]);
			prev = tp;
		}
		return cur;
		}
};

打家劫舍 ii

https://leetcode.cn/problems/house-robber-ii/

有环 ,如果偷了第 0 家,那么不可以偷第 n-1 家,所以 rob(nums, 0, n-2),如果偷了 n-1 家,那么不可以偷第 0 家,所以 rob(nums, 1, n-1)。

class Solution {
public:
    int rob_helper(vector<int>& nums, int start, int end) {
        int prev = 0;
        int cur = nums[start];
        for (int i = start + 1; i <= end; i++) {
            int tp = cur;
            cur = max(cur, prev + nums[i]);
            prev = tp;
        }
        return cur;
    }
    int rob(vector<int>& nums) {
        if (nums.size() == 1) return nums[0];
        int n = nums.size();
        return max(rob_helper(nums, 1, n - 1), rob_helper(nums, 0, n - 2));
    }
};

打家劫舍 iii

https://leetcode.cn/problems/house-robber-iii/

核心思想是后序遍历,现将子节点值遍历出来
建立两个哈希表,g[root]表示选择当前节点的最大值,f[root]表示不选择当前节点的最大值

同样的思想,偷到当前节点有两种情况,如果偷当前节点,那么左孩子节点和右孩子节点都不能偷,直接就是root→val+f[root→left]+f[root→right],如果不偷当前节点,那么左孩子可偷可不偷,右孩子也是可偷可不偷,所以max(g[root→left], f[root→right])+max(g[root→right], f[root→right])

class Solution {
private:
    unordered_map<TreeNode*, int> f; // choose
    unordered_map<TreeNode*, int> g; // not choose
public:
    void dfs(TreeNode* root){
        if(!root){
            return;
        }
        dfs(root->left);
        dfs(root->right);
        f[root] = root->val + g[root->left] + g[root->right];
        g[root] = max(g[root->left], f[root->left]) +  max(g[root->right], f[root->right]);
    }
    int rob(TreeNode* root) {
        dfs(root);
        return max(g[root], f[root]);
    }
};

200 岛屿数量

https://leetcode.cn/problems/number-of-islands

深度优先遍历+visit 数组

class Solution {
private:
    vector<vector<int>> directions = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
    vector<vector<bool>> visit;
    int n;
    int m;
public:
    bool is_valid(int i, int j, vector<vector<char>>& grid){
        return i<n && i>=0 && j<m && j>=0 && !visit[i][j] && grid[i][j] == '1';
    }
    void dfs(int i, int j, vector<vector<char>>& grid){
        visit[i][j] = true;
        for(auto& direction : directions){
            int nexti = i + direction[0];
            int nextj = j + direction[1];
            if(is_valid(nexti, nextj, grid)){
                dfs(nexti, nextj, grid);
            }
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        int ans = 0;
        n = grid.size();
        m = grid[0].size();
        visit = vector<vector<bool>> (n, vector<bool>(m));
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                if(!visit[i][j] && grid[i][j] == '1'){
                    ans++;
                    dfs(i, j, grid);
                }
            }
        }
        return ans;
    }
};

206 翻转链表

https://leetcode.cn/problems/reverse-linked-list

头插法:正向遍历+dummy 节点

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        auto dummy = new ListNode();
        while(head){
            auto dummy_next = dummy->next;
            auto head_next = head->next;
            dummy->next = head;
            head->next = dummy_next;
            head = head_next;
        }
        return dummy->next;
    }
};

迭代法

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* prev = nullptr;
        ListNode* cur = head;
        while(cur){
            auto next = cur->next;
            cur->next = prev;
            prev = cur;
            cur = next;
        }
        return prev;

    }
};

递归法

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head || !head->next) return nullptr;
        auto next = head->next;
        auto reversed = reverseList(head->next);
        head->next = nullptr; // 这是重点
        next->next = head;
        return reversed;
    }
};

207 课程表

https://leetcode.cn/problems/course-schedule

计算入度+广度优先算法

  1. 先遍历,记录所有节点的入度和整张图
  2. 用 stack 或者 queue push 进入度为 0 的课程(表示可以直接上掉课程),while 循环,每次 pop 出来记录可以上掉的课程,计算此课程的完成是否可以让其他课程进入入度为 0 的状态,如有则 push 进 stack 或者 queue,结果是完成课程的数量是否等于全部课程
class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        vector<int> in(numCourses, 0);
        vector<vector<int>> graph(numCourses, vector<int>(numCourses, 0));
        for(auto prerequisite: prerequisites){
            in[prerequisite[0]]++;
            graph[prerequisite[0]][prerequisite[1]] = 1;
        }
        int cnt = 0;
        stack<int> st;
        for(int i=0;i<numCourses;i++){
            if(in[i] == 0){
                st.push(i);
            }
        }
        while(!st.empty())
        {
            auto top = st.top();
            st.pop();
            cnt++;
            for(int i=0;i<numCourses;i++){
                if(graph[i][top] != 0){
                    in[i]--;
                    if(in[i] == 0){
                        st.push(i);
                    }
                }
            }
        }
        return cnt == numCourses;
    }
};

208 实现前缀树

https://leetcode.cn/problems/implement-trie-prefix-tree/

数据类型的主要结构是 children,是一个 vector<Trie*>, 长度为 26(对应 26 个字母)以及一个 is_end 的 bool 变量代表是否到了结尾。
在插入单词过程中,从 this 指针开始,遍历字符串的每个字符,每个字符串减去'a',获得下标,如果当前指针的 child 是 nullptr,那么创建新的。遍历到最后一个字母,需要将其 is_end 置 true。
search()和 startwith()通过深度优先搜索,如果是 nullptr 那么就没有包含这个单词,search 比 startwith 条件更加苛刻,需要 is_end 为 true

class Trie {
private:
    vector<Trie*> cache;
    bool is_end;
public:
    Trie(): cache(26), is_end(false) {
        is_end = false;
    }
    Trie* dfs(const string& word){
        auto cur = this;
        for(char c: word){
            int index = c - 'a';
            if(!cur->cache[index]){
                return nullptr;
            }
            cur = cur->cache[index];
        }
        return cur;
    }
    void insert(string word) {
        auto cur = this;
        for(char c:word){
            int index = c - 'a';
            if(!cur->cache[index]){
                cur->cache[index] = new Trie();
            }
            cur = cur->cache[index];
        }
        cur->is_end = true;
    }

    bool search(string word) {
        auto cur = dfs(word);
        return cur!=nullptr && cur->is_end;
    }

    bool startsWith(string prefix) {
        auto cur = dfs(prefix);
        return cur!=nullptr;
    }
};

215 数组中的第 k 大元素

https://leetcode.cn/problems/kth-largest-element-in-an-array/

1、快速排序的变形
在快速排序中,如果 pivot 就是第 k 个,就得到了最终的结果,如果 pivot 比 k 要小,那么快排 pivot+1 到 end,如果 pivot 比 k 要大,那么快排 begin 到 pivot-1

class Solution {
private:
    int k;
public:
    int partition(vector<int>& nums, int left, int right){
        int i = left-1;
        int pivot_val = nums[right];
        for(int j=left;j<right;j++){
            if(nums[j] < pivot_val){
                i++;
                swap(nums[j], nums[i]);
            }
        }
        i++;
        swap(nums[i], nums[right]);
        return i;
    }
    int quick_sort(vector<int>& nums, int left, int right){
        int pivot = partition(nums, left, right);
        if(pivot == k){
            return nums[pivot];
        }else if(pivot < k){
            return quick_sort(nums, pivot+1, right);
        }
        return quick_sort(nums, left, pivot-1);
    }
    int findKthLargest(vector<int>& nums, int k) {
        this->k = nums.size() - k ;
        return quick_sort(nums, 0, nums.size()-1);
    }
};

2、堆排序
用大根堆,大根堆第一个元素是最大值,进行 k 次删除堆顶元素之后就是答案

class Solution {
public:
    void heapify(vector<int>& nums, int i, int n){
        int left = i*2+1;
        int right = i*2+2;
        int largest = i;
        if(left < n && nums[left]> nums[largest] ){
            largest = left;
        }
        if(right < n && nums[right] > nums[largest]){
            largest = right;
        }
        if(largest!=i){
            swap(nums[largest], nums[i]);
            heapify(nums, largest, n);
        }
    }
    void build_heap(vector<int>& nums){
        for(int i=(nums.size()-2)/2;i>=0;i--){
            heapify(nums, i, nums.size());
        }
    }
    int findKthLargest(vector<int>& nums, int k) {
        build_heap(nums);
        int n = nums.size();
        for(int i=0;i<k-1;i++){
            swap(nums[0], nums[n-1]);
            n--;
            heapify(nums, 0, n);
        }
        return nums[0];
    }
};

221.最大正方形

https://leetcode.cn/problems/maximal-square/

f[i][j] = min(f[i-1][j-1], min(f[i-1][j], f[i][j-1])) + 1;

class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        int n = matrix.size();
        int m = matrix[0].size();
        int res = 0;
        vector<vector<int>> f(n, vector<int>(m, 0));
        for(int i=0;i<n;i++)
        {
            f[i][0] = matrix[i][0] == '1'? 1: 0;
            res = max(res, f[i][0]);
        }
        for(int i=0;i<m;i++)
        {
            f[0][i] = matrix[0][i] == '1'? 1: 0;
            res = max(res, f[0][i]);
        }
        for(int i=1;i<n;i++)
        {
            for(int j=1;j<m;j++)
            {
                if(matrix[i][j] == '1')
                {
                    f[i][j] = min(f[i-1][j-1], min(f[i-1][j], f[i][j-1])) + 1;
                    res = max(res, f[i][j]);
                }
            }
        }
        return res*res;
    }
};

226 翻转二叉树

https://leetcode.cn/problems/invert-binary-tree/

1、递归方法

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(!root) return root;
        auto left = root->left;
        auto right = root->right;
        invertTree(root->left);
        invertTree(root->right);
        root->left = right;
        root->right = left;
        return root;
    }
};

2、非递归
用 stack 或者 queue 进行储存(两种容器的代码是一样的,只不过或是层序遍历,stack 是深度优先遍历)

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        if(root==nullptr){
            return nullptr;
        }
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty())
        {
            auto front = q.top();
            q.pop();
            auto left = front->left;
            front->left = front->right;
            front->right = left;
            if(front->left){
                q.push(front->left);
            }
            if(front->right){
                q.push(front->right);
            }
        }
        return root;

    }
};

234 回文链表

https://leetcode.cn/problems/palindrome-linked-list/

1、用 vector 储存

class Solution {
public:
    bool isPalindrome(ListNode* head) {
        vector<ListNode*> v;
        while(head){
            v.push_back(head);
            head = head->next;
        }
        for(int i=0, j=v.size()-1;i<j;i++, j--){
            if(v[i]->val != v[j]->val){
                return false;
            }
        }
        return true;
    }
};

2、找到中点,翻转链表。 奇数链表中间点被看做前半部分,判断比较前半部分和翻转的后半部分是否相同时,以后半部分为标准。

class Solution {
public:
    ListNode* reverse_list(ListNode* head){
        ListNode* prev = nullptr;
        while(head){
            auto next = head->next;
            head->next = prev;
            prev = head;
            head = next;
        }
        return prev;
    }
    bool isPalindrome(ListNode* head) {
        auto fast = head;
        auto slow = head;
        while(fast->next && fast->next->next){
            fast = fast->next->next;
            slow = slow->next;
        }
        auto mid = slow->next;
        slow->next = nullptr;
        mid = reverse_list(mid);
        while(mid && head){
            if(mid->val != head->val){
                return false;
            }
            mid = mid->next;
            head = head->next;
        }
        return true;
    }
};

236 二叉树的公共祖先

https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/

1、遍历方法
深度搜索函数搜查是否包含 q 和 p,在搜索过程中,如果 root 等于 q 或者 p 并且 left 和 right 某个为正,或者左右都为正,代表是公共祖先

class Solution {
private:
    TreeNode* res;
public:
    bool contains(TreeNode* root, TreeNode* p, TreeNode* q){
        if(root == nullptr) return false;
        bool left = contains(root->left, p, q);
        bool right = contains(root->right, p, q);
        if( ( root==p || root==q) && (left || right) || (left && right)){
            res = root;
            return true;
        }
        return root==p || root == q ||left || right;
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        contains(root, p, q);
        return res;
    }
};

2、递归
递归函数返回值为 nullptr 表示没有找到

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == nullptr || root==p || root==q) return root;
        auto left = lowestCommonAncestor(root->left, p, q);
        auto right = lowestCommonAncestor(root->right, p, q);
        if(left && right) return root;
        if(left != nullptr)
        return left;
        return right;
    }
};

238 除自身以为的数组的乘积

https://leetcode.cn/problems/product-of-array-except-self/

维护两个数组,一个是左边累积;一个是右边累积

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        vector<int> res(nums.size(), 1);
        vector<int> left(nums.size(), 1);
        vector<int> right(nums.size(), 1);
        for(int i=1;i<nums.size();i++)
        {
            left[i] = nums[i-1]*left[i-1];
        }
        for(int i=nums.size()-2;i>=0;i--)
        {
            right[i] = nums[i+1]*right[i+1];
        }
        for(int i=1;i<nums.size()-1;i++)
        {
            res[i] = left[i]*right[i];
        }
        res[0] = right[0];
        res[nums.size()-1] = left[nums.size()-1];
        return res;
    }
};

可以用一个变量代替第二个数组,因为不用保留前面的变量。

239 滑动窗口的最大值

https://leetcode.cn/problems/sliding-window-maximum/

1、优先队列
维护一个优先队列,内容是元素的值和下标的 pair,每次 push 元素之前判断队列顶元素下标是否在范围内。

class Solution {
public:
    struct node{
        int idx;
        int val;
    };
    struct cmp{
        bool operator ()(node& a, node& b){
            return a.val < b.val;
        }
    };
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        priority_queue<node, vector<node>, cmp> q;
        vector<int> res;
        for(int i=0;i<k;i++)
        {
            q.push(node{i, nums[i]});
        }
        res.push_back(q.top().val);
        for(int i=k;i<nums.size();i++)
        {
            q.push(node{i, nums[i]});
            while(q.top().idx <= i-k){
                q.pop();
            }
            res.push_back(q.top().val);
        }
        return res;
    }
};

2、优化——单调队列
维护一个双端队列, 里面存元素的下标,让队列前面元素比后面元素大,且开头元素一定要符合窗口的范围。

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> dq;
        vector<int> res;
        for (int i = 0; i < k; i++) {
            while (!dq.empty() && nums[i] > nums[dq.back()]) {
                dq.pop_back();
            }
            dq.push_back(i);
        }
        res.push_back(nums[dq.front()]);
        for (int i = k; i < nums.size(); i++) {
            while (!dq.empty() && dq.front() <= i - k) {
                dq.pop_front();
            }
            while (!dq.empty() && nums[dq.back()] <= nums[i]) {
                dq.pop_back();
            }
            dq.push_back(i);
            res.push_back(nums[dq.front()]);
        }
        return res;
    }
};

240 搜索二维矩阵

https://leetcode.cn/problems/search-a-2d-matrix-ii/

从右上开始搜索

class Solution
{
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int n = matrix.size();
        int m = matrix[0].size();
        int i = 0;
        int j = m - 1;
        while (i < n && j >= 0) {
            int val = matrix[i][j];
            if (val == target) {
                return true;
            }
            else if (val > target) {
                j--;
            }
            else {
                i++;
            }

        }
        return false;
    }
};

279 完全平方数

动态规划 f[i] = min(f[i], f[i-j*j]+1),其中(j*j≤i)

class Solution {
public:
    int numSquares(int n) {
        vector<int> f(n+1, INT_MAX);
        f[0] = 0;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=sqrt(i);j++)
            {
                f[i] = min(f[i], f[i-j*j]+1);
            }
        }
        return f[n];
    }
};

283 移动零

https://leetcode.cn/problems/move-zeroes/

和快速排序的 parition 差不多

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

287 寻找重复数字

https://leetcode.cn/problems/find-the-duplicate-number/

1、二分法 O(n*lgn) 每次 n 次循环计算

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

2、快慢指针
因为一定有某个点有两条线指向自己,所以必定存在环,且环的入口就是重复的数字。同链表的环

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

297 二叉树的序列化和反序列化

https://leetcode.cn/problems/serialize-and-deserialize-binary-tree/

注意字符串拼接的时候要用+= ,c++底层有优化。
1、层序遍历

class Codec {
public:
   vector<string> get_nodes(string data) {
       vector<string> res;
       int i = 0;
       while (i < data.size()) {
           int j = i;
           while (j < data.size() && data[j] != ',') {
               j++;
           }
           res.push_back(data.substr(i, j - i));
           i = j + 1;
       }
       return res;
   }

   // Encodes a tree to a single string.
   string serialize(TreeNode* root) {
       string data = "";
       if (root == nullptr) return data;
       queue<TreeNode*> q;
       q.push(root);
       while (!q.empty()) {
           auto front = q.front();
           q.pop();
           if (front) {
               data += to_string(front->val) + ",";
               q.push(front->left);
               q.push(front->right);
           }
           else {
               data += "null,";
           }
       }
       return data.substr(0, data.size() - 1);
   }

   // Decodes your encoded data to tree.
   TreeNode* deserialize(string data) {
       if (data.size() == 0) return nullptr;
       auto nodes = get_nodes(data);
       queue<TreeNode*> q;
       auto root = new TreeNode(stoi(nodes[0]));
       q.push(root);
       int idx = 1;
       while (!q.empty()) {
           auto front = q.front();
           q.pop();
           if (nodes[idx] != "null") {
               front->left = new TreeNode(stoi(nodes[idx]));
               q.push(front->left);
           }
           idx++;
           if (nodes[idx] != "null") {
               front->right = new TreeNode(stoi(nodes[idx]));
               q.push(front->right);
           }
           idx++;
       }
       return root;
   }
};

300 最长递增子序列

https://leetcode.cn/problems/longest-increasing-subsequence/

动态规划,要注意把之前的也加上

class Solution {
public:
   int lengthOfLIS(vector<int>& nums) {
       vector<int> f(nums.size(), 1);
       int res = 1;
       for (int i = 1; i < nums.size(); i++) {
           for (int j = 0; j < i; j++) {
               if (nums[i] > nums[j]) {
                   f[i] = max(f[i], f[j] + 1);
               }
           }
           res = max(f[i], res);
       }
       return res;
   }
};

301 删除无效括号

https://leetcode.cn/problems/remove-invalid-parentheses/

先计算需要删除的左括号和右括号,然后通过深度优先搜索遍历

class Solution {
private:
    vector<string> res;
public:
    bool is_pair(char a, char b){
        if(a==')' && b=='(') return true;
        return false;
    }
    bool is_valid(string s){
        stack<char> stk;
        for(char c: s){
            if(c!=')' && c!='(') continue;
            if(!stk.empty() && is_pair(c, stk.top())){
                stk.pop();
            }else{
                stk.push(c);
            }
        }
        return stk.empty();
    }

    void dfs(string s, int lr, int rr, int idx){
        if(lr==0 && rr == 0 && is_valid(s)){
            res.push_back(s);
            return;
        }
        for(int i=idx;i<s.size();i++){
            if(i!=0 && s[i] == s[i-1]) continue;
            if(lr+rr > s.size()-i) return;
            if(s[i] == '(' && lr>0){
                dfs(s.substr(0, i)+s.substr(i+1, s.size()-i-1), lr-1, rr, i);

            }
            if(s[i] == ')' && rr>0){
                dfs(s.substr(0, i)+s.substr(i+1, s.size()-i-1), lr, rr-1, i);
            }
        }
    }
    vector<string> removeInvalidParentheses(string s) {
        int lr = 0;
        int rr = 0;
        for(char c: s){
            if(c=='('){
                lr++;
            }else if(c==')'){
                if(lr>0){
                    lr--;
                }else{
                    rr++;
                }
            }
        }
        dfs(s, lr, rr, 0);
        return res;
    }
};

20 有效括号

https://leetcode.cn/problems/valid-parentheses/submissions/
用 stack 进行判断,最后判断 stack 是否为空

class Solution {
public:
   bool is_pair(char a, char b) {
       if (a == ')' && b == '(') return true;
       if (a == ']' && b == '[') return true;
       if (a == '}' && b == '{') return true;
       return false;
   }
   bool isValid(string s) {
       stack<char> st;
       for (auto c : s) {
           if (!st.empty() && is_pair(c, st.top()) == true) {
               st.pop();
           }
           else {
               st.push(c);
           }
       }
       return st.empty();
   }
};

22 括号生成

https://leetcode.cn/problems/generate-parentheses/

回溯+剪枝
左括号数量比右括号数量多

class Solution {
private:
   vector<string> res;
public:
   void dfs(string& str, int left, int right, int n) {
       if (left > n || right > n) return;
       if (left == n && right == n) {
           res.push_back(str);
       }
       if (left > right) {
           str.push_back('(');
           dfs(str, left + 1, right, n);
           str.pop_back();
           str.push_back(')');
           dfs(str, left, right + 1, n);
           str.pop_back();
       }
       else if (left == right) {
           str.push_back('(');
           dfs(str, left + 1, right, n);
           str.pop_back();
       }
   }
   vector<string> generateParenthesis(int n) {
       int left = 0;
       int right = 0;
       string str = "";
       dfs(str, left, right, n);
       return res;
   }
};

309 买卖股票

121 买卖股票的最佳时机

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/

维护一个变量,记录到当前的最小值

class  Solution {
public:
		int  maxProfit(vector<int>&  prices) {
				int curMin = INT_MAX;
				int res = 0;
				for(int price:prices){
						res = max(res, price-curMin);
						curMin = min(curMin, price);
					}
					return res;
		}
};

122. 买卖股票的最佳时机 II

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-ii/

不能多次交易股票,在 i 天结束后,只存在两个状态,0 是持有现金,1 是持有股票

f[0][i] = max(f[0][i-1], f[1][i-1]+price[i])
f[1][i] = max(f[1][i-1], f[0][i-1]-price[i])

i 天如果持有现金,要么 i 天没有买股票,那么取 i-1 天持有现金,或者 i 天卖出股票
i 天如果持有股票, 那么 i 天没有买股票,那么取 i-1 天持有股票,或者 i 天买入股票

class Solution
{
	public:
		int maxProfit(vector<int> &prices)
		{
			// 动态规划
			vector<vector < int>> f(2, vector<int> (prices.size(), 0));
			int cash_hold = 0;
			int stock_hold = -prices[0];
			for (int i = 1; i < prices.size(); i++)
			{
				cash_hold = max(cash_hold, stock_hold + prices[i]);
				stock_hold = max(stock_hold, cash_hold - prices[i]);
			}

			return cash_hold;
		}
};

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

https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-with-cooldown/

有冷却期,那么 i 天结束后就有三种状态:1、持有现金,非冷却期。2、持有现金,冷却期 3、持有股票

class Solution {
public:
   int maxProfit(vector<int>& prices) {
       if (prices.size() == 1 || prices.size() == 0)return 0;
       vector<vector<int>> f(3, vector<int>(prices.size()));
       // f[0][i] 持有现金,非冷冻期
       // f[1][i] 持有现金,冷冻期
       // f[2][i] 持有股票
       f[2][0] = -prices[0];
       for (int i = 1; i < prices.size(); i++)
       {
           f[0][i] = max(f[0][i - 1], f[1][i - 1]);
           f[1][i] = f[2][i - 1] + prices[i];
           f[2][i] = max(f[2][i - 1], f[0][i - 1] - prices[i]);
       }
       return max(f[0][prices.size() - 1], f[1][prices.size() - 1]);
   }
};

312.戳气球

https://leetcode.cn/problems/burst-balloons/
动态规划要求子问题必须独立,所以我们将其 nums 扩容 2(两个边界-1 和 n)并设置成 1,气球的索引变成了 1-n,并且我们将戳气球的操作变成添加气球操作

题解

动态规划

dp[i][j]

表示戳破在 i 和 j 之间的气球可以获得的最大值,其中不包括 i 和 j,这样以来就转换成求

dp[0][n+1]

如果正向思考,那么就是回到回溯。我们需要反向思考,

谁是最后一个被戳破的气球

,那就问题就可以被转换成

dp[i][j] = max(d[i][k]+d[k][j] + points[i]*point[j]*point[k])
d[i][j]

依赖的状态是

d[i][k]

d[k][j]

(其中 i<k<j),所以要注意顺序,

class Solution {
public:
    int maxCoins(vector<int>& nums) {
        int n = nums.size();
        vector<int> val(n + 2, 1);
        for (int i = 0; i < nums.size(); i++)
        {
            val[i + 1] = nums[i];
        }
        vector<vector<int>> dp(n + 2, vector<int>(n + 2, 0));
        for (int i = n - 1; i >= 0; i--)
        {
            for (int j = i + 1; j <= n + 1; j++)
            {
                for (int k = i + 1; k < j; k++)
                {
                    int sum = dp[i][k] + dp[k][j] + val[i] * val[j] * val[k];
                    dp[i][j] = max(dp[i][j], sum);
                }
            }
        }
        return dp[0][n + 1];
    }
};

322 零钱兑换

https://leetcode.cn/problems/coin-change/
动态规划
f[i] = min(f[i-coin]+1, f[i])

class Solution {
public:
    int coinChange(vector<int>& coins, int amount) {
        int max_val = amount + 1;
        vector<int> f(amount + 1, max_val);
        f[0] = 0;
        for (int i = 0; i <= amount; i++) {
            for (int coin : coins) {
                if (i - coin >= 0) {
                    f[i] = min(f[i], f[i - coin] + 1);
                }
            }
        }
        return f[amount] != max_val ? f[amount] : -1;
    }
};

338 比特位计算

https://leetcode.cn/problems/counting-bits/

动态规划, 如果 i 是偶数,那么比特位就是 i/2,如果是奇数,那么就是 f[i-1]+1
因为偶数的话相当于末尾补了一个 0, 如 2:10 4:100
奇数的话就相当于上一个偶数添加了一个 1, 如 2:10 3:11

class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> f(n + 1);
        f[0] = 0;
        for (int i = 1; i <= n; i++) {
            if (i % 2 == 0) {
                f[i] = f[i / 2];
            }
            else {
                f[i] = f[i - 1] + 1;
            }
        }
        return f;
    }
};

347 前 k 个高频元素

https://leetcode.cn/problems/kth-largest-element-in-an-array/solution/

先用 unordered_map 进行存储,之后对频率进行操作,类似于第 k 个大的元素
1、优先队列

class Solution {
private:
    vector<int> res;
public:
    struct node {
        int val;
        int freq;
    };
    struct cmp {
        bool operator()(node a, node b) {
            return a.freq < b.freq;
        }
    };
    vector<int> topKFrequent(vector<int>& nums, int k) {
        priority_queue<node, vector<node>, cmp> pq;
        unordered_map<int, int> dict;
        for (int each : nums) {
            dict[each]++;
        }
        for (auto it = dict.begin(); it != dict.end(); it++) {
            pq.push(node{ it->first, it->second });
        }
        for (int i = 0; i < k; i++) {
            auto front = pq.top();
            pq.pop();
            res.push_back(front.val);
        }
        return res;
    }
};

! 394. 字符串解码

使用栈

https://leetcode.cn/problems/decode-string/?favorite=2cktkvj

class Solution {
public:
    string decodeString(string s) {
        string ans = "";
        int mul = 0;
        stack<pair<int, string>> stk;
        for(int i=0;i<s.size();i++){
            char cur = s[i];
            if(isdigit(cur)){
                mul = mul*10 + cur-'0';
            }else if(isalpha(cur)){
                if(stk.empty()){
                    ans.push_back(cur);
                }else{
                    stk.top().second.push_back(cur);
                }
            }else if(cur == '['){
                stk.push({mul, ""});
                mul = 0;
            }else if(cur == ']'){
                auto top = stk.top();
                stk.pop();
                string tp = "";
                for(int i=0;i<top.first;i++){
                    tp += top.second;
                }
                if(stk.empty()){
                    ans += tp;
                }else{
                    stk.top().second += (tp);
                }
            }
        }
        return ans;
    }
};

406 根据身高建立队列

https://leetcode.cn/problems/queue-reconstruction-by-height/
cmp 函数,第一个顺序是身高(高的排前面),第二顺序前面的人数(人少的排前面)
然后 for 循环遍历插入,insert 位置是 vector.begin()+前面排的人数

class Solution {
public:
    static bool cmp(vector<int> a, vector<int> b){
        if(a[0] != b[0]) return a[0] > b[0];
        return a[1] < b[1];
    }
    vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
        vector<vector<int>> res;
        sort(people.begin(), people.end(), cmp);
        for(int i=0;i<people.size();i++){
            res.insert(res.begin() + people[i][1], people[i]);
        }
        return res;
    }
};

416 分割等和子集

https://leetcode.cn/problems/partition-equal-subset-sum/

题目要求是是否存在等分子集,首先算出 sum,然后转化为背包问题,也就是可以可以完全放下 sum/2 的物品

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int sum = 0;
        for(int val: nums){
            sum += val;
        }
        if(sum%2 != 0) return false;
        int target = sum/2;
        int n = nums.size();
        vector<vector<int>> dp(n+1, vector<int>(target+1, false));
        for(int i=0;i<=n;i++)
        {
            dp[i][0] = true;
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=target;j++)
            {
                if(j-nums[i-1] < 0)
                {
                    dp[i][j] = dp[i-1][j];
                }else{
                    if(j-nums[i-1] == 0){
                        dp[i][j] = true;
                    }else{
                        dp[i][j] = dp[i-1][j-nums[i-1]] || dp[i-1][j];
                    }
                }
            }
        }
        return dp[n][target];
    }
};

437 路径总和

https://leetcode.cn/problems/path-sum-iii/

前缀和, dfs 回溯

class Solution {
private:
    unordered_map<long long, int> cache;
    int res = 0;
public:
    void dfs(TreeNode* root, long long cur, int targetSum){
        cur += root->val;
        res += cache[cur-targetSum];
        cache[cur]++;
        if(root->left)
            dfs(root->left, cur, targetSum);
        if(root->right)
            dfs(root->right, cur, targetSum);
        cache[cur]--;
    }
    int pathSum(TreeNode* root, int targetSum) {
        if(root == nullptr) return 0;
        cache[0] = 1;
        dfs(root, 0, targetSum);
        return res;
    }
};

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

https://leetcode.cn/problems/find-all-anagrams-in-a-string/

滑动窗口,注意滑动窗口 valid 的条件,当 windows[add]==need[add]是 valid 才增加或者减少,valid 的数值和 need 的 key 值相同时进行左边界改变

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        unordered_map<char, int> need;
        unordered_map<char, int> window;
        for(char c: p){
            need[c]++;
        }
        vector<int> res;
        int left = 0;
        int right = 0;
        int valid = 0;
        while(right<s.size())
        {
            char add = s[right++];
            if(need.count(add))
            {
                window[add]++;
                if(window[add] == need[add]){
                    valid++;
                }
            }
            while(valid >= need.size())
            {
                char deleted = s[left];
                if(right-left==p.size()){
                    res.push_back(left);
                }
                if(need.count(deleted))
                {
                    if(window[deleted] == need[deleted]){
                        valid--;
                    }
                    window[deleted]--;
                }
                left++;
            }
        }
        return res;
    }
};

560 和为 K 的子数组

https://leetcode.cn/problems/subarray-sum-equals-k/

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<long long, int> cache;
        cache[0] = 1;
        int cur = 0;
        int res = 0;
        for(int i=0;i<nums.size();i++)
        {
            cur += nums[i];
            res += cache[cur-k];
            cache[cur]++;
        }
        return res;
    }
};

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

https://leetcode.cn/problems/find-all-numbers-disappeared-in-an-array/

tricky, 遍历数组, num[i]的 index 对应的数字加上数组的长度,最后小于等于数组长度的就是消失的。

class Solution {
public:
    vector<int> findDisappearedNumbers(vector<int>& nums) {
        int n = nums.size();
        for(int i=0;i<nums.size();i++)
        {
            nums[(nums[i]-1)%n] += n;
        }
        vector<int> res;
        for(int i=0;i<nums.size();i++){
            if(nums[i] <=n){
                res.push_back(i+1);
            }
        }
        return res;
    }
};

461 汉明距离

https://leetcode.cn/problems/hamming-distance/

异或

class Solution {
public:
    int hammingDistance(int x, int y) {
        int t = x^y;
        int op = 1;
        int res = 0;
        while(t)
        {
            res += t&op;
            t = t>>1;
        }
        return res;
    }
};

494 目标和

https://leetcode.cn/problems/target-sum

算出 neg 的和然后转化为背包问题

class Solution {
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        int sum = 0;
        for(int num: nums){
            sum += num;
        }
        // neg + pos = sum
        // -neg + pos = target
        int tp = sum - target;
        if(tp %2 != 0 || tp<0){
            return 0;
        }
        int neg = tp/2;
        int n = nums.size();
        vector<vector<int>> dp(n+1, vector<int>(neg+1, 0));
        dp[0][0] = 1;
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j<=neg;j++)
            {
                int cur = nums[i-1];
                if(j- cur < 0){
                    dp[i][j] = dp[i-1][j];
                }else{
                    dp[i][j] = dp[i-1][j] + dp[i-1][j-cur]; // 不选:计算i-1,j的数量,选了:计算i-1, j-cur的数量;
                }
            }
        }
        return dp[n][neg];
    }
};

538. 把二叉搜索树转换为累加树

https://leetcode.cn/problems/convert-bst-to-greater-tree/

深度搜索,不过是先右节点,中间节点,左节点,用全局的 cur 变量记录

class Solution {
private:
    int cur;
public:
    void dfs(TreeNode* root){
        if(root==nullptr) return;
        dfs(root->right);
        cur += root->val;
        root->val = cur;
        dfs(root->left);
    }
    TreeNode* convertBST(TreeNode* root) {
        dfs(root);
        return root;
    }
};

543 二叉树直径

https://leetcode.cn/problems/diameter-of-binary-tree

定义 max_depth 函数,在计算最大深度中顺便计算本题结果。

class Solution {
private:
    int res = INT_MIN;
public:
    int max_depth(TreeNode* root){
        if(root == nullptr) return 0;
        int left = max_depth(root->left);
        int right = max_depth(root->right);
        res = max(res, left+right);
        return 1+max(left, right);
    }
    int diameterOfBinaryTree(TreeNode* root) {
        if(root == nullptr) return 0;
        max_depth(root);
        return res;
    }
};

560 和为 K 的子数组

https://leetcode.cn/problems/subarray-sum-equals-k

前缀和

class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        unordered_map<long long, int> cache;
        cache[0] = 1;
        int cur = 0;
        int res = 0;
        for(int i=0;i<nums.size();i++)
        {
            cur += nums[i];
            res += cache[cur-k];
            cache[cur]++;
        }
        return res;
    }
};

581 最短无序连续子数组

https://leetcode.cn/problems/shortest-unsorted-continuous-subarray

分为三段左段,中段和右段。左端和右端是标准的升序数组,中段无序,但是满足其最小值大于左段最大值,最大值小于右端最小值。我们分别从左边开始遍历和从右边开始遍历,左边开始遍历寻找中段 end,具体是,如果遍历到的点比最大值还要小那么更新 end(原理是右端总是有当前值就是最大值),同理右边开始遍历,寻找中段 begin,如果遍历到点比最小值还小那么更新 begin(原理是左端总是有当前值就是最小值)

class Solution {
public:
    int findUnsortedSubarray(vector<int>& nums) {
        int end = -1;
        int max_val = nums[0];
        for(int i=0;i<nums.size();i++){
            max_val = max(max_val, nums[i]);
            if(nums[i] < max_val){
                end = i;
            }
        }
        int start = -1;
        int min_val = nums[nums.size()-1];
        for(int i = nums.size()-1;i>=0;i--){
            min_val = min(min_val, nums[i]);
            if(nums[i]>min_val){
                start = i;
            }
        }
        return start==-1? 0: end-start+1;
    }
};

617 合并二叉树

https://leetcode.cn/problems/merge-two-binary-trees

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

647 回文子串

https://leetcode.cn/problems/palindromic-substrings

中心拓展法

class Solution {
private:
    int res = 0;
public:
    void extend_center(string& s, int start, int end)
    {
        while(start>=0 && end<s.size() && s[start] == s[end])
        {
            res++;
            start--;
            end++;
        }
    }

    int countSubstrings(string s) {
        for(int i=0;i<s.size();i++)
        {
            extend_center(s, i, i+1);
            extend_center(s, i, i);
        }
        return res;
    }
};

739 每日温度

https://leetcode.cn/problems/daily-temperatures/

单调栈

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        stack<int> st;
        vector<int> res(temperatures.size(), 0);
        for(int i=0;i<temperatures.size();i++)
        {
            while(!st.empty() && temperatures[st.top()] < temperatures[i] )
            {
                res[st.top()] = i-st.top();
                st.pop();
            }
            st.push(i);
        }
        return res;
    }
};

621 任务调度器

tricky

class Solution {
public:
    static bool cmp(int x, int y){
        return x>y;
    }
    int leastInterval(vector<char>& tasks, int n) {
        int len = tasks.size();
        vector<int> vec(26);
        for(char c: tasks) vec[c-'A']++;
        sort(vec.begin(), vec.end(), cmp);
        int cnt = 0;
        while(cnt<vec.size() && vec[cnt]==vec[0]) cnt++;
        return max(len, cnt+(n+1)*(vec[0]-1));
    }
};

1 两数之和

https://leetcode.cn/problems/two-sum/

用 unordered_map 储存

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> dict;
        vector<int> res;
        if(nums.size() == 0){
            return res;
        }
        for(int i=0;i<nums.size();i++){
            if(dict.count(nums[i])){
                res.push_back(i);
                res.push_back(dict[nums[i]]);
                return res;
            }
            dict[target-nums[i]] = i;
        }
        return res;
    }
};

2 两数相交

https://leetcode.cn/problems/intersection-of-two-linked-lists/

链表,注意进位的最后一次运算

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        //
        // 9 9 9 9 9 9 9
        // 9 9 9 9
        //
        auto dummy = new ListNode(0);
        auto cur = dummy;
        int val;
        int x = 0;
        while(l1 && l2){
            val = l1->val+l2->val + x;
            x = val/10;
            cur->next = new ListNode(val%10);
            cur = cur->next;
            l1 = l1->next;
            l2 = l2->next;
        }
        if(l1==nullptr){
            swap(l1, l2);
        }
        while(l1){
            val = l1->val + x;
            x = val/10;
            cur->next = new ListNode(val%10);
            cur = cur->next;
            l1 = l1->next;
        }
        if(x){
            cur->next = new ListNode(x);
            cur = cur->next;
        }
        return dummy->next;
    }
};

3 无重复字符的最长子串

https://leetcode.cn/problems/longest-substring-without-repeating-characters/

双指针 or 滑动窗口?

利用哈希表 unordered_map<char, int>记录字符出现的位置,左指针初始为-1,左开右比,每次更新左指针的最大值

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int res = 0;
        unordered_map<char, int> cache;
        int left = -1;
        for(int right=0;right<s.size();right++){
            char cur = s[right];
            if(cache.count(cur)){
                left = max(left, cache[cur]);
            }
            res = max(res, right-left);
            cache[cur] = right;
        }
        return res;
    }
};

4 寻找两个正序数组的中位数

将问题化简,当数组是奇数个时,求中间的那个,偶数时求中间的两位数,为了防止分类讨论,将问题转换为求第(n+m+1)/2 个数字和第(n+m+2)/2 个数字

这道题让我们求两个有序数组的中位数,而且限制了时间复杂度为 O(log (m+n)),看到这个时间复杂度,自然而然的想到了应该使用二分查找法来求解。那么回顾一下中位数的定义,如果某个有序数组长度是奇数,那么其中位数就是最中间那个,如果是偶数,那么就是最中间两个数字的平均值。这里对于两个有序数组也是一样的,假设两个有序数组的长度分别为 m 和 n,由于两个数组长度之和 m+n 的奇偶不确定,因此需要分情况来讨论,对于奇数的情况,直接找到最中间的数即可,偶数的话需要求最中间两个数的平均值。为了简化代码,不分情况讨论,我们使用一个小 trick,我们分别找第 (m+n+1) / 2 个,和 (m+n+2) / 2 个,然后求其平均值即可,这对奇偶数均适用。加入 m+n 为奇数的话,那么其实 (m+n+1) / 2 和 (m+n+2) / 2 的值相等,相当于两个相同的数字相加再除以 2,还是其本身。

这里我们需要定义一个函数来在两个有序数组中找到第 K 个元素,下面重点来看如何实现找到第 K 个元素。首先,为了避免产生新的数组从而增加时间复杂度,我们使用两个变量 i 和 j 分别来标记数组 nums1 和 nums2 的起始位置。然后来处理一些边界问题,比如当某一个数组的起始位置大于等于其数组长度时,说明其所有数字均已经被淘汰了,相当于一个空数组了,那么实际上就变成了在另一个数组中找数字,直接就可以找出来了。还有就是如果 K=1 的话,那么我们只要比较 nums1 和 nums2 的起始位置 i 和 j 上的数字就可以了。难点就在于一般的情况怎么处理?因为我们需要在两个有序数组中找到第 K 个元素,为了加快搜索的速度,我们要使用二分法,对 K 二分,意思是我们需要分别在 nums1 和 nums2 中查找第 K/2 个元素,注意这里由于两个数组的长度不定,所以有可能某个数组没有第 K/2 个数字,所以我们需要先检查一下,数组中到底存不存在第 K/2 个数字,如果存在就取出来,否则就赋值上一个整型最大值。如果某个数组没有第 K/2 个数字,那么我们就淘汰另一个数字的前 K/2 个数字即可。有没有可能两个数组都不存在第 K/2 个数字呢,这道题里是不可能的,因为我们的 K 不是任意给的,而是给的 m+n 的中间值,所以必定至少会有一个数组是存在第 K/2 个数字的。最后就是二分法的核心啦,比较这两个数组的第 K/2 小的数字 midVal1 和 midVal2 的大小,如果第一个数组的第 K/2 个数字小的话,那么说明我们要找的数字肯定不在 nums1 中的前 K/2 个数字,所以我们可以将其淘汰,将 nums1 的起始位置向后移动 K/2 个,并且此时的 K 也自减去 K/2,调用递归。反之,我们淘汰 nums2 中的前 K/2 个数字,并将 nums2 的起始位置向后移动 K/2 个,并且此时的 K 也自减去 K/2,调用递归即可。

class Solution {
public:

    double findKth(vector<int>& nums1, int i1, vector<int>& nums2, int i2, int k){
        if(i1 >= nums1.size()) return nums2[i2+k-1];
        if(i2 >= nums2.size()) return nums1[i1+k-1];
        if(k==1) return min(nums1[i1], nums2[i2]);
        int nums1_mid = i1+(k/2)-1 >= nums1.size() ? INT_MAX: nums1[i1+(k/2)-1];
        int nums2_mid = i2+(k/2)-1 >= nums2.size() ? INT_MAX: nums2[i2+(k/2)-1];
        if(nums1_mid > nums2_mid){
            return findKth(nums1, i1, nums2, i2+(k/2), k-k/2);
        }
        return findKth(nums1, i1+(k/2), nums2, i2, k-k/2);
    }
    double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size();
        int m = nums2.size();
        int left = (m+n+1)/2;
        int right = (m+n+2)/2;
        return (1.0*findKth(nums1, 0, nums2, 0, left) + findKth(nums1, 0, nums2, 0, right))/2;
    }

};

5 最长回文字串

trick 中间增长法

class Solution {
private:
    string res;
public:
    void extend_mid(string &s , int i1, int i2){
        while(i1>=0 && i2<s.size() && s[i1] == s[i2]){
            int len = i2-i1+1;
            if(len>res.size()){
                res = s.substr(i1, len);
            }
            i1--;
            i2++;
        }
    }
    string longestPalindrome(string s) {
        for(int i=0;i<s.size();i++)
        {
            extend_mid(s, i, i+1);
            extend_mid(s, i, i);
        }
        return res;
    }
};

11 盛最多水的容器

双指针

https://leetcode.cn/problems/container-with-most-water/

left=0, right = size()-1, 当前的水容量是

$$
min(height[left], height[right])* (right-left)
$$

当减少宽度时,如果移动更高的一边,那么容量肯定只会减少,如果移动更高的一边,容量可能减少也可能增加

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

15 三数之和

https://leetcode.cn/problems/3sum/

首先对数组进行排序,然后转换为两数之和。(其中要进行去重 both in main and two sum)

class Solution {
private:
    vector<vector<int>> res;
public:
    void twoSum(vector<int> nums, int start, int target){
        int left = start+1;
        int right = nums.size()-1;
        while(left<right){
            int cur = nums[left] + nums[right];
            if(cur == target){
                res.push_back(vector<int>{nums[left], nums[right], nums[start]});
                int left_val = nums[left];
                int right_val  = nums[right];
                while(left<right && nums[left] == left_val) left++;
                while(left<right && nums[right] == right_val) right--;
            }else if(cur<target){
                left++;
            }else{
                right--;
            }
        }
    }
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        for(int i=0;i<nums.size();i++)
        {
            if(i!=0 && nums[i] == nums[i-1]) continue;
            twoSum(nums, i, -nums[i]);
        }
        return res;
    }
};

17 电话号码的字母组合

https://leetcode.cn/problems/letter-combinations-of-a-phone-number/

class Solution {
private:
    unordered_map<char, string> dict = {
        {'2', "abc"},
        {'3', "def"},
        {'4', "ghi"},
        {'5', "jkl"},
        {'6', "mno"},
        {'7', "qprs"},
        {'8', "tuv"},
        {'9', "wxyz"},
    };
    vector<string> res;
public:
    void dfs(string& digits, int idx, string& cur)
    {
        if(idx == digits.size()){
            res.push_back(cur);
        }
        for(int i=0;i<dict[digits[idx]].size();i++)
        {
            cur.push_back(dict[digits[idx]][i]);
            dfs(digits, idx+1, cur);
            cur.pop_back();
        }
    }
    vector<string> letterCombinations(string digits) {
        if(digits.size() == 0) return res;
        string cur = "";
        dfs(digits, 0, cur);
        return res;
    }
};

19 删除链表的倒数第 N 个结点

https://leetcode.cn/problems/remove-nth-node-from-end-of-list

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        auto dummy = new ListNode(0);
        dummy->next = head;
        auto fast = dummy;
        auto slow = dummy;
        for(int i=0;i<n;i++){
            fast = fast->next;
        }
       while(fast->next){
           fast = fast->next;
           slow = slow->next;
       }
       slow->next = slow->next->next;
       return dummy->next;
    }
};

20 有效括号

https://leetcode.cn/problems/valid-parentheses

使用栈

class Solution {
public:
    bool is_pair(char a, char b){
        if(a==')' && b=='(') return true;
        if(a==']' && b=='[') return true;
        if(a=='}' && b=='{') return true;
        return false;
    }
    bool isValid(string s) {
        stack<char> st;
        for(auto c:s){
            if(!st.empty() && is_pair(c, st.top())==true){
                st.pop();
            }else{
                st.push(c);
            }
        }
        return st.empty();
    }
};

合并两个有序链表

https://leetcode.cn/problems/merge-two-sorted-lists/

  1. 递归
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        if(list1 == nullptr) return list2;
        if(list2 == nullptr) return list1;
        if(list1->val > list2->val){
            swap(list1, list2);
        }
        list1->next = mergeTwoLists(list1->next, list2);
        return list1;
    }
};
  1. 迭代
class Solution {
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
        if(!l1) return l2;
        if(!l2) return l1;
        ListNode* dummy = new ListNode(0);
        ListNode* tp = dummy;
        while(l1&&l2){
            if(l1->val < l2->val){
                tp->next = l1;
                l1 = l1->next;
            }else{
                tp->next = l2;
                l2 = l2->next;
            }
            tp = tp->next;
        }
        if(l1) tp->next = l1;
        if(l2) tp->next = l2;
        return dummy->next;
    }
};

括号生成

https://leetcode.cn/problems/generate-parentheses

深度搜索,其中注意左括号数量永远大于等于右括号

class Solution {
private:
    vector<string> res;
    int n;
public:
    void dfs(string& cur, int left, int right){
        if(left>n || right>n || left<right){
            return;
        }
        if(left==n && right==n){
            res.push_back(cur);
        }

        cur.push_back('(');
        dfs(cur, left+1, right);
        cur.pop_back();

        cur.push_back(')');
        dfs(cur, left, right+1);;
        cur.pop_back();
    }
    vector<string> generateParenthesis(int n) {
        string tp = "";
        this->n = n;
        dfs(tp, 0, 0);
        return res;
    }
};

23 合并 K 个升序链表

利用堆,注意大于是小根堆,小于是大根堆

class Solution {
public:
    class cmp{
        public:
            bool operator ()(ListNode* a, ListNode* b){
                return a->val > b->val;
            }
    };
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        auto dummy = new ListNode(0);
        auto cur = dummy;
        priority_queue<ListNode*, vector<ListNode*>, cmp> q;
        for(auto& node: lists){
            if(node!=nullptr){
                q.push(node);
            }
        }
        while(!q.empty())
        {
            auto front = q.top();
            q.pop();
            cur->next = front;
            cur = cur->next;
            if(front->next != nullptr){
                q.push(front->next);
            }
        }
        return dummy->next;
    }
};

!31 下一个排列

https://leetcode.cn/problems/next-permutation/

class Solution {
public:

    void nextPermutation(vector<int>& nums) {
        int i = nums.size()-1;
        while(i>0){
            if(nums[i-1] < nums[i]){
                break;
            }
            i--;
        }
        if(i==0){
            sort(nums.begin(), nums.end());
            return;
        }
        i = i-1;
        int j = nums.size() - 1;
        while(j>=0){
            if(nums[j] > nums[i]){
                break;
            }
            j--;
        }

        swap(nums[i], nums[j]);
        sort(nums.begin()+i+1, nums.end());
    }
};

32. 最长有效括号

https://leetcode.cn/problems/longest-valid-parentheses/

动态规划

dp[i] 是 i 个位置的字符为结尾的有效括号长度

当 s[i]是(的时候,dp[i]为 0,

当 s[i]是)的时候, 讨论 s[i-1]的情况

当 s[i-1]是(,那么 dp[i] = dp[i-2]+2; 注意 i-2 的是合法

当 s[i-2]是), 那么需要讨论 i-1-len,len 是 dp[i-1]的值,如果 s[i-1-len]是(,那么 dp[i] = dp[i-1]+dp[i-1-dp[i-1]-1] +2

最终返回所有 dp 的最大值

class Solution {
public:
    int longestValidParentheses(string s) {
        vector<int> dp(s.size(), 0);
        if(s.size() == 0) return 0;
        int res = 0;
        for(int i=1;i<s.size();i++)
        {
            if(s[i] == '('){
                dp[i] = 0;
            }else if(s[i] == ')'){
                if(s[i-1] == '('){
                    dp[i] = 2;
                    if(i-2>=0){
                        dp[i] += dp[i-2];
                    }
                }else{
                    if(dp[i-1]>0){
                        int len = dp[i-1];
                        int start = i-1-len;
                        if(start>=0){
                            if(s[start] == '('){
                                dp[i] = dp[i-1] + 2;
                                if(start-1 >=0){
                                    dp[i] += dp[start-1];
                                }
                            }
                        }else{
                            dp[i] = 0;
                        }
                    }
                }
            }
            res = max(res, dp[i]);
        }
        return res;
    }
};

49 字母异分位词

https://leetcode.cn/problems/group-anagrams/

利用 dict

class Solution {
private:
    vector<vector<string>> res;
    unordered_map<string, vector<string>> cache;
    vector<bool> visit;
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        for(auto& str: strs){
            auto reve = str;
            sort(reve.begin(), reve.end());
            cache[reve].push_back(str);
        }
        for(auto it=cache.begin();it!=cache.end();it++){
            res.push_back(it->second);
        }
        return res;
    }
};

53 最大子数组和

https://leetcode.cn/problems/maximum-subarray/

动态规划

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
         int cur = nums[0];
         int res = nums[0];
         for(int i=1;i<nums.size();i++){
             cur = max(nums[i], nums[i]+cur);
             res = max(res, cur);
         }
         return res;
    }
};

55 跳跃游戏

https://leetcode.cn/problems/jump-game/

贪心,定义一个最远能跳的距离 most_right, 遍历数组,如果能小于等于 most_right,那么通过该下表推进 most_right

class Solution {

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

33 搜索旋转排序数组

https://leetcode.cn/problems/search-in-rotated-sorted-array

二分搜索,分类讨论,target 是在两块区间的那个部分(和 nums[0]作比较),然后再讨论 num[mid]在哪个区间

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

56 合并区间

https://leetcode.cn/problems/merge-intervals

首先对区间进行 sort,第一排序顺序是左边界,第二排序顺序号是右边边界,建立 result vector 数组,对区间进行遍历,如果区间右边界大于 result 数组最后一个的右边界,那么 push 进去,否则对 result 最后一个的右边界进行扩增

class Solution {
public:
    static bool cmp(vector<int> a, vector<int> b){
        if(a[0] != b[0]) return a[0] < b[0];
        return a[1] < b[1];
    }
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(), cmp);
        vector<vector<int>> res;
        res.push_back(intervals[0]);
        for(int i=1;i<intervals.size();i++)
        {
            auto cur = intervals[i];
            auto last = res[res.size() - 1];
            if(cur[0] > last[1]){
                res.push_back(cur);
            }else{
                res[res.size()-1][1] = max(last[1], cur[1]);
            }
        }
        return res;
    }
};

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

https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array

upper_bound 和 lower_bound 函数的实现

要注意两点,第一个是注意返回的 idx 的合法性,第二个是 upper_bound 返回的 index 需要-1

class Solution {
public:
    int lower_bound(vector<int>& nums, int target){
        int left = 0;
        int right = nums.size();
        while(left<right)
        {
            int mid = left + (right-left)/2;
            if(target>nums[mid]){
                // (mid, right)
                left = mid+1;
            }else if(target==nums[mid]){
                right = mid;
            }else{
                 //
                 // target < nums[mid]
                 right = mid;
            }
        }
        return left;
    }
    int upper_bound(vector<int>& nums, int target){
        int left = 0;
        int right = nums.size();
        while(left < right){
            int mid = left+(right-left)/2;
            if(target>nums[mid]){
                // ()
                left = mid+1;
            }else if(target == nums[mid]){
                left = mid+1;
            }else{
                // target < nums[mid]
                right = mid;
            }
        }
        return left;
    }
    vector<int> searchRange(vector<int>& nums, int target) {
        int upper = upper_bound(nums, target);
        int lower = lower_bound(nums, target);
        vector<int> res{-1, -1};
        if(lower >=0 && lower<nums.size() && nums[lower] == target) res[0] = lower;
        if(upper-1 >= 0 && upper-1<nums.size() && nums[upper-1] == target) res[1] = upper-1;
        return res;
    }
};

!39 组合总和

https://leetcode.cn/problems/combination-sum/submissions/

经典题目,回溯剪枝,当前节点 选择还是不选择? 选择的话:

cur_vec.push_back(candidates[cur_idx]);
dfs(candidates, cur_val+candidates[cur_idx], cur_idx, target, cur_vec);
cur_vec.pop_back();

如果不选择

dfs(candidates, cur_val, cur_idx+1, target, cur_vec);
class Solution {
private:
    vector<vector<int>> res;
public:
    void dfs(vector<int>& candidates, int cur_val, int cur_idx, int target, vector<int>& cur_vec){
        if(cur_val == target){
            res.push_back(cur_vec);
            return;
        }
        if(cur_val > target || cur_idx>=candidates.size()){
            return;
        }
        // 选择当前路径
        cur_vec.push_back(candidates[cur_idx]);
        dfs(candidates, cur_val+candidates[cur_idx], cur_idx, target, cur_vec);
        cur_vec.pop_back();
        // 不选择当前路径
        dfs(candidates, cur_val, cur_idx+1, target, cur_vec);

    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<int> cur_vec;
        dfs(candidates, 0, 0, target, cur_vec);
        return res;
    }
};

42 接雨水

https://leetcode.cn/problems/trapping-rain-water/

维护一个 left_max 和 right_max


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

46 全排列

https://leetcode.cn/problems/permutations

回溯,visit 数组

class Solution {
private:
    vector<vector<int>> res;
    vector<bool> visit;
public:
    void dfs(vector<int> & nums, vector<int>& cur_vec){
        if(cur_vec.size() == nums.size()){
            res.push_back(cur_vec);
            return;
        }
        for(int i=0;i<nums.size();i++)
        {
            if(visit[i] == false)
            {
                visit[i] = true;
                cur_vec.push_back(nums[i]);
                dfs(nums, cur_vec);
                cur_vec.pop_back();
                visit[i] = false;
            }
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        visit = vector<bool> (nums.size(), false);
        vector<int> cur_vec;

        dfs(nums, cur_vec);

        return res;
    }
};

48 旋转图像

https://leetcode.cn/problems/rotate-image/

先进行轴翻转,然后行 reverse,在轴翻转的过程中注意 i 的范围是(0, n), j 的范围是(i, m),否则的话翻过来再翻过去等于没翻

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        int n = matrix.size();
        int m = matrix[0].size();
        for(int i=0;i<n;i++)
        {
            for(int j=i;j<m;j++)
            {
                swap(matrix[i][j], matrix[j][i]);
            }
        }
        for(auto& row: matrix)
        {
            reverse(row.begin(), row.end());
        }
    }
};

53 最大子数组和

https://leetcode.cn/problems/maximum-subarray/submissions/

动态规划

class Solution {
public:
    int maxSubArray(vector<int>& nums) {
         int ans = nums[0];
         int cur = nums[0];
         for(int i=1;i<nums.size();i++){
             cur = max(nums[i], cur+nums[i]);
             ans = max(ans, cur);
         }
         return ans;
    }
};

49 分母异位词

https://leetcode.cn/problems/group-anagrams/?

利用 dict 和 sort 函数

class Solution {
private:
    vector<vector<string>> res;
    unordered_map<string, vector<string>> cache;
    vector<bool> visit;
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        for(auto& str: strs){
            auto reve = str;
            sort(reve.begin(), reve.end());
            cache[reve].push_back(str);
        }
        for(auto it=cache.begin();it!=cache.end();it++){
            res.push_back(it->second);
        }
        return res;
    }
};

不同路径

https://leetcode.cn/problems/unique-paths/

动态规划


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

64 最小路径和

动态规划

https://leetcode.cn/problems/minimum-path-sum/

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

70 爬楼梯

动态规划

https://leetcode.cn/problems/climbing-stairs/

class Solution {
public:
    int climbStairs(int n) {
        int prev = 1;
        int cur = 2;
        if(n==1) return 1;
        if(n==2) return 2;
        for(int i=3;i<=n;i++){
            int tp = cur;
            cur = prev+cur;
            prev = tp;
        }
        return cur;
    }
};

72 编辑距离

https://leetcode.cn/problems/edit-distance/

class Solution {
private:
    vector<vector<int>> f;
public:
    int dfs(string& word1, string& word2, int i, int j){
        if(i==0){
            return j;
        }
        if(j==0){
            return i;
        }
        if(word1[i-1] == word2[j-1]){
            if(f[i-1][j-1] == -1){
                f[i][j] = dfs(word1, word2, i-1, j-1);
            }else{
                f[i][j] = f[i-1][j-1];
            }
            return f[i][j];
        }
        int delete_ , insert_, replace_;
        delete_ =  f[i-1][j] != -1 ? f[i-1][j]+1 :  dfs(word1, word2, i-1, j)+1;
        insert_ =  f[i][j-1] != -1  ? f[i][j-1]+1 :  dfs(word1, word2, i, j-1)+1;
        replace_ = f[i-1][j-1] != -1  ? f[i-1][j-1]+1 : dfs(word1, word2, i-1, j-1)+1;
        f[i][j] = min(min(delete_, insert_), replace_);
        return f[i][j];
    }
    int minDistance(string word1, string word2) {
        f = vector<vector<int>> (word1.size()+1, vector<int>(word2.size()+1, -1));
        return dfs(word1, word2, word1.size(), word2.size());
    }
};

75 颜色分类

https://leetcode.cn/problems/sort-colors/

三指针

本质就是三个指针,头指针和中指针负责 0 和 1 的交换,中指针和尾指针负责把 2 移到末

class Solution {
public:
    void sortColors(vector<int>& nums) {
        // 三指针
        int red_idx = 0;
        int blue_idx = nums.size()-1;
        int white_idx = 0;
        while(white_idx <= blue_idx){
           if(nums[white_idx] == 1){
               white_idx++;
           }else if(nums[white_idx] == 0){
               swap(nums[white_idx], nums[red_idx]);
               red_idx++;
               white_idx++;
           }else{
               swap(nums[blue_idx], nums[white_idx]);
               blue_idx--;
           }
        }
    }
};

78 子集

https://leetcode.cn/problems/subsets

回溯, 两条路,选或者不选

class Solution {
private:
    vector<vector<int>> res;
public:
    void dfs(vector<int>& nums, vector<int>& cur, int idx){
        if(idx>=nums.size()){
            res.push_back(cur);
            return;
        }
        dfs(nums, cur, idx+1);
        cur.push_back(nums[idx]);
        dfs(nums, cur, idx+1);
        cur.pop_back();
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        vector<int> cur;
        dfs(nums, cur, 0);
        return res;
    }
};

84. 柱状图中的最大矩形

https://leetcode.cn/problems/largest-rectangle-in-histogram

单调栈

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        vector<int> left_min(heights.size(), -1);
        vector<int> right_min(heights.size(), heights.size());
        stack<int> stk;
        for(int i=0;i<heights.size();i++)
        {
            while(!stk.empty() && heights[i] < heights[stk.top()]){
                right_min[stk.top()] = i;
                stk.pop();
            }
            stk.push(i);
        }
        stk = stack<int>{};
        for(int i=heights.size()-1;i>=0;i--)
        {
            while(!stk.empty() && heights[i] < heights[stk.top()]){
                left_min[stk.top()] = i;
                stk.pop();
            }
            stk.push(i);
        }
        int res = 0;

        for(int i=0;i<heights.size();i++)
        {
            int left = left_min[i];
            int right = right_min[i];
            res = max(res, (right-left-1)* heights[i]);
        }
        return res;
    }

};

85 最大矩形

https://leetcode.cn/problems/maximal-rectangle

! 注意 matrix 是字符的情况,转换成 int 的 matrix 的时候一定要注意先决条件 matrix[i][j] == '0', 还有要注意行和列

class Solution {

public:
    int maximalRectangle(vector<vector<char>>& matrix) {
        int n = matrix.size();
        int m = matrix[0].size();
        int res = 0;
        vector<vector<int>> int_matrix(n, vector<int>(m, 0));
        for(int i=0;i<n;i++)
        {
            int_matrix[i][0] = matrix[i][0] == '0' ? 0 : 1;
        }

        for(int i=0;i<n;i++)
        {
            for(int j=1;j<m;j++)
            {
                if(matrix[i][j] == '1')
                int_matrix[i][j] = int_matrix[i][j-1] + 1;
            }
        }
        for(int j=0;j<m;j++)
        {
            vector<int> down_min(n, n);
            vector<int> up_min(n, -1);
            stack<int> stk;
            for(int i=0;i<n;i++)
            {
                while(!stk.empty() && int_matrix[stk.top()][j] > int_matrix[i][j]){
                    down_min[stk.top()] = i;
                    stk.pop();
                }
                stk.push(i);
            }
            stk = stack<int>{};
            for(int i=n-1;i>=0;i--)
            {
                while(!stk.empty() && int_matrix[stk.top()][j] > int_matrix[i][j]){
                    up_min[stk.top()] = i;
                    stk.pop();
                }
                stk.push(i);
            }
            for(int i=0;i<n;i++)
            {
                res = max(res, int_matrix[i][j] * (down_min[i]-up_min[i]-1));
            }
        }
        return res;
    }
};

94 二叉树的中序遍历

https://leetcode.cn/problems/binary-tree-inorder-traversal/

掌握非递归方法

class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> ans;
        stack<TreeNode*> stk;
        auto cur = root;
        while(cur || !stk.empty()){
            while(cur){
                stk.push(cur);
                cur = cur->left;
            }
            auto top = stk.top();
            stk.pop();
            ans.push_back(top->val);
            if(top->right){
                cur = top->right;
            }
           
        }
    
        return ans;
    }
};


不同的二叉搜索树

一共i个点,左边j的范围[0, i-1],右边[i-1-j]

https://leetcode.cn/problems/unique-binary-search-trees

class Solution {
public:
    int numTrees(int n) {
        vector<int> g(n+1);
        if(n == 1) return 1;
        g[1] = 1;
        g[2] = 2;
        g[0] = 1;
        for(int i=3;i<=n;i++)
        {
            for(int j=1;j<=i;j++)
            {
                g[i] += g[j-1] * g[i-j];
            }
        }
        return g[n];
    }
};

98 验证二叉搜索树

https://leetcode.cn/problems/validate-binary-search-tree/

利用二叉搜索树中序遍历的性质

class Solution {
private:
    bool res = true;
    int cur = INT_MIN;
    bool init = false;
public:
    void dfs(TreeNode* root){
        if(root==nullptr) return;
        dfs(root->left);
        if(init == false){
            init = true;

        }else{
            if(root->val <= cur){
                res = false;
            }
        }
        cur = root->val;
        dfs(root->right);
    }
    bool isValidBST(TreeNode* root) {
        dfs(root);
        return res;
    }
};

101 对称二叉树

https://leetcode.cn/problems/symmetric-tree 递归

class Solution {
public:
    bool dfs(TreeNode* left, TreeNode* right){
        if(left==nullptr && right == nullptr) return true;
        if(left==nullptr || right == nullptr) return false;
        if(left->val != right->val) return false;
        return dfs(left->right, right->left) && dfs(left->left, right->right);
    }
    bool isSymmetric(TreeNode* root) {
        if(root==nullptr) return true;
        return dfs(root->left, root->right);
    }
};

102 二叉树的层序遍历

https://leetcode.cn/problems/binary-tree-level-order-traversal

队列

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        queue<TreeNode*> q;
        q.push(root);
        vector<vector<int>> res;
        if(root==nullptr) return res;
        while(!q.empty())
        {
            int sz = q.size();
            vector<int> cur;
            while(sz--){
                auto top = q.front();
                q.pop();
                cur.push_back(top->val);
                if(top->left){
                    q.push(top->left);
                }
                if(top->right){
                    q.push(top->right);
                }
            }
            res.push_back(cur);
        }
        return res;
    }
};

104 二叉树的最大深度

https://leetcode.cn/problems/maximum-depth-of-binary-tree/

class Solution {
public:
    int maxDepth(TreeNode* root) {
        if(root==nullptr) return 0;
        int left = maxDepth(root->left);
        int right = maxDepth(root->right);
        return 1+max(left, right);
    }
};

105 前序中序构造二叉树

https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/

class Solution {
public:
    TreeNode* build(vector<int>& preorder, int p_l, int p_r, vector<int>& inorder, int i_l, int i_r)
    {
        if(p_l > p_r) return nullptr;
        int root_val = preorder[p_l];
        int i;
        for(i=i_l;i<=i_r;i++)
        {
            if(inorder[i] == root_val){
                break;
            }
        }
        int left_num = i-i_l;
        auto root = new TreeNode(root_val);
        root->left = build(preorder, p_l+1, p_l+left_num, inorder, i_l, i-1);
        root->right = build(preorder, p_l+left_num+1, p_r, inorder, i+1, i_r);
        return root;
    }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        return build(preorder, 0, preorder.size()-1, inorder, 0, inorder.size()-1);
    }
};

114 二叉树展开为链表

https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/

递归解决

class Solution {
public:
    TreeNode* dfs(TreeNode* root){
        if(root == nullptr){
            return root;
        }
        auto left = dfs(root->left);
        auto right = dfs(root->right);
        if(left == nullptr){
            root->right = right;
            root->left = nullptr;
            return root;
        }
        auto cur = left;
        while(cur->right){
            cur = cur->right;
        }
        root->right = left;
        root->left = nullptr;
        cur->right = right;
        return root;
    }
    void flatten(TreeNode* root) {
        dfs(root);
    }
};

124 二叉树的最大路径和

https://leetcode.cn/problems/binary-tree-maximum-path-sum

递归

class Solution {
private:
    int ans = INT_MIN;
public:
    int dfs(TreeNode* root){
        if(!root) return 0;
        int left = dfs(root->left);
        int right = dfs(root->right);
        int val = root->val + max(left, 0) + max(right, 0);
        ans = max(ans, val);
        return root->val + max(0, max(left, right));
    }
    int maxPathSum(TreeNode* root) {
        dfs(root);
        return ans;
    }
};

128 最长连续序列

https://leetcode.cn/problems/longest-consecutive-sequence/

用 map 或者 set(有序的), 并且用 iterator

class Solution {
private:
    set<int> cache;
    int res = 1;
public:
    int longestConsecutive(vector<int>& nums) {
        for(int x: nums){
            cache.insert(x);
        }
        int res = 0;
        auto it =  cache.begin();
        int streak = 1;
        while(it!=cache.end())
        {
            streak = 1;
            while(it!=cache.end() &&cache.count(*it+1)!=0){
                it++;
                streak++;
            }
            res = max(res, streak);
            it++;
        }
        return res;
    }
};

136 只出现一次的数字

https://leetcode.cn/problems/single-number/

利用亦或

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int x = 0;
        for(int num: nums){
            x = x^num;
        }
        return x;
    }
};

139 单词拆分

https://leetcode.cn/problems/word-break

动态规划

class Solution {
private:
    vector<bool> dp;
public:
    bool wordBreak(string s, vector<string>& wordDict) {
        dp = vector<bool>(s.size()+1, false);
        dp[0] = true;
        for(int i=1;i<=s.size();i++)
        {
            if(dp[i-1] == true){
                for(string& word: wordDict){
                    bool match = true;
                    for(int j=0;j<word.size();j++)
                    {
                        if(word[j] != s[i-1+j]){
                            match = false;
                            break;
                        }
                    }
                    if(match == true){
                        dp[i+word.size()-1] = true;
                    }
                }
            }
        }
        return dp[s.size()];
    }
};

剑指 Offer 12. 矩阵中的路径

https://leetcode.cn/problems/ju-zhen-zhong-de-lu-jing-lcof/

用 visit 数组

class Solution {
private:
    vector<vector<bool>> visit;
    int n;
    int m;
    bool res =false;
    vector<vector<int>> directions{{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
public:
    void dfs(vector<vector<char>>& board, string& word, int i, int j, int cur){
        if(i<0 || i>=n || j<0 || j>=m || visit[i][j] || res==true || word[cur]!= board[i][j]){
            return;
        }
        if(cur==word.size()-1){
            res = true;
            return;
        }
        visit[i][j] = true;
        for(auto& direction: directions){
            int next_i = i + direction[0];
            int next_j = j + direction[1];
            dfs(board, word, next_i, next_j, cur+1);
        }
        visit[i][j] = false;
    }
    bool exist(vector<vector<char>>& board, string word) {
        n = board.size();
        if(n==0) return false;
        m = board[0].size();
        visit = vector<vector<bool>>(n, vector<bool>(m, false));
        for(int i=0;i<n && !res; i++)
        {
            for(int j=0;j<m && !res ;j++){
                dfs(board, word, i, j, 0);
            }
        }
        return res;
    }
};

剑指 Offer 14- I. 剪绳子

https://leetcode.cn/problems/jian-sheng-zi-lcof/

动态规划,切出来的部分分为两种情况:
一种继续切割,另一种一整块,不切割

class Solution {
public:
    int cuttingRope(int n) {
        vector<int> f(1005);
        f[1] = 1;
        for(int i=2;i<=n;i++){
            for(int j=1;j<=i/2;j++){
                int x1 = j;
                int x2 = i-j;
                f[i] = max(f[i], f[x1]*x2);
                f[i] = max(f[i], x1*x2);

            }
        }
        return f[n];
    }
};

剑指 Offer 16. 数值的整数次方

https://leetcode.cn/problems/shu-zhi-de-zheng-shu-ci-fang-lcof/

递归的方式(其实是二分)

class Solution {
public:
    double fun(double x, int n){
        if(n==0){
            return 1;
        }
        double half = fun(x, n/2);
        return n%2==0? half*half : x*half*half;
    }
    double myPow(double x, int n) {
        long long N = n;
        return n>0 ? fun(x, N) : 1.0/fun(x, -N);
    }
};

剑指 Offer 26. 树的子结构

https://leetcode.cn/problems/shu-de-zi-jie-gou-lcof/

递归

class Solution {

public:
    bool dfs(TreeNode* A, TreeNode* B){
        if(!B) return true;
        if(!A) return false;
        bool left = dfs(A->left, B->left);
        bool right = dfs(A->right, B->right);
        return A->val == B->val && left && right;
    }
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        if(!A || !B) return false;
        return dfs(A, B) || isSubStructure(A->left, B) || isSubStructure(A->right, B);
    }
};

剑指 Offer 31. 栈的压入、弹出序列

https://leetcode.cn/problems/zhan-de-ya-ru-dan-chu-xu-lie-lcof/

class Solution {
public:
    bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
        stack<int> st;
        int idx = 0;
        for(int i=0;i<pushed.size();i++){
            st.push(pushed[i]);
            while(!st.empty() && st.top() == popped[idx]){
                idx++;
                st.pop();
            }
        }
        return st.empty();
    }
};

剑指 Offer 33. 二叉搜索树的后序遍历序列

https://leetcode.cn/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/

遍历,veriy(vector<int>& postorder, int begin, int end), i 从 begin 到 root 是小于, begin+1,到 end 是小于,最后判断 i 是否等于 end。

class Solution {
public:
    bool verify(vector<int>& postorder, int l, int r){
        if(l>=r) return true;
        int root_val = postorder[r];
        int i = l;
        while(i<r && postorder[i]<root_val) i++;
        int tp = i;
        while(i<r && postorder[i]>root_val) i++;
        return i == r && verify(postorder, l, tp-1) && verify(postorder, tp, r-1);
    }
    bool verifyPostorder(vector<int>& postorder) {
        return verify(postorder, 0, postorder.size()-1);
    }
};

剑指 Offer 34. 二叉树中和为某一值的路径

dfs

https://leetcode.cn/problems/er-cha-shu-zhong-he-wei-mou-yi-zhi-de-lu-jing-lcof

class Solution {
private:
    vector<vector<int>> ans;
public:
    void dfs(TreeNode* root, int target, int cur, vector<int>& cur_vec){
        if(!root) return;
        cur_vec.push_back(root->val);
        if(root->left==nullptr && root->right == nullptr && cur+root->val == target){
            ans.push_back(cur_vec);
        }else{
            if(root->left)
            dfs(root->left, target, cur+root->val, cur_vec);
            if(root->right)
            dfs(root->right, target, cur+root->val, cur_vec);
        }
        cur_vec.pop_back();
    }
    vector<vector<int>> pathSum(TreeNode* root, int target) {
        vector<int> cur_vec;
        dfs(root, target, 0, cur_vec);
        return ans;
    }
};

剑指 Offer 35. 复杂链表的复制

https://leetcode.cn/problems/fu-za-lian-biao-de-fu-zhi-lcof/

用一个 unordered_map 存储对应的节点,然后利用先序遍历,当遍历完成之后,map 也做好了

class Solution {
private:
    unordered_map<Node*, Node*> cache;
public:
    Node* copyRandomList(Node* head) {
        if(head == nullptr) return nullptr;
        auto root = new Node(head->val);
        cache[head] = root;
        root->next = copyRandomList(head->next);
        root->random = cache[head->random];
        return root;
    }
};

剑指 Offer 36. 二叉搜索树与双向链表

https://leetcode.cn/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof/

head 和 prev

class Solution {
private:   
    Node* prev = nullptr;
    Node* head = nullptr;
public:
    void dfs(Node* root){
        if(!root){
            return ;
        }
        dfs(root->left);
        if(head==nullptr){
            head = root;
        }
        auto right = root->right;
        if(prev){
            prev->right = root;
        }
        root->left = prev;
        prev = root;
        dfs(right);
    }
    Node* treeToDoublyList(Node* root) {
        if(!root) return nullptr;
        dfs(root);
        head->left = prev;
        prev->right = head;
        return head;
    }
};

剑指 Offer 38. 字符串的排列

在 dfs 的时候去重

https://leetcode.cn/problems/zi-fu-chuan-de-pai-lie-lcof/

if(i!=0 && s[i]==s[i-1] && visit[i-1]==true) continue;

class Solution {
private:
    vector<string> ans;
    vector<bool> visit;
public:
    void dfs(const string& s, string& cur){
        if(cur.size() == s.size()){
            ans.push_back(cur);
            return;
        }
        for(int i=0;i<s.size();i++){
            if(i!=0 && s[i] == s[i-1] && visit[i-1]) continue;
            if(visit[i] == false){
                visit[i] = true;
                cur.push_back(s[i]);
                dfs(s, cur);
                cur.pop_back();
                visit[i] = false;
            }
        }
    }
    vector<string> permutation(string s) {
        sort(s.begin(), s.end());
        visit = vector<bool>(s.size(), false);
        string cur = "";
        dfs(s, cur);
        return ans;
    }
};
# 剑指 Offer 46. 把数字翻译成字符串

动态规划
如果`str[i-1]+str[i]` 在 10 到 25 之间的数字

$$
f_{i} = f_{i-1} + f_{i-2}
$$

否则

$$
f_{i} = f_{i-1}
$$

```c++
class Solution {
public:
    int translateNum(int num) {
        string str = to_string(num);
        vector<int> dp(str.size()+1);
        dp[1] = 1;
        dp[0] = 1;
        for(int i=2;i<=str.size();i++){
            int x = (str[i-2] - '0') * 10 + (str[i-1] - '0');
            if(x>=10 && x<=25){
                dp[i] = dp[i-2]+dp[i-1];
            }else{
                dp[i] = dp[i-1];
            }
        }

        return dp[str.size()];
    }
};

剑指 Offer 47. 礼物的最大价值

https://leetcode.cn/problems/li-wu-de-zui-da-jie-zhi-lcof/

动态规划

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

剑指 Offer 49. 丑数

https://leetcode.cn/problems/chou-shu-lcof/

类似于合并三个有序链表

class Solution {
public:
    int nthUglyNumber(int n) {
        vector<int> f(n, 0);
        f[0] = 1;
        int i2 = 0;
        int i3 = 0;
        int i5 = 0;
        for(int i=1;i<n;i++){
            int x2 = f[i2]*2;
            int x3 = f[i3]*3;
            int x5 = f[i5]*5;
            int min_val = min(min(x2, x3), x5);
            f[i] = min_val;
            if(x2 == min_val){
                i2++;
            }
            if(x3 == min_val){
                i3++;
            }
            if(x5 == min_val){
                i5++;
            }
        }
        return f[n-1];
    }
};

剑指 Offer 56 - I. 数组中数字出现的次数

https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof

首先将所有数字进行异或操作,得到 x, x 中找到那个为 1 的位,根据异或为 1 的那个位将数字分为两组

class Solution {
public:
    vector<int> singleNumbers(vector<int>& nums) {
        int x = 0;
        for(auto i:nums){
            x = (x^i);
        }
        int d = 1;
        while((d&x) != 0){
            d = d<<1;
        }
        vector<int> ans{0, 0};
        for(int i=0;i<nums.size();i++){
            if((nums[i] & d) == 0){
                ans[0] = ans[0] ^ nums[i];
            }else{
                ans[1] = ans[1] ^ nums[i];
            }
        }
        return ans;
    }
};

剑指 Offer 56 - II. 数组中数字出现的次数 II

https://leetcode.cn/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-ii-lcof/

题目大概意思是有一个数只出现过 1 次,其他数都出现过 3 次。
解法是每个位上的 1 进行统计,然后对三取余,如果是 1 的话说明只出现过一次的数这个位上有 1

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        vector<int> bits(32, 0);
        for(int num: nums){
            int x = 1;
            int i = 0;
            while(num){
                bits[i] += num & x;
                num = num >> 1;
                i++;
            }
        }
        int x = 0;
        int ans = 0;
        for(int i=0;i<32;i++){
            ans += bits[i]%3 * pow(2, x);
            x++;
        }
        return ans;
    }
};

剑指 Offer 60. n 个骰子的点数

逆向推导

image-1680526735131

剑指 Offer 64. 求 1+2+…+n

后序遍历

class Solution {
private:
    int x = 0;
public:
    int sumNums(int n) {
        int a = n>=1 && sumNums(n-1);
        x += n;
        return x;
    }
};

剑指 Offer 66. 构建乘积数组

https://leetcode.cn/problems/gou-jian-cheng-ji-shu-zu-lcof

构建从左边和从右边的累计数组

class Solution {
public:
    vector<int> constructArr(vector<int>& a) {
        vector<int> left(a.size(), 1);
        for(int i=1;i<a.size();i++){
            left[i] = a[i-1]*left[i-1];
        }
        vector<int> right(a.size(), 1);
        for(int i=a.size()-2;i>=0;i--){
            right[i] = a[i+1]*right[i+1];
        }
        vector<int> res(a.size(), 1);
        for(int i=0;i<a.size();i++){
            res[i] = left[i]*right[i];
        }
        return res;
    }
};

面试题 13. 机器人的运动范围

https://leetcode.cn/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof/

class Solution {
public:
    vector<int> constructArr(vector<int>& a) {
        vector<int> left(a.size(), 1);
        for(int i=1;i<a.size();i++){
            left[i] = a[i-1]*left[i-1];
        }
        vector<int> right(a.size(), 1);
        for(int i=a.size()-2;i>=0;i--){
            right[i] = a[i+1]*right[i+1];
        }
        vector<int> res(a.size(), 1);
        for(int i=0;i<a.size();i++){
            res[i] = left[i]*right[i];
        }
        return res;
    }
};

面试题 45. 把数组排成最小的数

对数字进行排序

string ab = a+b;
string ba = b+a;
return ab<ba;
class Solution {
public:
    static bool cmp(string a, string b){
        string ab = a+b;
        string ba = b+a;
        return ab<ba;
    }
    string minNumber(vector<int>& nums) {
        vector<string> str_num;
        for(int i=0;i<nums.size();i++){
            str_num.push_back(to_string(nums[i]));
        }
        sort(str_num.begin(), str_num.end(), cmp);
        string res = "";
        for(int i=0;i<str_num.size();i++){
            res = res + str_num[i];
        }
        return res;
    }
};

!面试题 59 - II. 队列的最大值

一个 queue 还有一个 deque,queue push_back 的时候,将 max deque 中 back 中小于等于 value 的都 pop 走,在 pop_front 的时候,将 max deque 中 front 中等于 pop 出来的 value 的 pop 出去,总体维护的队列 back 到 front 的数据是有序增大的

class MaxQueue {
private:
    queue<int> q;
    deque<int> max_q;
public:
    MaxQueue() {

    }

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

    int pop_front() {
        if(q.empty()){
            return -1;
        }
        int ans = q.front();
        q.pop();
        while(!max_q.empty() && ans == max_q.front()){
            max_q.pop_front();
        }
        return ans;
    }
};

剑指 Offer 19. 正则表达式匹配

https://leetcode.cn/problems/zheng-ze-biao-da-shi-pi-pei-lcof
p[i-1] == '*'的时候,总是可以删除一个前一个字符,即
dp[i][j] |= dp[i][j-2];,同时,如果如果当s[i-1] == p[j-2] || p[j-2] == '.'时,匹配
s 末尾的一个字符,将该字符扔掉,而该组合还可以继续进行匹配;

class Solution {
public:
    bool isMatch(string s, string p) {
        vector<vector<int>> dp(s.size()+1, vector<int>(p.size()+1, false));
        dp[0][0] = true;
        // 初始化首行 空串必须匹配 成对带* 的
        for(int i = 2; i <=p.size(); i += 2){
            dp[0][i] = (p[i-1] == '*' && dp[0][i-2]);
        }
        if(s.empty()) return dp[0][p.size()];

        for(int i=1;i<=s.size();i++){
            for(int j=1;j<=p.size();j++){
                if(s[i-1]==p[j-1] || p[j-1] == '.'){
                    dp[i][j] = dp[i-1][j-1];
                }else if(p[j - 1] == '*' && j>=2){
                    if(s[i-1] == p[j-2] || p[j-2] == '.'){
                        dp[i][j] = dp[i-1][j];
                    }
                    dp[i][j] |= dp[i][j-2];
                }

            }
        }
        return dp[s.size()][p.size()];
    }
};

剑指 Offer 51. 数组中的逆序对

https://leetcode.cn/problems/shu-zu-zhong-de-ni-xu-dui-lcof
利用 merge_sort

class Solution {
private:
    int ans = 0;
public:
    void merge(vector<int>& nums, int left, int mid, int right){
        vector<int> tp1(mid-left+1);
        vector<int> tp2(right-(mid+1)+1);
        for(int i=left;i<=mid;i++){
            tp1[i-left] = nums[i];
        }
        for(int i=mid+1;i<=right;i++){
            tp2[i-mid-1] = nums[i];
        }
        int i = left;
        int i1 = 0;
        int i2 = 0;
        while(i1<tp1.size() && i2<tp2.size()){
            if(tp1[i1] <= tp2[i2]){
                nums[i++] = tp1[i1++];
            }else{
                ans += (tp1.size()-i1);
                nums[i++] = tp2[i2++];
            }
        }
        while(i1<tp1.size()){
            nums[i++] = tp1[i1++];
        }
        while(i2<tp2.size()){
            nums[i++] = tp2[i2++];
        }
    }
    void merge_sort(vector<int>& nums, int left, int right){
        if(left>=right) return;
        int mid = left+(right-left)/2;
        merge_sort(nums, left, mid);
        merge_sort(nums, mid+1, right);
        merge(nums, left, mid, right);
    }

    int reversePairs(vector<int>& nums) {
        merge_sort(nums, 0, nums.size()-1);
        for(int i=0;i<nums.size();i++){
            printf("%d", nums[i]);
        }
        return ans;
    }
};

剑指 Offer 41. 数据流中的中位数

https://leetcode.cn/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof/

class MedianFinder {
private:
    priority_queue<int, vector<int>, greater<int>> min_q;
    priority_queue<int, vector<int>, less<int>> max_q;
public:
    /** initialize your data structure here. */
    MedianFinder() {

    }

    void addNum(int num) {
        max_q.push(num);
        int tp = max_q.top();
        max_q.pop();
        min_q.push(tp);

        if(max_q.size() != min_q.size()){
            int tp = min_q.top();
            min_q.pop();
            max_q.push(tp);
        }
    }

    double findMedian() {
        if(min_q.size() == max_q.size()){
            return (1.0 * min_q.top() + max_q.top()) / 2;
        }
        return max_q.top();
    }
};

剑指 Offer 43. 1 ~ n 整数中 1 出现的次数

class Solution {
public:
    int countDigitOne(int n) {
        int left, now, right;
        long long k = 1;
        int ans = 0;
        while(left){
            now = (n % (k*10)) / k;
            right = n%k;
            left = n / (k*10);
            if(now == 0){
                // 12033; left:(0-11), right:(0-99);
                ans += left*k;
            }else if(now == 1){
                // 12133  left:(0-11), right(0-99), 当left=left时, right=(0, right)
                ans += left*k + right+ 1;
            }else{
                // 12933  left:(left)
                ans += (left+1)*k;
            }
            k = k*10;
        }
        return ans;
    }
};
posted @ 2025-01-27 10:33  chx9  阅读(24)  评论(0)    收藏  举报