笔试算法

笔试算法题

链表

19. 删除链表的倒数第 N 个节点 【链表】【双指针】

题解

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 定义虚拟头节点 dummy
        ListNode* dummy = new ListNode(-1); 
        dummy->next = head;
        // 定义双指针 P1 P2
        ListNode* p1 = dummy, * p2 = dummy;
        // 让 P1 先走 n+1 步
        for (int i = 0; i <= n; ++i) {
            p2 = p2->next;
        }
        // 再让 P1 P2 同时走
        while (p2 != nullptr) {
            p1 = p1->next;
            p2 = p2->next;
        }
        // 此时 P2 为空,P1 指向倒数第 n+1 个节点,方便删除倒数第 n 个节点
        p1->next = p1->next->next;
        // head 有可能被删除,所以需要虚拟头节点
        return dummy->next;
    }
};

21. 合并两个有序链表 【链表】【双指针】

题解

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        // 定义一个虚拟头节点 dummy,p 标记 dummy 链表的节点位置
        ListNode* dummy = new ListNode(-1), *p = dummy;
        // 定义双指针,分别标记 L1 和 L2 的位置
        ListNode* p1 = list1, * p2 = list2;
        // 将 L1 和 L2 依次比较,较小的接在 dummy 链表之后
        while (p1 != nullptr && p2 != nullptr) {
            // 将 L1 节点放在 dummy 之后
            if (p1->val < p2->val) {
                p->next = p1;
                p = p->next;
                p1 = p1->next;
            } else {    // 将 L2 节点放在 dummy 之后
                p->next = p2;
                p = p->next;
                p2 = p2->next;
            }
        }
        // 如果 L1 L2 中有一个为空,把另一个的所有接在 dummy 后面
        if (p1 == nullptr) {
            p->next = p2;
        }
        if (p2 == nullptr) {
            p->next = p1;
        }
        // 返回合并后的链表
        return dummy->next;
    }
};

23. 合并 K 个有序链表 【链表】【多指针】【优先队列】

题解

class Solution {
public:
    struct comp {
        bool operator()(ListNode* a, ListNode* b) {
            // a > b 才是最小堆,默认是最大堆
            return a->val > b->val;
        }
    };

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        // 定义 dummy 节点,后接排序结果
        ListNode* dummy = new ListNode(-1), * p = dummy;
        // 定义优先队列,且为最小堆
        priority_queue<ListNode*, vector<ListNode*>, comp> pq;
        // 依次取链表,都把第一个节点放进去
        for (auto list : lists) {
            if (list != nullptr) {
                pq.push(list);
            }
        }
        // 如果优先队列不为空,则一直从中取元素
        while (!pq.empty()) {
        	// 这里的 node 是优先队列取出的节点,但也相当于合并两个链表时的指针
        	// 后续需要通过 node 来添加新的节点到 pq
            ListNode* node = pq.top();
            pq.pop();
            p->next = node;
            p = p->next;
            // 如果某条链表不为空,继续添加节点进去
            if (node->next != nullptr) {
                pq.push(node->next);
            }
        }
        // 返回排序后的链表
        return dummy->next;
    }
};

25. K 为一组反转链表 【链表】【递归】【迭代】

题解

## 假设原链表为 1 -> 2 -> 3 -> 4 -> 5 -> null, k = 2
## 可以先试着反转区间 [a, b) 间的节点, 其中 b - a = k
## 那么可以得到 null <- 1(a) <- 2(newHead)
## 递归可以得到 null <- 3(a) <- 4(newHead),1 连接上 4
## 剩余的不足 K 个的节点保持不反转

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) {
		if (head == nullptr) return nullptr;
		// 定义区间 [a, b)
		ListNode* a, * b;
		a = head, b = head;
		for (int i = 0; i < k; ++i) {
			// 如果剩余节点不足 k 个, 返回剩余链表头节点
			if (b == nullptr) return head;
			b = b->next;
		}
		// 根据 a b 区间反转节点
		ListNode* newHead = reverse(a, b);
		a->next = reverseKGroup(b, k);
		return newHead;
    }
    
	// 迭代反转区间 [a, b) 间的节点
	ListNode* reverse(ListNode* a, ListNode* b) {
		// 定义前置, 当前, 后置指针
		ListNode* pre, * cur, * nxt;
		pre = nullptr, cur = a;
		// 开始反转
		while (cur != b) {
			nxt = cur->next;
			cur->next = pre;
			pre = cur;
			cur = nxt;
		}
		// 返回新的头节点
		return pre;
	}
};

81. 分隔链表 【链表】【双指针】

题解

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        // 创建两个链表,分别存放大于小于 x 的节点
        ListNode* dummy1 = new ListNode(-1), *p1 = dummy1;
        ListNode* dummy2 = new ListNode(-1), *p2 = dummy2;
        // 为初始链表赋予一个指针 p
        ListNode* p = head;
        // 开始遍历
        while (p != nullptr) {
            ListNode* tmp = p;
            // 把小于 x 的节点放在 L1
            if (p->val < x) {
                p = p->next;
                p1->next = tmp;
                p1 = p1->next;
                p1->next = nullptr;
            } else {    // 把大于 x 的节点放在 L2
                p = p->next;
                p2->next = tmp;
                p2 = p2->next;
                p2->next = nullptr;
            }
            // 【注意】当 L1 或 L2 后加节点时,要将原来的 next 置空,否则会形成环
        }
        // 将两部分拼接
        p1->next = dummy2->next;
        return dummy1->next;
    }
};

83. 删除排序链表中的重复元素 【链表】【双指针】

题解

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        // 特殊情况
        if (head == nullptr || head->next == nullptr) return head;
        // 定义双指针
        ListNode* p1 = head, * p2 = head->next;
        // 开始迭代
        while (p2 != nullptr) {
            if (p1->val != p2->val) {
                p1->next = p2;
                p1 = p1->next;
            }
            p2 = p2->next;
        }
        // 把剩下的重复的节点给去除了
        p1->next = nullptr;
        return head;
    }
};

92. 反转链表Ⅱ 【链表】【递归】

题解

## 首先得实现反转前 n 个节点
## 那么对于 head 的 [2, 4] 个节点可以转化为 head->next->next 的 [0, 2] 即前 3 个节点

class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int left, int right) {
        if (head == nullptr) return nullptr;
        // 如果满足前 K 条件
        if (left == 1) {
            return reverseK(head, right);
        }
        // 不满足的话, 逼迫其满足
        head->next = reverseBetween(head->next, left - 1, right - 1);
        return head;
    }

    // 记录反转链表的后继节点
    ListNode* suc = nullptr;

    // 递归反转前 k 个节点
    ListNode* reverseK(ListNode* head, int k) {
        // 终止条件, 获取新头节点
        if (k == 1) {
            suc = head->next;
            return head;
        }
        // 递归子任务, 并传递新头节点
        ListNode* newHead = reverseK(head->next, k - 1);
        // 反转节点
        head->next->next = head;
        head->next = suc;
        return newHead;
    }
};

141. 环形链表 【链表】【快慢指针】

题解

class Solution {
public:
    bool hasCycle(ListNode *head) {
        // 如果为空链表, 则无环
        if (head == nullptr) return false;
        // 定义虚拟头节点 dummy
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        // 定义快慢指针 P1 P2
        ListNode* p1 = dummy, * p2 = dummy;
        // P1 每次走一格, P2 每次走两格
        while (p2 != nullptr) {
            p1 = p1->next;
            if (p2->next != nullptr) {
                p2 = p2->next->next;
            } else {
                p2 = p2->next;
            }
            // P1 P2 相遇, 一定有环
            if (p1 == p2) {
                return true;
            }
        }
        // 此时 P2 为空, 一定无环
        return false;
    }
};

142. 环形链表Ⅱ 【链表】【快慢指针】

题解

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        // 空链表, 则无环
        if (head == nullptr) return nullptr;
        // 定义虚拟头节点 dummy
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        // 定义快慢指针 P1 P2
        ListNode* p1 = dummy, * p2 = dummy;
        // 先判断有无环, P1 每次走一格, P2 每次走两格
        while (p2 != nullptr) {
            p1 = p1->next;
            if (p2->next != nullptr) {
                p2 = p2->next->next;
            } else {
                p2 = p2->next;
            }
            // 此时有环
            if (p1 == p2) {
                break;
            }
        }
        // P2 为空, 则无环
        if (p2 == nullptr) {
            return nullptr;
        } else {
            // 接着把 P1 放到起点, P1 P2 同步移动, 相遇位置就是环起点
            // P1 P2 在相遇点之间走过了 K, P2 独自从相遇点绕了一圈,环长为 K 的倍数
            // 相遇点到入环点的距离为 m, 则 P2 到达入环点要 k-m 格, 从 dummy 到 入环点也需要 k-m 格
            p1 = dummy;
            while (p1 != p2) {
                p1 = p1->next;
                p2 = p2->next;
            }
            return p1;
        }
    }
};

160. 相交链表 【链表】【双指针】

题解

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        // 定义双指针 P1 P2
        ListNode* p1 = headA, * p2 = headB;
        // 先让 P1 P2 同步走, 碰到末尾了重复遍历另一头节点
        // 如果相交那么肯定会相遇
        while (p1 != p2) {
            if (p1 != nullptr) {
                p1 = p1->next;
            } else {
                p1 = headB;
            }
            if (p2 != nullptr) {
                p2 = p2->next;
            } else {
                p2 = headA;
            }
        }
        // 此处满足 P1 = P2, 可能为节点, 可也能同为 nullptr
        return p1;
    }
};

206. 反转链表 【链表】【递归】

题解

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == nullptr) return nullptr;
        return reverse(head);
    }

    // 递归反转链表
    ListNode* reverse(ListNode* head) {
        // 终止条件, 目的是获取新的头节点
        if (head->next == nullptr) {
            return head;
        }
        // 递归, 并传递新的根节点
        ListNode* newHead = reverse(head->next);
        // 假设递归子任务处理完毕后, 当前节点需要:
        head->next->next = head;
        // 主要是确保 1 号节点最终指向 nullptr
        head->next = nullptr;
        return newHead;
    }
};

234. 回文链表 【链表】【递归】【双指针】

题解

class Solution {
public:
    // 定义 left 指针, 从左到右遍历
    ListNode* left = nullptr;

    bool isPalindrome(ListNode* head) {
        left = head;
        return traverse(head);
    }

    // 使用递归的后序遍历,从右到左遍历
    bool traverse(ListNode* right) {
        if (right == nullptr) return true;
        bool res = traverse(right->next);
        // 后续遍历, 如果满足条件,则对比下一对节点
        res = res && (left->val == right->val);
        left = left->next;
        return res;
    }
};

876. 链表的中间节点 【链表】【快慢指针】

题解

class Solution {
public:
    ListNode* middleNode(ListNode* head) {
        // 定义虚拟头节点 dummy
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        // 定义双指针 P1 P2
        ListNode* p1 = dummy, * p2 = dummy;
        // P1 每次移动一格, P2 每次移动两格
        while (p2 != nullptr) {
            p1 = p1->next;
            // 因为有 ->next->next 存在, 需要确保 ->next 不为空
            if (p2->next != nullptr) {
                p2 = p2->next->next;
            } else {
                p2 = p2->next;
            }
        }
        // P1 此时指向中间节点
        return p1;
    }
};

数组

1. 两数之和 【数组】【for 循环】

题解

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        // 朴实无华的两次 for 循环
        for (int i = 0; i < nums.size(); ++i) {
            for (int j = i + 1; j < nums.size(); ++j) {
                if (nums[i] + nums[j] == target) {
                    return {i, j};
                }
            }
        }
        return {-1-1};
    }
};

3. 无重复字符的最长字串 【字符串】【滑动窗口】

题解

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        // 滑动窗口
        int left = 0, right = 0;
        int res = 0;
        // 统计
        unordered_map<char, int> window;
        // 开始滑动
        while (right < s.size()) {
            char ch = s[right];
            right++;
            window[ch]++;
            while (window[ch] > 1) {
                char ch = s[left];
                left++;
                window[ch]--;
            }
            res = max(res, right - left);
        }
        return res;
    }
};

5. 最长回文子串 【数组】【中心扩散】【双指针】

题解

// 回文串需要从中间某一个元素, 或两个元素从两边扩散判断
// 针对每个元素, 都尝试求下它的回文长度

class Solution {
public:
    string longestPalindrome(string s) {
        string res = "";
        for (int i = 0; i < s.size(); ++i) {
            string s1 = palindrome(s, i, i);
            string s2 = palindrome(s, i, i + 1);
            if (res.size() < s1.size()) {
                res = s1;
            }
            if (res.size() < s2.size()) {
                res = s2;
            }
        }
        return res;
    }

    // 求字符串 s[n], s[n, n+1] 的最大回文子串
    string palindrome(string s, int left, int right) {
        while (left >= 0 && right < s.size() && s[left] == s[right]) {
                left--;
                right++;
        }
        return s.substr(left + 1, right - left - 1);
    } 
};

26. 删除有序数组中的重复项 【数组】【快慢指针】

题解

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if (nums.size() <= 1) return nums.size();
        // 定义快慢指针
        int p1 = 0, p2 = 1;
        while (p2 < nums.size()) {
            if (nums[p1] != nums[p2]) {
                p1++;
                // 相当于 [0, 0, 1, 1] -> [0, 1, 1, 1]
                // 碰到一个与 p1 不同的值, 将该值填充直到 p1
                nums[p1] = nums[p2];
            }
            p2++;
        }
        // 返回的是长度
        return p1 + 1;
    }
};

27. 移除元素 【数组】【快慢指针】

题解

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        // 特殊情况
        if (nums.size() == 0) return 0;
        // 定义双指针
        int p1 = 0, p2 = 0;
        // 开始迭代
        while (p2 < nums.size()) {
            if (nums[p2] != val) {
                nums[p1] = nums[p2];
                p1++;
            }
            p2++;
        }
        return p1;
    }
};

28. 找出字符串中第一个匹配项的下标 【字符串】【滑动窗口】【哈希映射】

题解

class Solution {
public:
    int strStr(string haystack, string needle) {
        int L = needle.size();
        int R = 256;
        long Q = 13131313;
        long RL = 1;
        for (int i = 0; i < L - 1; ++i) {
            RL = RL * R % Q;
        }
        // 计算字符串的哈希值
        long hashN = 0;
        for (int i = 0; i < needle.size(); ++i) {
            hashN = (hashN * R + int(needle[i])) % Q;
        }
        // 开滑
        int left = 0, right = 0;
        long hashW = 0;
        while (right < haystack.size()) {
            hashW = (hashW * R % Q + int(haystack[right]) % Q) % Q;
            right++;
            // 判断是否满足条件
            if (right - left == L) {
                if (hashW == hashN) {
                    if (haystack.substr(left, right - left) == needle) {
                        return left;
                    }
                }
                // 缩窗口, 因为 hashW - RL*s[left] 可能为负数, +Q 确保不为负
                hashW = (hashW - int(haystack[left]) * RL % Q + Q) % Q;
                left++;
            }
        }
        return -1;
    }
};

34. 在排序数组中查找元素的第一个和最后一个位置 【数组】【二分法】

题解

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int left = find_left(nums, target);
        int right = find_right(nums, target);
        return {left, right};
    }

    // 二分查找 - 左边界
    int find_left(vector<int>& nums, int target) {
        int left = 0, right = nums.size();
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] >= target) {
                right = mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            }
        }
        if (left >= 0 && left < nums.size() && nums[left] == target) {
            return left;
        } else {
            return -1;
        }
    }

    // 二分查找 - 右边界
    int find_right(vector<int>& nums, int target) {
        int left = 0, right = nums.size();
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] <= target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid;
            }
        }
        if (left - 1 >= 0 && left - 1 < nums.size() && nums[left - 1] == target) {
            return left - 1;
        } else {
            return -1;
        }
    }
};

48. 旋转图像 【矩阵】【反转】

题解

class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        // 矩阵的旋转 -> 矩阵的对角反转 + 矩阵的水平翻转
        // 对角反转
        int m = matrix.size(), n = matrix[0].size();
        for (int i = 0; i < m; ++i) {
            for (int j = i + 1; j < n; ++j) {
                int tmp = matrix[i][j];
                matrix[i][j] = matrix[j][i];
                matrix[j][i] = tmp;
            }
        }
        // 水平翻转
        for (int i = 0; i < m; ++i) {
            for (int j = 0; j < n / 2; ++j) {
                // [0 3] [1 2]    // [0 2]
                int tmp = matrix[i][j];
                matrix[i][j] = matrix[i][n - 1 - j];
                matrix[i][n - 1 - j] = tmp;
            }
        }
    }
};

54. 螺旋矩阵 【矩阵】【边界修改】

题解

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        // 设置上下左右边界, 用作 for 循环条件, 缩小边界范围
        int m = matrix.size(), n = matrix[0].size();
        int upper = 0, lower = m - 1, left = 0, right = n - 1;
        // 存放结果
        vector<int> res;
        // 开始遍历数组, 4个方向为一次循环
        while (res.size() < m * n) {
            if (upper <= lower) {
                for (int i = left; i <= right; ++i) {
                    res.push_back(matrix[upper][i]);
                }
                upper++;
            }
            if (left <= right) {
                for (int i = upper; i <= lower; ++i) {
                    res.push_back(matrix[i][right]);
                }
                right--;
            }
            if (upper <= lower) {
                for (int i = right; i >= left; --i) {
                    res.push_back(matrix[lower][i]);
                }
                lower--;
            }
            if (left <= right) {
                for (int i = lower; i >= upper; --i) {
                    res.push_back(matrix[i][left]);
                }
                left++;
            }
        }
        return res;
    }
};

59. 螺旋矩阵Ⅱ 【矩阵】【边界修改】

题解

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        // 创建矩阵
        vector<vector<int>> res(n, vector<int>(n));
        // 已经填充的个数
        int num = 1;
        int upper = 0, lower = n - 1, left = 0, right = n -  1;
        // 开始填充
        while (num <= n * n) {
            if (upper <= lower) {
                for (int i = left; i <= right; ++i) {
                    res[upper][i] = num++;
                }
                upper++;
            }
            if (left <= right) {
                for (int i = upper; i <= lower; ++i) {
                    res[i][right] = num++;
                }
                right--;
            }
            if (upper <= lower) {
                for (int i = right; i >= left; --i) {
                    res[lower][i] = num++;
                }
                lower--;
            }
            if (left <= right) {
                for (int i = lower; i >= upper; --i) {
                    res[i][left] = num++;
                }
                left++;
            }
        }
        return res;
    }
};

76. 最小覆盖字串 【字符串】【滑动窗口】

题解

// 从一个子串中找另一个字串, 想到滑动窗口
// 先移动窗口右边界, 寻找符合条件的解
// 再移动窗口左边界, 寻找最优解

class Solution {
public:
    string minWindow(string s, string t) {
        // 滑动窗口
        int left = 0, right = 0;
        // 寻求字串的起始, 长度
        int start = 0, len = INT_MAX;
        // 统计符合条件的字符数
        int valid  = 0;
        // 统计结果, 窗口, 目标
        unordered_map<char, int> window, need;
        // 给 need 初始化一下
        for (auto ch : t) {
            need[ch]++;
        }
        // 开始滑动窗口
        while (right < s.size()) {
            // 移动右边界
            char ch = s[right];
            right++;
            if (need.count(ch)) {
                window[ch]++;
                if (window[ch] == need[ch]) {
                    valid++;
                }
            }
            // 符合条件时移动左边界
            while (left <= right && valid == need.size()) {
                // 更新最优子串
                if (right - left < len) {
                    start = left;
                    len = right - left;
                }
                // 移动左边界
                char ch = s[left];
                left++;
                if (need.count(ch)) {
                    if (window[ch] == need[ch]) {
                        valid--;
                    }
                    window[ch]--;
                }
            }
        }
        if (len == INT_MAX) {
            return "";
        } else {
            return s.substr(start, len);
        }
    }
};

167. 两数之和Ⅱ 【数组】【双指针】

题解

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        // 定义双指针
        int p1 = 0, p2 = numbers.size() - 1;
        // 二分查找 0, 1, ..., N-1
        while (p1 < p2) {
            int sum = numbers[p1] + numbers[p2];
            if (sum > target) {
                p2--;
            } else if (sum < target) {
                p1++;
            } else {
                return {p1 + 1, p2 + 1};
            }
        }
        return {-1-1};
    }
};

187. 重复的 DNA 序列 【字符串】【滑动窗口】【哈希映射】

题解

class Solution {
public:
    vector<string> findRepeatedDnaSequences(string s) {
        vector<int> nums(s.size());
        for (int i = 0; i < s.size(); ++i) {
            switch (s[i]) {
                case 'A':
                    nums[i] = 0;
                    break;
                case 'G':
                    nums[i] = 1;
                    break;
                case 'C':
                    nums[i] = 2;
                    break;
                case 'T':
                    nums[i] = 3;
                    break;
            }
        }
        // 滑动窗口
        int left = 0, right = 0;
        // 进制, 数字长度
        int R = 4, L = 10, RL = pow(R, L - 1);
        int windowHash = 0;
        // 已经出现的字符串的哈希值
        unordered_set<int> seen; 
        // 记录重复出现的字符串, 用 set 去重
        unordered_set<string> res;
        // 开始滑动窗口
        while (right < nums.size()) {
            windowHash = windowHash * R + nums[right]; 
            right++;
            if (right - left == L) {
                if (seen.count(windowHash)) {
                    res.insert(s.substr(left, right - left));
                } else {
                    seen.insert(windowHash);
                }
                // 缩小窗口, 左边界移动
                windowHash = windowHash - nums[left] * RL;
                left++;
            }
        }
        return vector<string>(res.begin(), res.end());
    }
};

283. 移动零 【数组】【快慢指针】

题解

class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        // 定义双指针
        int p1 = 0, p2 = 0;
        // 开始迭代
        while (p2 < nums.size()) {
            if (nums[p2] != 0) {
                nums[p1] = nums[p2];
                p1++;
            }
            p2++;
        }
        cout << p1 << endl; 
        for (int i = p1; i < nums.size(); ++i) {
            nums[i] = 0;
        }
    }
};

303. 区域和检索 【数组】【前缀和】

题解

class NumArray {
private:
    vector<int> nums;
    vector<int> preSum;
public:
    NumArray(vector<int>& nums) {
    // 构造前缀和数组
        this->nums = nums;
        preSum.resize(nums.size());
        preSum[0] = nums[0];
        for (int i = 1; i < preSum.size(); ++i) {
            preSum[i] = preSum[i - 1] + nums[i];
        }
    }
    
    int sumRange(int left, int right) {
        int sum = preSum[right] - preSum[left] + nums[left];
        return sum;
    }
};

304. 二维区域和检索 【二维数组】【前缀和】

题解

class NumMatrix {
private:
    vector<vector<int>> matrix;
    vector<vector<int>> preSum;
public:
    NumMatrix(vector<vector<int>>& matrix) {
        this->matrix = matrix;
        int m = matrix.size(), n = matrix[0].size();
        preSum = vector<vector<int>>(m + 1, vector<int>(n + 1, 0));
        for (int i = 1; i < m + 1; ++i) {
            for (int j = 1; j < n + 1; ++j) {
                preSum[i][j] = preSum[i - 1][j] + preSum[i][j - 1] - preSum[i - 1][j - 1] + matrix[i - 1][j - 1];
            }
        }
    }
    
    int sumRegion(int row1, int col1, int row2, int col2) {
        if (matrix.size() == 0) return 0;
        int sum = preSum[row2 + 1][col2 + 1] - preSum[row2 + 1][col1] - preSum[row1][col2 + 1] + preSum[row1][col1];
        return sum;
    }
};

316. 去除重复字母 【字符串】【栈】

题解

class Solution {
public:
    string removeDuplicateLetters(string s) {
        // 搞一个栈, 记录放入的不重复元素
        stack<char> stk;
        // 用一个set 统计字符当前的出现次数
        unordered_map<char, int> umap;
        // 用一个 set 统计字符有无出现过
        unordered_set<char> uset;
        for (char c : s) {
            umap[c]++;
        }
        // 开始遍历
        for (char c : s) {
            // 取一个字符, 在统计 map 里更新
            umap[c]--;
            // 如果这个字符在 栈里出现过
            if (uset.count(c)) continue;
            // 没有出现过, 则添加
            // 先与上一个元素的字典序比较
            while (!stk.empty() && stk.top() > c) {
                // 判断后续还有没有这个字符了
                if (umap[stk.top()] == 0) {
                    break;
                } else {
                    // 后续还有, 那就取出来, 之后再添加
                    uset.erase(stk.top());
                    stk.pop();
                }
            }
            // 添加新的元素
            stk.push(c);
            uset.insert(c);
        }
        // 将栈中元素取出并反转
        string res = "";
        while (!stk.empty()) {
            res = stk.top() + res;
            stk.pop();
        }
        return res;
    }
};

344. 反转字符串 【字符串】【双指针】

题解

class Solution {
public:
    void reverseString(vector<char>& s) {
        // 定义双指针
        int p1 = 0, p2 = s.size() - 1;
        while (p1 < p2) {
            char tmp = s[p1];
            s[p1] = s[p2];
            s[p2] = tmp;
            p1++;
            p2--;
        }
    }
};

370. 区间加法 【数组】【差分】

题解

class Solution {
public:
    vector<int> getModifiedArray(int length, vector<vector<int>>& updates) {
        vector<int> diff(length, 0);
        for (auto update : updates) {
            // 进入前
            diff[update[0]] += update[2];
            // 出去时, 判断是否在边界退出
            if (update[1] + 1 < length) {
                diff[update[1] + 1] -= update[2];
            }
        }
        vector<int> res(length, 0);
        res[0] = diff[0];
        for (int i = 1; i < length; ++i) {
            res[i] = res[i - 1] + diff[i];
        }
        return res;
    }
};

380. 0(1) 时间插入、删除和获取随即元素 【数组】【哈希表】【元素交换】

题解

// 为了确保随机返回, 需要用数组来实现
// 为了插入删除复杂度 O(1), 需要先找到元素与尾部元素交换

class RandomizedSet {
private:
    unordered_map<int, int> umap;
    vector<int> nums;
public:
    RandomizedSet() {

    }
    
    bool insert(int val) {
        if (umap.count(val)) {
            return false;
        } else {
            umap[val] = nums.size();
            nums.push_back(val);
            return true;
        }
    }
    
    bool remove(int val) {
        if (!umap.count(val)) {
            return false;
        } else {
            // 待移除元素 和 最后元素的索引
            int s_idx = umap[val], l_idx = nums.size() - 1;
            int tmp = nums[s_idx];
            nums[s_idx] = nums[l_idx];
            nums[l_idx] = tmp;
            // 修改最后元素的索引
            umap[nums[s_idx]] = s_idx;
            nums.pop_back();
            // 移除原有元素的索引
            umap.erase(val);
            return true;
        }
    }
    
    int getRandom() {
        int randIdx = rand() % nums.size();
        return nums[randIdx];
    }
};

410. 分割数组的最大值 【数组】【二分法】

题目

// 这题可以等效为货船那题【1011】 , 分成 k 份货物, 使货船 k 次可以运完

class Solution {
public:
    int splitArray(vector<int>& nums, int k) {
        return find_left(nums, k);
    }

    // 最大和为 x, 那么分成的份数 k 会随之改变
    int func(vector<int>& nums, int x) {
        int k = 0;
        int sum = 0;
        for (int i = 0; i < nums.size(); ++i) {
            sum += nums[i];
            if (sum <= x) continue;
            if (sum > x) {
                k++;
                sum = nums[i];
            }
        }
        if (sum > 0) k++;
        return k;
    }

    // 二分法, 修改 x, k也会改变, 寻找 k 为固定时的左值
    int find_left(vector<int>& nums, int target) {
        int max_num = *max_element(nums.begin(), nums.end());
        int left = max_num, right = max_num * nums.size();
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (func(nums, mid) > target) {
                left = mid + 1;
            } else if (func(nums, mid) <= target) {
                right = mid;
            }
        }
        return left;
    }
};

438. 找出字符串中所有字母异位词 【字符串】【滑动窗口】

题解

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        // 滑动窗口
        int left = 0, right = 0, valid = 0;
        // 存放索引数组
        vector<int> res;
        // 统计
        unordered_map<char, int> window, need;
        for (auto ch : p) {
            need[ch]++;
        }
        // 开始滑动
        while (right < s.size()) {
            char ch = s[right];
            right++;
            if (need.count(ch)) {
                window[ch]++;
                if (window[ch] == need[ch]) {
                    valid++;
                }
            }
            // 左边界
            while (right - left == p.size()) {
                if (valid == need.size()) {
                    res.push_back(left);
                }
                // 左移
                char ch = s[left];
                left++;
                if (need.count(ch)) {
                    if (window[ch] == need[ch]) {
                        valid--;
                    }
                    window[ch]--;
                }
            }
        }
        return res;
    }
};

528. 按权重随机选择 【数组】【前缀和】【二分法】

题解

// 给了几个数, 让我随机挑选一个数的下标, 按值分配概率
// 那么可以搞成前缀和数组, 一个随机数随机的落在该前缀和数组上
// 比如 [1, 2, 3] -> [_1, __3, ___6]
// rand(1) = 1, rand(23) = 2, rand(456) = 3
// 下标的寻找靠二分法寻找

class Solution {
private:
    // 构建前缀和数组
    vector<int> preSum;
public:
    Solution(vector<int>& w) {
        // 初始化前缀和数组
        preSum = vector<int>(w.size() + 1, 0);
        for (int i = 1; i <= w.size(); ++i) {
            preSum[i] = preSum[i - 1] + w[i - 1];
        }
    }
    
    int pickIndex() {
        // 搞个随机数, [1, preSum[-1]]
        int randNum = rand() % preSum[preSum.size() - 1] + 1;
        // 把这个数放入前缀和数组, 应该的下标是多少呢
        return find_left(preSum, randNum) - 1;
    }

    // 二分查找
    int find_left(vector<int>& nums, int target) {
        int left = 0, right = nums.size();
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] >= target) {
                right = mid;
            }
        }
        return left;
    }
};

567. 字符串的排列 【字符串】【滑动窗口】

题解

class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        // 使用哈希表统计出现信息
        unordered_map<char, int> window, need;
        for (auto ch : s1) {
            need[ch]++;
        }
        // 滑动窗口边界
        int left = 0, right = 0;
        int valid = 0;
        // 开始滑动
        while (right < s2.size()) {
            char ch = s2[right];
            right++;
            if (need.count(ch)) {
                window[ch]++;
                if (window[ch] == need[ch]) {
                    valid++;
                }
            }
            // 左边界
            while (right - left == s1.size()) {
                if (valid == need.size()) {
                    return true;
                }
                // 左移
                char ch = s2[left];
                left++;
                if (need.count(ch)) {
                    if (need[ch] == window[ch]) {
                        valid--;
                    }
                    window[ch]--;
                }
            }
        }
        return false;
    }
};

704. 二分查找 【数组】【二分法】

题解

class Solution {
public:
    int search(vector<int>& nums, int target) {
        // 从一个有序的数组中查找元素, 首先想到二分查找
        int left = 0, right = nums.size();
        // 开始二分
        while (left < right) {
            int mid = left + (right - left) / 2;
            // [left, mid), [mid] [mid + 1, right)
            if (nums[mid] > target) {
                right = mid;
            } else if (nums[mid] < target) {
                left = mid + 1;
            } else {
                return mid;
            }
        }
        return -1;
    }
};

710. 黑名单中的随机数 【数组】【哈希映射】

题解

// 使用一个哈希表存放黑名单中的数
// 如果前 wn 个数, 获取的不是黑名单的数, 那么直接返回
// 如果前 wn 个数, 获取的是黑名单的数, 将这个黑名单数映射到一个 wn 后的白名单的数

class Solution {
private:
    int wn;
    unordered_map<int, int> umap;
public:
    Solution(int n, vector<int>& blacklist) {
        // 计算白名单的前 wn 个数
        wn = n - blacklist.size();
        // 把黑名单记录下来方便查询
        for (int num : blacklist) {
            umap[num] = 666;
        }
        int last = n - 1;
        for (int num : blacklist) {
            // 本身不在前 wn 个, 不用管
            if (num >= wn) {
                continue;
            }
            // 如果所处位置在白名单, 则找一个位于最后的白名单的数, 交换
            while (umap.count(last)) {
                last--;
            }
            umap[num] = last;
            last--;
        }
    }
    
    int pick() {
        int randIdx = rand() % wn;
        if (umap.count(randIdx)) {
            return umap[randIdx];
        } else {
            return randIdx;
        }
    }
};

870 优势洗牌 【数组】【优先队列】【双指针】

题解

class Solution {
public:
    struct comp {
        bool operator() (const pair<int, int>& a, const pair<int, int>& b) {
            return a.second < b.second;
        }
    };
    vector<int> advantageCount(vector<int>& nums1, vector<int>& nums2) {
        int n = nums1.size();
        priority_queue<pair<int, int>, vector<pair<int, int>>, comp> pq;
        for (int i = 0; i < n; ++i) {
            pq.push({i, nums2[i]});
        }
        sort(nums1.begin(), nums1.end(), [](int a, int b){return a > b;});
        vector<int> res(n, 0);
        // 双指针
        int left = 0, right = n - 1;
        while (!pq.empty()) {
            auto cur = pq.top();
            pq.pop();
            cout << cur.second << " ";
            if (cur.second < nums1[left]) {
                res[cur.first] = nums1[left];
                left++;
            } else {
                res[cur.first] = nums1[right];
                right--;
            }
        }
        return res;
    }
};

875. 爱吃香蕉的珂珂 【数组】【二分法】

题解

// 有一堆香蕉要吃, 需要在规定时间内吃完
// 香蕉是恒定的, 吃香蕉的速度 K, 与需要吃的时间 H 成反比, 可以整个函数
// 二分查找来寻找左边界

class Solution {
public:
    int minEatingSpeed(vector<int>& piles, int h) {
        return find_left(piles, h);
    }

    // 吃香蕉函数
    long func(vector<int>& nums, int K) {
        long H = 0;
        for (auto num : nums) {
            H += num / K;
            if (num % K != 0) {
                H++;
            }
        }
        return H;
    }

    // 二分查找
    int find_left(vector<int>& piles, int target) {
        long left = 1, right = 10e9;
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (func(piles, mid) <= target) {
                right = mid;
            } else if (func(piles, mid) > target) {
                left = mid + 1;
            }
        }
        return int(left);
    }
};

1011. 在 D 天内送达包裹的能力 【数组】【二分法】

题解

// 现在有一些包裹, 这些包裹的总重量固定, 需要 f(x) 天运送, 每次运用的重量 x 可变
// 现在求 x 为多少时, f(x) 为 5, 明显用二分法
// 先构造函数

class Solution {
public:
    int shipWithinDays(vector<int>& weights, int days) {
        return find_left(weights, days);
    }

    // 求运送量为 x 时,  天数 f(x) 为多少
    int func(vector<int>& weights, int x) {
        int day = 0;
        int sum = 0;
        for (int i = 0; i < weights.size(); ++i) {
            sum += weights[i];
            if (sum <= x) continue;
            if (sum > x) {
                day++;
                sum = weights[i];
            }
        }
        if (sum > 0) day++;
        return day;
    }

    // 二分搜素
    int find_left(vector<int>& weights, int target) {
        int max_num = *max_element(weights.begin(), weights.end());
        int left = max_num, right = 500 * weights.size();
        while (left < right) {
            int mid = left + (right - left) / 2;
            if (func(weights, mid) > target) {
                left = mid + 1;
            } else if (func(weights, mid) <= target) {
                right = mid;
            }
        }
        return left;
    }
};

1081. 不同字符的最小子序列 【字符串】【栈】

题解

class Solution {
public:
    string smallestSubsequence(string s) {
        // 使用 map 存放剩余字符的出现字数
        // 使用 set 记录当前字符有无出现过
        unordered_map<char, int> umap;
        unordered_set<char> uset;
        for (char c : s) {
            umap[c]++;
        }
        // 定义栈用于存放符合条件的字符
        stack<char> stk;
        // 开始遍历
        for (char c : s) {
            umap[c]--;
            // 添加过这个字符, 直接跳过
            if (uset.count(c)) {
                continue;
            }
            // 第一次遇到, 判断是否加进去
            // 如果有字典序更大的数被添加了已经, 找到更小字典序的数
            while (!stk.empty() && stk.top() > c) {
                // 如过后续没有多余的 top 数
                if (umap[stk.top()] == 0) {
                    break;
                }
                uset.erase(stk.top());
                stk.pop();
            }
            // 终于此时我的字典序最大了, 直接添加
            stk.push(c);
            uset.insert(c);
        }
        // 获取字符串
        string res = "";
        while (!stk.empty()) {
            res = stk.top() + res;
            stk.pop();
        }
        return res;
    }
};

1094. 拼车 【数组】【差分】

题解

class Solution {
public:
    bool carPooling(vector<vector<int>>& trips, int capacity) {
        // 区间和 -> 差分数组 -> 判断元素是否大于 capacity
        int start = INT_MAX, end = 0;
        for (auto trip : trips) {
            if (trip[1] < start) {
                start = trip[1];
            }
            if (trip[2] > end) {
                end = trip[2];
            }
        }
        cout << start << " " << end << endl;
        // 差分数组
        vector<int> diff(end - start + 1);
        vector<int> res(end - start + 1);
        // 因为到站了下车, 所以应该在 diff[to] 处减, 而不是 diff[to+1]
        for (auto trip : trips) {
            diff[trip[1] - start] += trip[0];
            if (trip[2] - start < end - start + 1) {
                diff[trip[2] - start] -= trip[0];
            }
        }
        // 恢复原数组
        res[0] = diff[0];
        if (res[0] > capacity) return false;
        for (int i = 1; i < end - start + 1; ++i) {
            res[i] = res[i - 1] + diff[i];
            if (res[i] > capacity) return false;
        }
        for (int i = 0; i < end - start + 1; ++i) {
            cout << res[i] << endl;
        }
        return true;
    }
};

1109. 航班预定统计 【数组】【差分】

题解

class Solution {
public:
    vector<int> corpFlightBookings(vector<vector<int>>& bookings, int n) {
        // 区间加法问题, 一眼差分数组
        vector<int> diff(n);
        vector<int> res(n);
        for (auto book : bookings) {
            diff[book[0] - 1] += book[2];
            if (book[1] < n) {
                diff[book[1]] -= book[2];
            }
        }
        // 根据差分数组恢复原数组
        res[0] = diff[0];
        for (int i = 1; i < n; ++i) {
            res[i] = res[i - 1] + diff[i];
        }
        return res;
    }
};

二叉树

95. 不同的二叉搜索树Ⅱ 【BST】【分解】【后序】

题解

class Solution {
public:
    vector<TreeNode*> generateTrees(int n) {
        return build(1, n);
    }
    // 构建二叉树, 返回所有种类的排列树
    vector<TreeNode*> build(int left, int right) {
        vector<TreeNode*> res;
        if (left > right) {
            res.push_back(nullptr);
            return res;
        }
        if (left == right) {
            TreeNode* root = new TreeNode(left);
            res.push_back(root);
            return res;
        }
        for (int i = left; i <= right; ++i) {
            vector<TreeNode*> left_nodes = build(left, i - 1);
            vector<TreeNode*> right_nodes = build(i + 1, right);
            for (auto left_node : left_nodes) {
                for (auto right_node : right_nodes) {
                    // 构建根节点
                    TreeNode* root = new TreeNode(i);
                    root->left = left_node;
                    root->right = right_node;
                    // 把构建好的子树作为一类, 放入 res
                    res.push_back(root);
                }
            }
        }
        return res;
    }
};

98. 验证二叉搜索树 【BST】【遍历】【中序】

题解

class Solution {
private:
    long pre_max = LONG_MIN;
public:
    bool isValidBST(TreeNode* root) {
        return traverse(root);
    }

    bool traverse(TreeNode* root) {
        // 终止
        if (root == nullptr) return true;
        bool left = traverse(root->left);
        if (root->val <= pre_max) return false;
        pre_max = root->val;
        bool right = traverse(root->right);
        return left && right;
    }
};

104. 二叉树的最大深度 【二叉树】【遍历】【前序】

题解

class Solution {
private:
    int depth = 0, res = 0;
public:
    int maxDepth(TreeNode* root) {
        traverse(root);
        return res;
    }

    void traverse(TreeNode* root) {
        // 终止条件
        if (root == nullptr) return;
        // 前序位置
        depth++;
        if (res < depth) {
            res = depth;
        }
        traverse(root->left);
        traverse(root->right);
        // 后序位置
        depth--;
    }
};

105. 从前序与中序遍历序列构造二叉树 【二叉树】【构造】【分解】【前序】

题解

class Solution {
private:
    unordered_map<int, int> umap;
public:
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n = preorder.size();
        for (int i = 0; i < inorder.size(); ++i) {
            umap[inorder[i]] = i;
        }
        return build(preorder, inorder, 0, n - 1, 0, n - 1);
    }

    TreeNode* build(vector<int>& pre, vector<int> in, int pre_l, int pre_r, int in_l, int in_r) {
        if (pre_l > pre_r) return nullptr;
        // 确定左右节点的个数
        int pre_root = pre[pre_l];
        int in_idx = umap[pre_root];
        int left_num = in_idx - in_l;
        // 构建根节点
        TreeNode* node = new TreeNode(pre_root);
        // [pre_l] [pre_l+1, pre_l+left_num] [pre_l+left_num+1, pre_r]
        // [in_l, in_l+left_num] [in_idx] [in_idx+1, in_r]
        node->left = build(pre, in, pre_l + 1, pre_l + left_num, in_l, in_l + left_num);
        node->right = build(pre, in, pre_l + left_num + 1, pre_r, in_idx + 1, in_r);
        return node;
    }
};

106. 从中序与后序遍历序列构造二叉树 【二叉树】【构造】【分解】【前序】

题解

class Solution {
private:
    unordered_map<int, int> umap;
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        int n = inorder.size();
        for (int i = 0; i < inorder.size(); ++i) {
            umap[inorder[i]] = i;
        }
        return build(inorder, postorder, 0, n - 1, 0, n - 1);
    }

    // 构建二叉树
    // 中 [左, !中, 右]
    // 后 [左, 右, !中]
    TreeNode* build(vector<int>& in, vector<int>& post, int in_l, int in_r, int post_l, int post_r) {
        if (post_l > post_r) return nullptr;
        // 先定位, 划分数组为两部分
        int post_root = post[post_r];
        int in_idx = umap[post_root];
        int left_num = in_idx - in_l;
        // 构造根节点
        TreeNode* node = new TreeNode(post_root);
        // [in_l, in_idx-1] [in_idx] [in_idx+1, in_r]
        // [post_l, post_l+left_num-1] [post_l+left_num, post_r-1] [post_r]
        node->left = build(in, post, in_l, in_idx - 1, post_l, post_l + left_num - 1);
        node->right = build(in, post, in_idx + 1, in_r, post_l + left_num, post_r - 1);
        return node;
    }
};

114. 二叉树展开为链表 【二叉树】【分解】【后序】

题解

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

116. 填充每个节点的下一个右侧节点指针 【多叉树】【遍历】【前序】

题解

class Solution {
public:
    Node* connect(Node* root) {
        if (root == nullptr) return nullptr;
        traverse(root->left, root->right);
        return root;
    }

    void traverse(Node* root1, Node* root2) {
        // 终止
        if (root1 == nullptr) return;
        // 进入
        root1->next = root2;
        traverse(root1->left, root1->right);
        traverse(root1->right, root2->left);
        traverse(root2->left, root2->right);
    }
};

222. 完全二叉树的节点个数 【完全二叉树】【分解】

题解

class Solution {
public:
    int countNodes(TreeNode* root) {
        TreeNode* left = root, * right = root;
        int h_left = 0, h_right = 0;
        while (left != nullptr) {
            left = left->left;
            h_left++;
        }
        while (right != nullptr) {
            right = right->right;
            h_right++;
        }
        if (h_left == h_right) {
            return pow(2, h_left) - 1;
        }
        int left_num = countNodes(root->left);
        int right_num = countNodes(root->right);
        return 1 + left_num + right_num;
    }
};

226. 翻转二叉树 【二叉树】【遍历】【前序】

题解

class Solution {
public:
    TreeNode* invertTree(TreeNode* root) {
        traverse(root);
        return root;
    }

    void traverse(TreeNode* root) {
        // 终止
        if (root == nullptr) return;
        // 进入
        TreeNode* tmp = root->left;
        root->left = root->right;
        root->right = tmp;
        traverse(root->left);
        traverse(root->right);
    }
};

230. 二叉搜索树中第 K 小的元素 【BST】【遍历】【中序】

题解

class Solution {
private:
    int k = 0;
    int cur = 0;
    int res = 0;
public:
    int kthSmallest(TreeNode* root, int k) {
        this->k = k;
        traverse(root);
        return res;
    }

    void traverse(TreeNode* root) {
        if (root == nullptr) return;
        traverse(root->left);
        cur++;
        if (cur == k) {
            res = root->val;
            return;
        }
        traverse(root->right);
    }
};

235. 二叉搜索树的最近公共祖先 【公共祖先】【分解】【后续】

题解

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        int val1 = min(p->val, q->val);
        int val2 = max(p->val, q->val);
        return find(root, val1, val2);
    }

    TreeNode* find(TreeNode* root, int val1, int val2) {
        if (root == nullptr) return nullptr;
        if (root->val > val2) {
            return find(root->left, val1, val2);
        }
        if (root->val < val1) {
            return find(root->right, val1, val2);
        }
        if (root->val >= val1 && root->val <= val2) {
            return root;
        }
        return root;
    } 
};

236. 二叉树的最近公共祖先 【公共祖先】【分解】【后序】

题解

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        return find(root, p->val, q->val);
    }

    // 遍历查找节点
    TreeNode* find(TreeNode* root, int val1, int val2) {
        if (root == nullptr) return nullptr;
        if (root->val == val1 || root->val == val2) {
            return root;
        }
        TreeNode* left = find(root->left, val1, val2);
        TreeNode* right = find(root->right, val1, val2);
        // 后序位置, 判断是否左右子树均有目标值
        if (left != nullptr && right != nullptr) {
            return root;
        }
        return left != nullptr ? left : right;
    }
};

297. 二叉树的序列化与反序列化 【二叉树】【序列化】【分解】【前序】

题解

class Codec {
private:
	// 将序列化数据放在数组中, 方便操作 
    vector<string> strs;
public:
    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        // 碰到空节点的情况
        if (root == nullptr) return "#";
        string left = serialize(root->left);
        string right = serialize(root->right);
        string cur = left + "," + right + "," + to_string(root->val);
        return cur;
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        int pos = 0;
        while ((pos = data.find(",")) != data.npos) {
            strs.push_back(data.substr(0, pos));
            data.erase(0, pos + 1);
        }
        strs.push_back(data);
        return build();
    }

    // 构造二叉树
    TreeNode* build() {
        if (strs.empty()) return nullptr;
        // 找到根节点
        string s = strs.back();
        strs.pop_back();
        if (s == "#") return nullptr;
        TreeNode* node = new TreeNode(stoi(s));
        node->right = build();
        node->left = build();
        return node;
    }
};

341. 扁平化嵌套列表迭代器 【嵌套数组】【遍历】

题解

class NestedIterator {
private:
    vector<int> res;
    vector<int>::iterator it;
    // 遍历函数
    void traverse(vector<NestedInteger>& nestedList) {
        for (int i = 0; i < nestedList.size(); ++i) {
            if (nestedList[i].isInteger()) {
                res.push_back(nestedList[i].getInteger());
            } else {
                traverse(nestedList[i].getList());
            }
        }
    }
public:
    NestedIterator(vector<NestedInteger> &nestedList) {
        traverse(nestedList);
        it = res.begin();
    }
    
    int next() {
        return *it++;
    }
    
    bool hasNext() {
        return it != res.end();
    }
};

450. 删除二叉搜索树中的节点 【BST】【遍历】【前序】

题解

class Solution {
private:
    int key;
    int bak;    // 删除交换后的节点后, 用于恢复原始的 key
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        this->key = key;
        this->bak = key;
        return traverse(root);
    }

    // 先通过遍历找到需要删除的节点
    // 然后将其删除
    TreeNode* traverse(TreeNode* root) {
        if (root == nullptr) return nullptr;
        if (root->val == key) {
            if (root->left == nullptr && root->right == nullptr) {
                return nullptr;
            } else if (root->left == nullptr){
                return root->right;
            } else if (root->right == nullptr) {
                return root->left;
            } else {
                TreeNode* mini = getMin(root->right);
                key = mini->val;
                root->right = traverse(root->right);
                key = bak;
                mini->left = root->left;
                mini->right = root->right;
                root = mini;
            }
        } else if (root->val > key) {
            root->left = traverse(root->left);
        } else {
            root->right = traverse(root->right);
        }
        return root;
    }

    // 获取以该节点开始的最小节点
    TreeNode* getMin(TreeNode* root) {
        while (root->left != nullptr) {
            root = root->left;
        }
        return root;
    }
};

538. 把二叉搜索树转换为累加树 【BST】【遍历】【中序】

题解

class Solution {
private:
    int sum = 0;
public:
    TreeNode* convertBST(TreeNode* root) {
        traverse(root);
        return root;
    }

    // 遍历 BST
    void traverse(TreeNode* root) {
        if (root == nullptr) return;
        traverse(root->right);
        sum += root->val;
        root->val = sum;
        traverse(root->left);
    }
};

543. 二叉树的直径 【二叉树】【分解】【后序】

题解

class Solution {
private:
    int maxDiam = 0;
public:
    int diameterOfBinaryTree(TreeNode* root) {
        maxDepth(root);
        return maxDiam;
    }

    int maxDepth(TreeNode* root) {
        if (root == nullptr) return 0;
        int left = maxDepth(root->left);
        int right = maxDepth(root->right);
        // 后续位置
        int curDepth = max(left, right) + 1;
        int diam = left + right;
        maxDiam = max(maxDiam, diam);
        return curDepth;
    }
}; 

652. 寻找重复的子树 【二叉树】【序列化】【分解】【后序】

题解

class Solution {
private:
    // 记录目前出现过的字符串 
    unordered_map<string, int> umap;
    // 记录最终的结果
    vector<TreeNode*> res;
public:
    vector<TreeNode*> findDuplicateSubtrees(TreeNode* root) {
        serialize(root);
        return res;
    }

    string serialize(TreeNode* root) {
        if (root == nullptr) return "#";
        string left = serialize(root->left);
        string right = serialize(root->right);
        string cur = left + "," + right + "," + to_string(root->val);
        if (umap.count(cur)) {
            if (umap[cur] == 1) {
                res.push_back(root);
            }
            umap[cur]++;
        } else {
            umap[cur]++;
        }
        return cur;
    }
};

654. 最大二叉树 【二叉树】【构造】【分解】【前序】

题解

class Solution {
public:
    TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
        return traverse(nums, 0, nums.size());
    }

    // 遍历构建数
    TreeNode* traverse(vector<int>& nums, int left, int right) {
        if (left >= right) return nullptr;
        // 先构建根节点
        auto max_it = max_element(nums.begin() + left, nums.begin() + right);
        int max_idx = (max_it - nums.begin());
        TreeNode* node = new TreeNode(*max_it);
        // 构建左子树   [left, mid) [mid] [mid + 1, right)
        if (left < max_idx) {
            node->left = traverse(nums, left, max_idx);
        }
        // 构建右子树
        if (max_idx + 1 < right) {
            node->right = traverse(nums, max_idx + 1, right);
        }
        return node;
    }
};

700. 二叉搜索树中的搜索 【BST】【遍历】【前序】

题解

class Solution {
private:
    int val;
    TreeNode* res = nullptr;
public:
    TreeNode* searchBST(TreeNode* root, int val) {
        this->val = val;
        traverse(root);
        return res;
    }

    // 遍历, 搜索
    void traverse(TreeNode* root) {
        if (root == nullptr) return;
        if (root->val == val) {
            res = root;
            return;
        }
        if (root->val > val) {
            traverse(root->left);
        }
        if (root->val < val) {
            traverse(root->right);
        }
    }
};

701. 二叉搜索树中的插入操作 【BST】【遍历】【前序】

题解

class Solution {
private:
    int val;
public:
    TreeNode* insertIntoBST(TreeNode* root, int val) {
        if (root == nullptr) return new TreeNode(val);
        this->val = val;
        traverse(root);
        return root;
    }

    // 先找到位置(叶子节点), 再插入
    TreeNode* traverse(TreeNode* root) {
        // 找到合适的叶子节点
        if (root == nullptr) {
            return new TreeNode(val);
        }
        if (root->val > val) {
            root->left = traverse(root->left);
        } else {
            root->right = traverse(root->right);
        }
        return root;
    }
};

889. 根据前序和后序遍历构造二叉树 【二叉树】【构造】【分解】【前序】

题解

class Solution {
private:
    unordered_map<int, int> umap;
public:
    TreeNode* constructFromPrePost(vector<int>& preorder, vector<int>& postorder) {
        int n = preorder.size();
        for (int i = 0; i < n; ++i) {
            umap[postorder[i]] = i;
        }
        return build(preorder, postorder, 0, n - 1,0 , n - 1);
    }

    // 构造二叉树
    // 前 【中1, 【中2, 左, 右】, 右】
    // 后 【【左, 右, 中2】, 右, 中1】
    // 根据 中2 判断左半部分节点的个数
    TreeNode* build(vector<int>& pre, vector<int>& post, int pre_l, int pre_r, int post_l, int post_r) {
        if (pre_l == pre_r) return new TreeNode(pre[pre_l]);
        if (pre_l > pre_r) return nullptr;
        // 先确定左半节点个数
        int pre_root1 = pre[pre_l];
        int pre_root2 = pre[pre_l + 1];
        int post_idx2 = umap[pre_root2];
        int left_num = post_idx2 - post_l + 1;
        // 开始构造
        TreeNode* node = new TreeNode(pre_root1);
        // [pre_l] [pre_l+1, pre_l+left_num] [pre_l+left_num+1, pre_r]
        // [post_l, post_l+left_num-1] [post_l+left_num, post_r-1] [post_r]
        node->left = build(pre, post, pre_l + 1, pre_l + left_num, post_l, post_l + left_num - 1);
        node->right = build(pre, post, pre_l + left_num + 1, pre_r, post_l + left_num, post_r - 1);
        return node;
    }
};

1038. 从二叉搜索树到更大和树 【BST】【遍历】【中序】

题解

class Solution {
private:
    int sum = 0;
public:
    TreeNode* bstToGst(TreeNode* root) {
        traverse(root);
        return root;
    }

    // 遍历 BST
    void traverse(TreeNode* root) {
        if (root == nullptr) return;
        traverse(root->right);
        sum += root->val;
        root->val = sum;
        traverse(root->left);
    }
};

1644. 二叉树的最近公共祖先Ⅱ 【公共祖先】【分解】【后续】

题解

class Solution {
private:
    bool findP = false, findQ = false;
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        TreeNode* res = find(root, p->val, q->val);
        if (findP && findQ) {
            return res;
        } else {
            return nullptr;
        }
    }
    // 寻找子节点有 val1 val2 的父节点
    TreeNode* find(TreeNode* root, int val1, int val2) {
        if (root == nullptr) return nullptr;
        TreeNode* left = find(root->left, val1, val2);
        TreeNode* right = find(root->right, val1, val2);
        // 后序位置判断, 确保所有节点都有遍历到
        if (left != nullptr && right != nullptr) {
            return root;
        }
        // 判断节点是不是目标值
        if (root->val == val1 || root->val == val2) {
            if (root->val == val1) findP = true;
            if (root->val == val2) findQ = true;
            return root;
        }
        return left != nullptr ? left : right;
    }
};

1650. 二叉树的最近公共祖先Ⅲ 【公共祖先】【链表相交】

题解

class Solution {
public:
    Node* lowestCommonAncestor(Node* p, Node * q) {
        // 由于存在父节点指针, 因此可以视为链表相交问题
        Node* a = p, * b = q;
        while (a != b) {
            if (a == nullptr) {
                a = q;
            } else {
                a = a->parent;
            }
            if (b == nullptr) {
                b = p;
            } else {
                b = b->parent;
            }
        }
        return a;
    }
};

1676. 二叉树的最近公共祖先Ⅳ 【公共祖先】【分解】【后续】

题解

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, vector<TreeNode*> &nodes) {
        unordered_set<int> uset;
        for (auto node : nodes) {
            uset.insert(node->val);
        }
        return find(root, uset);
    }

    // 分解
    TreeNode* find(TreeNode* root, unordered_set<int>& uset) {
        if (root == nullptr) return nullptr;
        // 当前节点的值在 uset 中
        if (uset.count(root->val)) return root;
        TreeNode* left = find(root->left, uset);
        TreeNode* right = find(root->right, uset);
        // 判断
        if (left != nullptr && right != nullptr) {
            return root;
        }
        return left != nullptr ? left : right;
    }
};

207. 📙课程表【图】【环判断】【DFS】【邻接表】

class Solution {
private:
	unordered_map<int, vector<int>> graph;		// 邻接表
	vector<int> visited;						// 记录遍历过的节点
	vector<int> onPath;							// 记录当前路径上的节点
	bool hasCycle = false;						// 记录有无环
public:
	bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
		// 初始化邻接表
		for (int i = 0; i < prerequisites.size(); ++i) {
			graph[prerequisites[i][1]].push_back(prerequisites[i][0]);
		}
		// 遍历数组初始化
		visited = vector<int>(numCourses, false);
		onPath = vector<int>(numCourses, false);
		// 开始逐个访问节点, 判断有无环 DFS
		for (int i = 0; i < numCourses; ++i) {
			traverse(i);
		}
		return !hasCycle;
	}
	
	// 从节点 val 开始遍历, 将访问过的节点置为 true
	void traverse(int val) {
		// 如果遍历的路径上已经存在 val, 则标记有环
		if (onPath[val]) hasCycle = true;
		// 如果有环, 或者碰到过之前已经遍历完的节点, 直接返回
		if (hasCycle || visited[val]) return;
		// 遇到新节点, 更新状态
		visited[val] = true;
		onPath[val] = true;
        for (int num : graph[val]) {
            traverse(num);
        }
        // 离开时, 当前节点状态更新
        onPath[val] = false;
	}
};

207. 📙课程表【图】【环判断】【BFS】【邻接表】

class Solution {
private:
	unordered_map<int, vector<int>> graph;		// 邻接表
    vector<int> inDegree;                       // 入度矩阵
    queue<int> que;                             // 队列, 用于 BFS
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        // 初始化邻接表
        inDegree.resize(numCourses);
		for (int i = 0; i < prerequisites.size(); ++i) {
            inDegree[prerequisites[i][0]]++;
			graph[prerequisites[i][1]].push_back(prerequisites[i][0]);
		}
        // 队列, 用于 BFS, 将入度为 0 的节点入队
        for (int i = 0; i < numCourses; ++i) {
            if (inDegree[i] == 0) {
                que.push(i);
            }
        }
        int count = 0;          // 记录多少门课上过了
        // BFS
        while (!que.empty()) {
            int val = que.front(); que.pop();
            count++;
            // 更新所有关联课程的入度
            for (auto num : graph[val]) {
                inDegree[num]--;
                // 如果更新后的节点入度为 0, 则假如队列
                if (inDegree[num] == 0) {
                    que.push(num);
                }
            }
        }
        if (count == numCourses) return true;
        return false;
    }
};

210. 📙课程表Ⅱ【图】【环判断】【拓扑排序】【DFS】【邻接表】

class Solution {
private:
	unordered_map<int, vector<int>> graph;		// 邻接表
	vector<int> visited;						// 记录遍历过的节点
	vector<int> onPath;							// 记录当前路径上的节点
	bool hasCycle = false;						// 记录有无环
    vector<int> postorder;                      // 记录后序遍历的结果
    vector<int> res;                            // 存放最终结果
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
		// 初始化邻接表
		for (int i = 0; i < prerequisites.size(); ++i) {
			graph[prerequisites[i][1]].push_back(prerequisites[i][0]);
		}
		// 遍历数组初始化
		visited = vector<int>(numCourses, false);
		onPath = vector<int>(numCourses, false);
		// 开始逐个访问节点, 判断有无环 DFS
		for (int i = 0; i < numCourses; ++i) {
			traverse(i);
		}
        if (hasCycle) return {};
        reverse(postorder.begin(), postorder.end());
        res = vector<int>(postorder.begin(), postorder.begin() + numCourses);
        return res;
    }

	// 从节点 val 开始遍历, 将访问过的节点置为 true
	void traverse(int val) {
		// 如果遍历的路径上已经存在 val, 则标记有环
		if (onPath[val]) hasCycle = true;
		// 如果有环, 或者碰到过之前已经遍历完的节点, 直接返回
		if (hasCycle || visited[val]) return;
		// 遇到新节点, 更新状态
		visited[val] = true;
		onPath[val] = true;
        for (int num : graph[val]) {
            traverse(num);
        }
        // 记录后序遍历的结果, 即为拓扑排序的结果
        postorder.push_back(val);
        // 离开时, 当前节点状态更新
        onPath[val] = false;
	}
};

210. 📙课程表Ⅱ【图】【环判断】【拓扑排序】【BFS】【邻接表】

class Solution {
private:
    unordered_map<int, vector<int>> graph;		// 邻接表
    vector<int> inDegree;                       // 入度矩阵
    queue<int> que;                             // 队列, 用于 BFS
    vector<int> res;                            // 拓扑排序结果
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        // 初始化邻接表
        inDegree.resize(numCourses);
		for (int i = 0; i < prerequisites.size(); ++i) {
            inDegree[prerequisites[i][0]]++;
			graph[prerequisites[i][1]].push_back(prerequisites[i][0]);
		}
        // 队列, 用于 BFS, 将入度为 0 的节点入队
        for (int i = 0; i < numCourses; ++i) {
            if (inDegree[i] == 0) {
                que.push(i);
            }
        }
        int count = 0;          // 记录多少门课上过了
        // BFS
        while (!que.empty()) {
            int val = que.front(); que.pop();
            res.push_back(val);
            count++;
            // 更新所有关联课程的入度
            for (auto num : graph[val]) {
                inDegree[num]--;
                // 如果更新后的节点入度为 0, 则假如队列
                if (inDegree[num] == 0) {
                    que.push(num);
                }
            }
        }
        if (count == numCourses) return res;
        return {};
    }
};

277. 📙搜索名人【图】【交互】【邻接矩阵】

class Solution {
private:
    queue<int> que;         // 整个队列, 存放候选名人
public:
    int findCelebrity(int n) {
        for (int i = 0; i < n; ++i) {
            que.push(i);
        }
        // 一直排除, 直到只剩下一个候选人
        while (que.size() > 1) {
            int cand = que.front(); que.pop();
            int other = que.front(); que.pop();
            if (knows(cand, other) || !knows(other, cand)) {
                que.push(other);
            } else {
                que.push(cand);
            }
        }
        // 最后只剩下一个人
        int cand = que.front(); que.pop();
        for (int other = 0; other < n; ++other) {
            if (cand == other) continue;
            if (knows(cand, other) || !knows(other, cand)) {
                return -1;
            }
        }
        return cand;
    }
};

785. 📙判断二分图【图】【二分图】【DFS】【邻接表】

class Solution {
private:
    bool isBit = true;                 // 是否为二分图
    int n;                              // 节点个数
    vector<vector<int>> graph;          // 邻接表
    vector<bool> color;                 // flase, true 代表两种颜色
    vector<bool> visited;               // 记录节点是否被访问过
public:
    bool isBipartite(vector<vector<int>>& graph) {
        int n = graph.size();
        for (auto num : graph) {
            this->graph.push_back(num);
        }
        color.resize(n);
        visited.resize(n);
        // DFS
        for (int i = 0; i < n; ++i) {
            if (!visited[i]) {
                traverse(i);
            }
        }
        return isBit;
    }

    // DFS
    void traverse(int val) {
        // 判断当前是否为二分图
        if (!isBit) return;
        // 进入节点
        visited[val] = true;
        // 访问相邻节点并染色
        for (int num : graph[val]) {
            if (!visited[num]) {
                color[num] = !color[val];
                // 继续遍历
                traverse(num);
            } else {
                if (color[num] == color[val]) {
                    isBit = false;
                    return;
                }
            }
        }
    }
};

785. 📙判断二分图【图】【二分图】【BFS】【邻接表】

class Solution {
private:
    bool isBit = true;                  // 是否为二分图
    int n;                              // 节点个数
    vector<vector<int>> graph;          // 邻接表
    vector<bool> color;                 // flase, true 代表两种颜色
    vector<bool> visited;               // 记录节点是否被访问过
    queue<int> que;                     // 队列
public:
    bool isBipartite(vector<vector<int>>& graph) {
        int n = graph.size();
        for (auto num : graph) {
            this->graph.push_back(num);
        }
        color.resize(n);
        visited.resize(n);
        // BFS
        for (int i = 0; i < n; ++i) {
            if (!visited[i]) {
                bfs(i);
            }
        }
        return isBit;
    }

    // BFS
    void bfs(int val) {
        visited[val] = true;
        que.push(val);
        while (!que.empty() && isBit) {
            int num = que.front(); que.pop();
            // 放入相邻的节点
            for (auto data : graph[num]) {
                // 如果这个节点没有遇到过
                if (!visited[data]) {
                    color[data] = !color[num];
                    visited[data] = true;
                    que.push(data);
                } else {    // 如果遇到过
                    if (color[data] == color[num]) {
                        isBit = false;
                        return;
                    }
                }
            }
        }
    }
};

797. 📙所有可能的路径【图】【路径】【DFS】【邻接表】

class Solution {
private:
    unordered_map<int, vector<int>> graph;          // 邻接表
    int n = 0;                                      // 节点数
    vector<int> path;                               // 单条路径
    vector<vector<int>> res;                        // 所有路径
public:
    // 给了邻接表, 求路径
    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph) {
        // 初始化邻接表
        for (int i = 0; i < graph.size(); ++i) {
            this->graph[i] = graph[i];
        }
        this->n = graph.size();
        traverse(0);
        return res;
    }

    // DFS
    void traverse(int val) {
        // 进入节点
        path.push_back(val);
        // 判断是否退出
        if (val == n - 1) {
            res.push_back(path);
        }
        // 访问下一节点
        for (auto num : graph[val]) {
            traverse(num);
        }
        // 出去节点
        path.pop_back();
    }
};

886. 📙可能的二分法【图】【二分图】【DFS】【邻接表】

class Solution {
private:
    bool isBit = true;                      // 是否是二分图
    unordered_map<int, vector<int>> graph;  // 邻接表
    vector<bool> visited;                   // 节点是否遍历过
    vector<bool> color;                     // 节点是否染色过
public:
    bool possibleBipartition(int n, vector<vector<int>>& dislikes) {
        // 邻接表
        for (auto dislike : dislikes) {
            this->graph[dislike[0]].push_back(dislike[1]);
            this->graph[dislike[1]].push_back(dislike[0]);
        }
        visited.resize(n + 1);
        color.resize(n + 1);
        // DFS, 每个节点都开始一遍
        for (int i = 1; i <= n; ++i) {
            if (!visited[i]) {
                dfs(i);
            }
        }
        return isBit;
    }

    void dfs(int val) {
        // 终止
        if (!isBit) return;
        // 进入节点
        visited[val] = true;
        // 开始染色下一层
        for (auto num : graph[val]) {
            // 对于没有访问过的节点, 染色
            if (!visited[num]) {
                color[num] = !color[val];
                // 继续遍历
                dfs(num);
            } else {
                // 判断是否为二分图
                if (color[num] == color[val]) {
                    isBit = false;
                    return;
                }
            }
        }
    }
};

排序

215. 数组中的第 K 个最大元素 【快速排序】【分解】【先序】【三指针】

题解

class Solution {
private:
    vector<int> data;
    int target;
    int res;
public:
    int findKthLargest(vector<int>& nums, int k) {
        int n = nums.size();
        target = n - k;
        data.resize(n);
        for (int i = 0; i < n; ++i) {
            data[i] = nums[i];
        }
        quickSort(0, n - 1);
        return res;
    }

    void quickSort(int left, int right) {
        if (left > right) return;
        if (left == right) {
            if (left == target) {
                res = data[target];
                return;
            }
        }
        vector<int> p = part(left, right);
        if (p[0] + 1 <= target && p[1] - 1 >= target) {
            res = data[target];
            return;
        }
        quickSort(left, p[0]);
        quickSort(p[1], right);
    }

    vector<int> part(int left, int right) {
        int pivot_idx = rand() % (right - left + 1) + left;
        int pivot = data[pivot_idx];
        swap(left, pivot_idx);
        int curp = left + 1, leftp = left, rightp = right + 1;
        while (curp < rightp) {
            if (data[curp] < pivot) {
                leftp++;
                swap(curp, leftp);
                curp++;
            } else if (data[curp] > pivot) {
                rightp--;
                swap(curp, rightp);
            } else {
                curp++;
            }
        }
        swap(left, leftp);
        return {leftp - 1, rightp};
    }

    void swap(int a, int b) {
        int tmp = data[a];
        data[a] = data[b];
        data[b] = tmp;
    }
};

315. 计算右侧小于当前元素的个数 【归并排序】【分解】【双指针】

题解

class Solution {
private:
    // 原始数组及下标
    vector<pair<int, int>> data;
    // 统计大于当前数的次数
    vector<int> count;
    // 结果
    vector<int> res;
public:
    vector<int> countSmaller(vector<int>& nums) {
        int n = nums.size();
        data.resize(n);
        count.resize(n);
        res.resize(n);
        for (int i = 0; i < n; ++i) {
            data[i] = make_pair(nums[i], i);
        }
        mergeSort(0, n - 1);
        return count;
    }

    // 归并排序
    void mergeSort(int left, int right) {
        if (left >= right) return;
        int mid = left + (right - left) / 2;
        mergeSort(left, mid);
        mergeSort(mid + 1, right);
        merge(left, mid, right);
    }

    // 合并函数
    void merge(int left, int mid, int right) {
        int len = right - left + 1;
        vector<pair<int, int>> bak(len, make_pair(0, 0));
        int p1 = left, p2 = mid + 1, p = 0;
        while (p1 <= mid && p2 <= right) {
            if (data[p1].first <= data[p2].first) {
                count[data[p1].second] += p2 - mid - 1;
                bak[p++] = data[p1++];
            } else {
                bak[p++] = data[p2++];
            }
        }
        while (p1 <= mid) {
            count[data[p1].second] += p2 - mid - 1;
            bak[p++] = data[p1++];
        }
        while (p2 <= right) {
            bak[p++] = data[p2++];
        }
        for (int i = 0; i < bak.size(); ++i) {
            data[left + i] = bak[i];
        }
    }
};

327. 区间和的个数 【归并排序】【前缀和】【分解】【双指针】【滑动窗口】

题解

class Solution {
private:
    vector<long> preSum;
    int count = 0;
    long lower, upper;
public:
    int countRangeSum(vector<int>& nums, int lower, int upper) {
        int n = nums.size();
        this->lower = lower;
        this->upper = upper;
        preSum.resize(n + 1);
        for (int i = 0; i < n; ++i) {
            preSum[i + 1] = preSum[i] + nums[i];
        }
        mergeSort(0, n);
        return count;
    }

    void mergeSort(int left, int right) {
        if (left >= right) return;
        // 先找到一个中点
        int mid = left + (right - left) / 2;
        // 让子任务先排好序 [left, mid] [mid+1, right]
        mergeSort(left, mid);
        mergeSort(mid + 1, right); 
        // 子任务弄好后, 将他俩合并
        merge(left, mid, right);
    }

    // 将两个有序数组排序, 双指针
    void merge(int left, int mid, int right) {
        // 双指针
        int p1 = left, p2 = mid + 1, p = 0;
        // 核心逻辑
        int start = mid + 1, end = mid + 1;
        for (int i = left; i <= mid; ++i) {
            for (int j = start; j <= right; ++j) {
                if (long(preSum[j]) - long(preSum[i]) < long(lower)) {
                    start++;
                } else {
                    break;
                }
            }
            for (int k = end; k <= right; ++k) {
                if (long(preSum[k]) - long(preSum[i]) <= long(upper)) {
                    end++;
                } else {
                    break;
                }
            }
            count += end - start;
        }
        vector<long> bak(right - left + 1, 0);
        while (p1 <= mid && p2 <= right) {
            if (preSum[p1] < preSum[p2]) {
                bak[p++] = preSum[p1++];
            } else {
                bak[p++] = preSum[p2++];
            }
        }
        while (p1 <= mid) {
            bak[p++] = preSum[p1++];
        }
        while (p2 <= right) {
            bak[p++] = preSum[p2++];
        }
        for (int i = 0; i < bak.size(); ++i) {
            preSum[left + i] = bak[i];
        }
    }
};

493. 翻转对 【归并排序】【分解】【双指针】

题解

class Solution {
private:
    vector<int> data;
    int count = 0;
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        data.resize(n);
        for (int i = 0; i < n; ++i) {
            data[i] = nums[i];
        }
        mergeSort(0, n - 1);
        return count;
    }

    // 归并排序
    void mergeSort(int left, int right) {
        if (left >= right) return;
        int mid = left + (right - left) / 2;
        mergeSort(left, mid);
        mergeSort(mid + 1, right);
        merge(left, mid, right);
    }

    // 将两个有序数组排序, 双指针
    void merge(int left, int mid, int right) {
        // 双指针
        int p1 = left, p2 = mid + 1, p = 0;
        int end = mid + 1;
        for (int i = left; i <= mid; ++i) {
            for (int j = end; j <= right; ++j) {
                if (long(data[i]) > 2 * long(data[j])) {
                    end++;
                } else {
                    break;
                }
            }
            count += (end - (mid + 1));
        }
        vector<int> bak(right - left + 1, 0);
        while (p1 <= mid && p2 <= right) {
            if (data[p1] < data[p2]) {
                bak[p++] = data[p1++];
            } else {
                bak[p++] = data[p2++];
            }
        }
        while (p1 <= mid) {
            bak[p++] = data[p1++];
        }
        while (p2 <= right) {
            bak[p++] = data[p2++];
        }
        for (int i = 0; i < bak.size(); ++i) {
            data[left + i] = bak[i];
        }
    }
};

912. 排序数组 【归并排序】【分解】【双指针】

题解

class Solution {
private:
    vector<int> data;
public:
    vector<int> sortArray(vector<int>& nums) {
        int n = nums.size();
        data.resize(n);
        for (int i = 0; i < n; ++i) {
            data[i] = nums[i];
        }
        mergeSort(0, n - 1);
        return data;
    }

    void mergeSort(int left, int right) {
        if (left >= right) return;
        // 先找到一个中点
        int mid = left + (right - left) / 2;
        // 让子任务先排好序 [left, mid] [mid+1, right]
        mergeSort(left, mid);
        mergeSort(mid + 1, right); 
        // 子任务弄好后, 将他俩合并
        merge(left, mid, right);
    }
    // 将两个有序数组排序, 双指针
    void merge(int left, int mid, int right) {
        // 双指针
        int p1 = left, p2 = mid + 1, p = 0;
        vector<int> bak(right - left + 1, 0);
        while (p1 <= mid && p2 <= right) {
            if (data[p1] < data[p2]) {
                bak[p++] = data[p1++];
            } else {
                bak[p++] = data[p2++];
            }
        }
        while (p1 <= mid) {
            bak[p++] = data[p1++];
        }
        while (p2 <= right) {
            bak[p++] = data[p2++];
        }
        for (int i = 0; i < bak.size(); ++i) {
            data[left + i] = bak[i];
        }
    }
};

912. 排序数组 【快速排序】【分解】【先序】【三指针】

题解

class Solution {
private:
    vector<int> data;
public:
    vector<int> sortArray(vector<int>& nums) {
        int n = nums.size();
        data.resize(n);
        for (int i = 0; i < n; ++i) {
            data[i] = nums[i];
        }
        quickSort(0, n - 1);
        return data;
    }

    void quickSort(int left, int right) {
        if (left >= right) return;
        vector<int> p = part(left, right);
        quickSort(left, p[0]);
        quickSort(p[1], right);
    }

    vector<int> part(int left, int right) {
        // [0, right - left] -> [left, right]
        int pivot_idx = rand() % (right - left + 1) + left;
        int pivot = data[pivot_idx];
        swap(left, pivot_idx);
        //  leftp    cur                        rightp
        // [pivot] [left+1, ..., right-1, right]
        int curp = left + 1, leftp = left, rightp = right + 1;
        while (curp < rightp) {
            // 把 <= pivot 的值都放左边
            if (data[curp] < pivot) {
                leftp++;
                swap(leftp, curp);
                curp++;
            } else if (data[curp] == pivot) {
                curp++;
            } else {
                // 这里怕交换后的数, 依然大于 pivot
                rightp--;
                swap(curp, rightp);
            }
        }
        swap(leftp, left);
        return {leftp - 1, rightp};
    }

    void swap(int a, int b) {
        int tmp = data[a];
        data[a] = data[b];
        data[b] = tmp;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

进击的小老虎丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值