算法汇总

哈希表

49. 字母异位词分组

非常简单

vector<vector<string>> groupAnagrams(vector<string> &strs) {  
    unordered_map<string, vector<string >> map;  

    for (auto str: strs) {  
        string s = str;  
        sort(s.begin(), s.end());  
        map[s].push_back(str);  
    }  
    vector<vector<string>> ans;  
    for(auto [_,v]:map){  
        ans.push_back(v);  
    }  
    return ans;  
}

128. 最长连续序列

int longestConsecutive(vector<int> &nums) {  
    unordered_set<int> set(nums.begin(), nums.end());  
  
    int ans = 0;  
    for (int x: set) {  
        if (!set.contains(x - 1)) {  
            int y = x + 1;  
            while (set.contains(y)) {  
                y++;  
            }  
            ans = max(ans, y - x);  
        }  
    }  
    return ans;  
}

双指针

15. 三数之和 🌟🌟🌟

双指针题型的无冕之王
两数之和升级版,方法完全不一样,还用hash的话,会有很多特殊情况需要考虑,推荐使用 排序+双指针 法。字节常考题,必会。

vector<vector<int>> threeSum(vector<int> &nums) {  
    sort(nums.begin(), nums.end());  
    vector<vector<int>> ans;  
    int n = nums.size();  
    for (int i = 0; i < n - 2 && nums[i] <= 0; i++) {  
        if (i && nums[i] == nums[i - 1]) continue;  
  
        int j = i + 1, k = n - 1;  
        while (j < k) {  
            int x = nums[i] + nums[j] + nums[k];  
            if (x < 0) {  
                j++;  
            } else if (x > 0) {  
                k--;  
            } else {  
                ans.push_back({nums[i], nums[j++], nums[k--]});  
                while (j < k && nums[j] == nums[j - 1]) {  
                    j++;  
                }  
                while (j < k && nums[k] == nums[k + 1]) {  
                    k--;  
                }  
            }  
        }  
    }  
    return ans;  
}

42. 接雨水

最简单的困难题,字节常考题

int trap(vector<int> &height) {  
	int n = height.size();  
	int left[n], right[n];  
	left[0] = height[0];  
	right[n - 1] = height[n - 1];  
	for (int i = 1; i < n; i++) {  
		left[i] = max(height[i], left[i - 1]);  
	}  
	for (int j = n - 2; j >= 0; j--) {  
		right[j] = max(height[j], right[j + 1]);  
	}  

	int ans = 0;  
	for (int i = 1; i < n - 1; i++) {  
		ans += min(left[i], right[i]) - height[i];  
	}  
	return ans;  
}  

滑动窗口

/* 滑动窗口算法框架 */
void slidingWindow(string s) {
    // 用合适的数据结构记录窗口中的数据
    unordered_map<char, int> window;
    
    int left = 0, right = 0;
    while (right < s.size()) {
        // c 是将移入窗口的字符
        char c = s[right];
        window.add(c)
        // 增大窗口
        right++;
        // 进行窗口内数据的一系列更新
        ...

        /*** debug 输出的位置 ***/
        // 注意在最终的解法代码中不要 print
        // 因为 IO 操作很耗时,可能导致超时
        printf("window: [%d, %d)\n", left, right);
        /********************/
        
        // 判断左侧窗口是否要收缩
        while (left < right && window needs shrink) {
            // d 是将移出窗口的字符
            char d = s[left];
            window.remove(d)
            // 缩小窗口
            left++;
            // 进行窗口内数据的一系列更新
            ...
        }
    }
}

3. 无重复字符的最长子串

双指针+哈希表

int lengthOfLongestSubstring(string s) {
	int n = s.size();
	unordered_set<char> set;
	int res=0;

	for (int i = 0, j = 0; i < n; i++) {
		while (set.contains(s[i])) {
			set.erase(s[j++]);
		}
		set.insert(s[i]);
		res = max(res,i-j+1);
	}
	return res;
}

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

vector<int> findAnagrams(string s, string p) {  
    int m = s.size(), n = p.size();  
    vector<int> ans;  
    if (m < n) return ans;  
  
    vector<int> need(26), window(26);  
    for (int i = 0; i < n; i++) {  
        need[p[i]-'a']++ ;  
        window[s[i]-'a']++;  
    }  
    if(need==window){  
        ans.push_back(0);  
    }  
    for(int i=n;i<m;i++){  
        window[s[i]-'a']++;  
        window[s[i-n]-'a']--;  
        if(need==window){  
            ans.push_back(i-n+1);  
        }  
    }  
    return ans;  
}

76. 最小覆盖子串

最经典的滑动窗口

string minWindow(string s, string t) {  
    int m = s.size(), n = t.size();  
    vector<int> need(128), window(128);  
    for (auto c: t) {  
        need[c]++;  
    }  
    int cnt = 0, start = -1, len = INT_MAX;  
    for (int i = 0, j = 0; i < m; i++) {  
        char c = s[i];  
        window[c]++;  
        if (need[c] >= window[c]) {  
            cnt++;  
        }  
        while (cnt == n) {  
            if (i - j + 1 < len) {  
                len = i - j + 1;  
                start = j;  
            }  
            char d = s[j];  
            if (need[d] >= window[d]) {  
                cnt--;  
            }  
            window[d]--;  
            j++;  
        }  
    }  
    return start < 0 ? "" : s.substr(start, len);  
} 

239. 滑动窗口最大值

优先级队列

vector<int> maxSlidingWindow(vector<int> &nums, int k) {  
    int n = nums.size();  
    priority_queue<pair<int, int>> q;  
    for (int i = 0; i < k - 1; i++) {  
        q.push(make_pair(nums[i], i));  
    }  
    vector<int> res;  
    for (int i = k - 1; i < n; i++) {  
        q.push({nums[i],i});  
        while(q.top().second<=i-k){  
            q.pop();  
        }  
        res.push_back(q.top().first);  
    }  
    return res;  
}

53. 最大子数组和 🌟

滑动窗口 或 动态规划

int maxSubArray(vector<int>& nums) {  
    int left = 0, right = 0;  
    int windowSum = 0, maxSum = INT_MIN;  
    while(right < nums.size()){  
        // 扩大窗口并更新窗口内的元素和  
        windowSum += nums[right];  
        right++;  
  
        // 更新答案  
        maxSum = windowSum > maxSum ? windowSum : maxSum;  
  
        // 判断窗口是否要收缩  
        while(windowSum < 0) {  
            // 缩小窗口并更新窗口内的元素和  
            windowSum -=  nums[left];  
            left++;  
        }  
    }  
    return maxSum;  
}

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

209. 长度最小的子数组

int minSubArrayLen(int target, vector<int>& nums) {
	int n=nums.size();
	int l=0,r=0;
	int sum=0,len=INT_MAX;
	while(r<n){
		sum+=nums[r];
		r++;
		while(sum>=target){
			len = min(len,r-l);
			sum-=nums[l++];
		}
	}
	return len==INT_MAX?0:len;
}

数组

56. 合并区间

阿里面试题

vector<vector<int>> merge(vector<vector<int>> &intervals) {  
    sort(intervals.begin(), intervals.end(), [](const auto &a, const auto &b) {  
        return a[0] < b[0];  
    });  
    vector<vector<int>> ans;  
    for (int i = 0; i < intervals.size(); i++) {  
        int L = intervals[i][0], R = intervals[i][1];  
        if(ans.empty()||ans.back()[1]<L){  
            ans.push_back({L,R});  
        }  
        else{  
            ans.back()[1]=max(ans.back()[1],R);  
        }  
    }  
    return ans;  
}

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

困难,hot100,必会题,重点是求两个数组中第k大的数 , 备忘解析 (如果数组只会一题那么必然是它)

double findMedianSortedArrays(vector<int> &nums1, vector<int> &nums2) {  
    int m = nums1.size(), n = nums2.size();  
    int a = getKth(0, 0, (m + n + 1) / 2, nums1, nums2);  
    int b = getKth(0, 0, (m + n + 2) / 2, nums1, nums2);  
    return (a + b) / 2.0;  
}  
  
int getKth(int i, int j, int k, vector<int> &nums1, vector<int> &nums2) {  
    int m = nums1.size(), n = nums2.size();  
    if (i >= m) {  
        return nums2[j + k - 1];  
    }  
    if (j >= n) {  
        return nums1[i + k - 1];  
    }  
    if (k == 1) {  
        return min(nums1[i], nums2[j]);  
    }  
    int p = k / 2;  
    int x = i + p - 1 < m ? nums1[i + p - 1] : INT_MAX;  
    int y = j + p - 1 < n ? nums2[j + p - 1] : INT_MAX;  
    return x < y ? getKth(i + p, j, k - p, nums1, nums2) : getKth(i, j + p, k - p, nums1, nums2);  
}

88. 合并两个有序数组

经典 逆向双指针

void merge(vector<int> &nums1, int m, vector<int> &nums2, int n) {  
    for (int i = m - 1, j = n - 1, k = m + n - 1; ~j; k--) {  
        nums1[k] = i >= 0 && nums1[i] > nums2[j] ? nums1[i--] : nums2[j--];  
    }  
}

27. 移除元素

双指针 so easy

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

11. 盛最多水的容器

双指针

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

560. 和为 K 的子数组

前缀和的经典应用

int subarraySum(vector<int> &nums, int k) {  
    unordered_map<int, int> map;  
    int ans = 0, pre = 0;  
    map[0] = 1;  
    for (auto x: nums) {  
        pre+=x;  
        if(map.count(pre-k)){  
            ans+=map[pre-k];  
        }  
        map[pre]++;  
    }  
    return ans;  
}

41. 缺失的第一个正数 🌟

int firstMissingPositive(vector<int> &nums) {  
    int n = nums.size();  
    for (int i = 0; i < n; i++) {  
        while (nums[i] >= 1 && nums[i] <= n && nums[i] != nums[nums[i] - 1]) {  
            swap(nums[i], nums[nums[i - 1]]);  
        }  
    }  
    for (int i = 0; i < n; ++i) {  
        if (i + 1 != nums[i]) {  
            return i + 1;  
        }  
    }  
    return n + 1;  
}

矩阵

54. 螺旋矩阵

vector<int> spiralOrder(vector<vector<int>> &matrix) {  
    vector<int> res;  
    int dirs[5] = {0, 1, 0, -1, 0};  
    int m = matrix.size(), n = matrix[0].size();  
    int i = 0, j = 0, k = 0;  
    for (int h = m * n; h > 0; h--) {  
        res.push_back(matrix[i][j]);  
        matrix[i][j] = 200;  
        int x = i + dirs[k], y = j + dirs[k + 1];  
        if (x < 0 || x >= m || y < 0 || y >= n || matrix[x][y] == 200) {  
            k = (k + 1) % 4;  
        }  
        i += dirs[k];  
        j += dirs[k + 1];  
    }  
    return res;  
}

48. 旋转图像

vector<vector<int> > rotateMatrix(vector<vector<int> >& mat, int n) {
        for(int i=0;i<n>>1;i++){
            for(int j=0;j<n;j++){
                swap(mat[i][j], mat[n-1-i][j]);
            }
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<i;j++){
                swap(mat[i][j], mat[j][i]);
            }
        }
        return mat;
    }
}

void rotate(vector<vector<int>> &matrix) {  
    int n = matrix.size();  
    for (int i = 0; i < n / 2; i++) {  
        for (int j = 0; j < (n + 1) / 2; j++) {  
            tie(matrix[i][j],matrix[j][n-i-1],matrix[n-i-1][n-j-1],matrix[n-j-1][i]) = \  
            make_tuple(matrix[n-j-1][i],matrix[i][j],matrix[j][n-i-1],matrix[n-i-1][n-j-1]);  
        }  
    }  
}

240. 搜索二维矩阵 II

或则直接从左下角开始搜索

bool searchMatrix(vector<vector<int>> &matrix, int target) {  
    int m = matrix.size(), n = matrix[0].size();  
    for (int i = 0; i < m; i++) {  
        auto it = lower_bound(matrix[i].begin(), matrix[i].end(), target);  
        if (it < matrix[i].end() && *it == target) {  
            return true;  
        }  
    }  
    return false;  
}

字符串

415. 字符串相加 🌟🌟🌟

字符串加减最最常考的字符串题

class Solution {
public:
    string addStrings(string num1, string num2) {
        int i = num1.size() - 1, j = num2.size() - 1;
        string ans;
        for (int c = 0; i >= 0 || j >= 0 || c; --i, --j) {
            int a = i < 0 ? 0 : num1[i] - '0';
            int b = j < 0 ? 0 : num2[j] - '0';
            c += a + b;
            ans += to_string(c % 10);
            c /= 10;
        }
        reverse(ans.begin(), ans.end());
        return ans;
    }

    string subStrings(string num1, string num2) {
        int m = num1.size(), n = num2.size();
        bool neg = m < n || (m == n && num1 < num2);
        if (neg) {
            swap(num1, num2);
        }
        int i = num1.size() - 1, j = num2.size() - 1;
        string ans;
        for (int c = 0; i >= 0; --i, --j) {
            c = (num1[i] - '0') - c - (j < 0 ? 0 : num2[j] - '0');
            ans += to_string((c + 10) % 10);
            c = c < 0 ? 1 : 0;
        }
        while (ans.size() > 1 && ans.back() == '0') {
            ans.pop_back();
        }
        if (neg) {
            ans.push_back('-');
        }
        reverse(ans.begin(), ans.end());
        return ans;
    }
};

5. 最长回文子串 🌟🌟

必须掌握,方法1:动态规划,方法2:中心扩展法

/* f[i][j] 即 s[i...j]是否为回文串  
 * if s[i]=s[j],f[i][j]=f[i-1][j+1] */
string longestPalindrome(string s) {  
    int n = s.size();  
    vector<vector<bool>> f(n, vector<bool>(n, true));  
    int k = 0, mx = 1;  
    for (int i = n - 2; i >= 0; i++) {  
        for (int j = i + 1; j < n; j++) {  
            f[i][j] = false;  
            if (s[i] == s[j]) {  
                f[i][j] = f[i + 1][j - 1];  
                if (f[i][j] && mx < j - i + 1) {  
                    mx = j - i + 1;  
                    k = i;  
                }  
            }  
        }  
    }  
    return s.substr(k, mx);  
}

// 中心扩展发
class Solution1 {  
public:  
    string palindrome(string s, int l, int r) {  
        int n = s.size();  
        while (l >= 0 && r < n && s[l] == s[r]) {  
            l--;  
            r++;  
        }  
        return s.substr(l + 1, r - l - 1);  
    }  
  
    string longestPalindrome(string s) {  
        string res = "";  
        for (int i = 0; i < s.length(); i++) {  
            string s1 = palindrome(s, i, i);  
            string s2 = palindrome(s, i, i + 1);  
            res = res.size() > s1.size() ? res : s1;  
            res = res.size() > s2.size() ? res : s2;  
        }  
        return res;  
    }  
};

2的1000次方

字符串乘法,腾讯面试题

#include <iostream>  
#include <string>  
  
using namespace std;  
  
string multiplyByTow(const string &num) {  
    string res;  
    int carry = 0;  
    for (int i = num.size() - 1; i >= 0; i--) {  
        int digit = num[i] - '0';  
        int product = digit * 2 + carry;  
        carry = product / 10;  
        res.insert(res.begin(), '0' + (product % 10));  
    }  
    while (carry > 0) {  
        res.insert(res.begin(), '0' + (carry % 10));  
        carry /= 10;  
    }  
    return res;  
}  
  
int main() {  
    string res = "1";  
    for (int i = 0; i < 1000; i++) {  
        res = multiplyByTow(res);  
    }  
    cout << "2^1000 = " << res << endl;  
}

14. 最长公共前缀

面试不会直接回家种田

string longestCommonPrefix(vector<string> &strs) {  
    for (int i = 0; i < strs[0].size(); i++) {  
        for (int j = 1; j < strs.size(); j++) {  
            if (i >= strs[j].size() || strs[j][i] != strs[0][i]) {  
                return strs[0].substr(0,i);  
            }  
        }  
    }  
    return strs[0];  
}

1768. 交替合并字符串

超级简单

string mergeAlternately(string word1, string word2) {  
    int m = word1.size(), n = word2.size();  
    string ans;  
    for (int i = 0; i < m || i < n; ++i) {  
        if (i < m) ans += word1[i];  
        if (i < n) ans += word2[i];  
    }  
    return ans;  
}

28. 找出字符串中第一个匹配项的下标 🌟🌟

KMP

void getNext(int *next, const string &s) {  
    int j = 0;  
    next[0] = 0;  
    for (int i = 1; i < s.size(); i++) {  
        while (j > 0 && s[i] != s[j]) {  
            j = next[j - 1];  
        }  
        if (s[i] == s[j]) {  
            j++;  
        }  
        next[i] = j;  
    }  
}  
  
int strStr(string haystack, string needle) {  
    if (needle.size() == 0) return 0;  
    int next[needle.size()];  
    getNext(next, needle);  
    int j = 0;  
    for (int i = 0; i < haystack.size(); i++) {  
        while (j > 0 && haystack[i] != needle[j]) {  
            j = next[j - 1];  
        }  
        if (haystack[i] == needle[j]) {  
            j++;  
        }  
        if (j == needle.size()) {  
            return (i - needle.size() + 1);  
        }  
    }  
    return -1;  
}

// 面试实在想不出来就直接暴力
int strStr(string s, string p) {
	int n = s.size(), m = p.size();
	for (int i = 0; i <= n - m; i++) {
		int j = i, k = 0;
		while (k < m && s[j] == p[k]) {
			j++;
			k++;
		}
		if (k == m) return i;
	}
	return -1;
}

面试题 01.06. 字符串压缩

string compressString(string s) {  
    if (s.size() == 0) return s;  
    string res = "";  
    int cnt = 1;  
    res += s[0];  
    for (int i = 1; i < s.size(); i++) {  
        if (s[i] == s[i - 1]) {  
            cnt++;  
        } else {  
            res += to_string(cnt);  
            res += s[i];  
            cnt = 1;  
        }  
    }  
    res+= to_string(cnt);  
    return res.size() < s.size() ? res : s;  
}

链表

206. 反转链表🌟

迭代和递归双方法,这个都不会回家种田吧

class Solution {  
public:  
    ListNode* reverseList(ListNode* head) {  
        ListNode *pre = nullptr;  
        ListNode *cur = head;  
        for(;cur!= nullptr;){  
            ListNode* next = cur->next;  
            cur->next = pre;  
            pre = cur;  
            cur = next;  
        }  
        return pre;  
    }  
};
#include<iostream>  
using namespace std;  
struct ListNode {  
    int val;  
    ListNode *next;  
  
    ListNode(int x) : val(x), next(nullptr) {}  
};  
  
ListNode *constructList(vector<int> vec) {  
    ListNode *dummy = new ListNode(0);  
    ListNode *cur = dummy;  
    int n = vec.size();  
    for (int i = 0; i < n; i++) {  
        ListNode *newNode = new ListNode(vec[i]);  
        cur->next = newNode;  
        cur = cur->next;  
    }  
    return dummy->next;  
}  
  
void printListNode(ListNode *head) {  
    if (head == nullptr) {  
        cout << "list is empty" << endl;  
        return;  
    }  
    ListNode *cur = head;  
    while (cur != nullptr) {  
        cout << cur->val << " ";  
        cur = cur->next;  
    }  
    cout << endl;  
}  
  
ListNode *reverseList(ListNode *head) {  
    if (head == nullptr || head->next == nullptr) return head;  
    ListNode *last = reverseList(head->next);  
    head->next->next = head;  
    head->next = nullptr;  
    return last;  
}  
  
int main() {  
    vector<int> vec;  
    int n, val;  
    cin >> n;  
    while (n--) {  
        cin >> val;  
        vec.push_back(val);  
    }  
    ListNode *head = constructList();  
    printListNode(head);  
    printListNode(reverseList(head));  
}

142. 环形链表 II

经典

ListNode *detectCycle(ListNode *head) {  
    ListNode *slow=head,*fast = head;  
    while(fast&& fast->next){  
        slow=slow->next;  
        fast=fast->next->next;  
        if(slow==fast){  
            slow=head;  
            while (slow!=fast){  
                slow=slow->next;  
                fast=fast->next;  
            }  
            return slow;  
        }  
    }  
    return nullptr;  
}

21. 合并两个有序链表 🌟🌟🌟

太过经典,和反转链表是链表面试题的半壁江山

ListNode *mergeTwoLists(ListNode *list1, ListNode *list2) {  
    ListNode *dummy = new ListNode(-1);  
    ListNode *cur = dummy;  
    for (; list1 && list2;cur=cur->next) {  
        if (list1->val <= list2->val) {  
            cur->next = list1;  
            list1 = list1->next;  
        }else{  
            cur->next = list2;  
            list2 = list2->next;  
        }  
    }  
    if(list1){  
        cur->next=list1;  
    }  
    if(list2){  
        cur->next= list2;  
    }  
    return dummy->next;  
}

2. 两数相加

ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
	ListNode *dummy = new ListNode();
	int carry = 0;
	ListNode *cur = dummy;
	while (l1 || l2 || carry) {
		int s = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + carry;
		carry = s / 10;
		cur->next = new ListNode(s % 10);
		cur = cur->next;
		l1 = l1?l1->next: nullptr;
		l2 = l2?l2->next: nullptr;
	}
	return dummy->next;
}

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

双指针,必背,必考

ListNode* removeNthFromEnd(ListNode* head, int n) {
	ListNode* dummy = new ListNode(0,head);
	ListNode* node = findKthFromEnd(dummy,n+1);
	node->next = node->next->next;
	return dummy->next;
}

ListNode* findKthFromEnd(ListNode* head, int k) {
	ListNode *fast = head, *slow = head;
	while (k--) fast = fast->next;
	while (fast) {
		fast = fast->next;
		slow = slow->next;
	}
	return slow;
}

24. 两两交换链表中的节点

ListNode *swapPairs(ListNode *head) {  
    if (head == nullptr || head->next == nullptr) return head;  
  
    ListNode* nextNode = head->next;  
    head->next = swapPairs(nextNode->next);  
    nextNode->next = head;  
    return nextNode;  
}

25. K 个一组翻转链表

困难

ListNode *reverseKGroup(ListNode *head, int k) {  
    if (k <= 1) return head;  
    ListNode *dummy = new ListNode(0, head);  
    ListNode *pre = dummy, *cur = dummy;  
    while (cur->next) {  
        for (int i = 0; i < k && cur != nullptr; i++) {  
            cur = cur->next;  
        }  
        if(cur== nullptr){  
            return dummy->next;  
        }  
        ListNode* nextGroupHead = cur->next;  
        cur->next= nullptr;  
        ListNode* start = pre->next;  
        pre->next = reverseList(start);  
        start->next = nextGroupHead;  
        pre = start;  
        cur = pre;  
    }  
    return dummy->next;  
}  
  
ListNode *reverseList(ListNode *head) {  
    if (head == nullptr || head->next == nullptr) return head;  
    ListNode *last = reverseList(head->next);  
    head->next->next = head;  
    head->next = nullptr;  
    return last;  
}

138. 随机链表的复制

Node *copyRandomList(Node *head) {  
    Node *dummy = new Node(-1);  
    Node *pre = dummy;  
    unordered_map<Node *, Node *> map;  
    for (auto cur = head; cur; cur = cur->next) {  
        pre->next = new Node(cur->val);  
        pre = pre->next;  
        map[cur] = pre;  
    }  
    pre = dummy->next;  
    for(auto cur = head;cur;cur=cur->next){  
        pre->random = map[cur->random];  
        pre = pre->next;  
    }  
    return dummy->next;  
}

148. 排序链表

归并排序

ListNode *sortList(ListNode *head) {  
    if (head == nullptr || head->next == nullptr) {  
        return head;  
    }  
  
    ListNode *midNode = middleNode(head);  
    ListNode *rightHead = midNode->next;  
    midNode->next = nullptr;  
  
    ListNode* left = sortList(head);  
    ListNode* right = sortList(rightHead);  
  
    return mergeTwoLists(left,right);  
}  
  
// 0876 变  
ListNode *middleNode(ListNode *head) {  
    if (head == nullptr || head->next == nullptr) {  
        return head;  
    }  
    ListNode *slow = head;  
    ListNode *fast = head->next->next;  
    while (fast != nullptr && fast->next != nullptr) {  
        slow = slow->next;  
        fast = fast->next->next;  
    }  
    return slow;  
}  
  
// 0021  
ListNode *mergeTwoLists(ListNode *list1, ListNode *list2) {  
    ListNode *dummy = new ListNode(-1);  
    ListNode *curr = dummy;  
    while (list1 != nullptr && list2 != nullptr) {  
        if (list1->val < list2->val) {  
            curr->next = list1;  
            list1 = list1->next;  
        } else {  
            curr->next = list2;  
            list2 = list2->next;  
        }  
        curr = curr->next;  
    }  
    curr->next = list1 != nullptr ? list1 : list2;  
    return dummy->next;  
}

23. 合并 K 个升序链表 🌟🌟🌟

ListNode* mergeKLists(vector<ListNode*>& lists) {  
    auto fn = [](auto a,auto b){  
        return a->val>b->val;  
    };  
    priority_queue<ListNode*,vector<ListNode*>,decltype(fn)> pq(fn);  
  
    for(auto x:lists){  
        if(x) pq.push(x);  
    }  
  
    ListNode* dummy = new ListNode(-1),*cur = dummy;  
    while(!pq.empty()){  
        ListNode* node = pq.top();pq.pop();  
        cur->next = node;  
        node = node->next;  
        if(node) pq.push(node);  
        cur=cur->next;  
    }  
    return dummy->next;  
}

归并排序

ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
	ListNode temp_head(0);
	ListNode *pre = &temp_head;
	while (l1 && l2){
		if (l1->val < l2->val){
			pre->next = l1;
			l1 = l1->next;
		}
		else{
			pre->next = l2;
			l2 = l2->next;
		}
		pre = pre->next;
	}
	if (l1){
		pre->next = l1;
	}
	if (l2){
		pre->next = l2;
	}
	return temp_head.next;
}	
ListNode* merge(vector <ListNode*>& lists,int l,int r){
	if(l==r) return lists[l];
	if(l>r) return nullptr;
	int mid=(l+r)>>1;
	return mergeTwoLists(merge(lists,l,mid),merge(lists,mid+1,r))   ;     
}
ListNode* mergeKLists(std::vector<ListNode*>& lists) {
	return merge(lists,0,lists.size()-1);
}

82. 删除排序链表中的重复元素 II

ListNode *deleteDuplicates(ListNode *head) {  
    ListNode *dummy = new ListNode(-1);  
    dummy->next = head;  
    ListNode *cur = head, *pre = dummy;  
    while (cur) {  
        while (cur->next && cur->val == cur->next->val) {  
            cur = cur->next;  
        }  
        if (pre->next == cur) {  
            pre = cur;  
        } else {  
            pre->next = cur->next;  
        }  
    }  
    return dummy->next;  
}

83. 删除排序链表中的重复元素

ListNode *deleteDuplicates(ListNode *head) {  
    ListNode* cur = head;  
    while (cur != nullptr && cur->next != nullptr) {  
        if (cur->val == cur->next->val) {  
            cur->next = cur->next->next;  
        } else {  
            cur = cur->next;  
        }  
    }  
    return head;  
}

设计

208. 实现 Trie (前缀树)

非常重要,还有他的扩展 基数树

class Trie {  
    bool end;  
    Trie *next[26];  
public:  
    Trie() : end(false) {  
        memset(next, 0, sizeof next);  
    }  
  
    void insert(string word) {  
        Trie *cur = this;  
        for (char c: word) {  
            if (cur->next[c - 'a'] == nullptr) {  
                cur->next[c - 'a'] = new Trie();  
            }  
            cur = cur->next[c - 'a'];  
        }  
        cur->end = true;  
    }  
  
    Trie *searchNode(string word) {  
        Trie *cur = this;  
        for (char c: word) {  
            if (cur->next[c - 'a'] == nullptr) {  
                return nullptr;  
            }  
            cur = cur->next[c - 'a'];  
        }  
        return cur;  
    }  
  
    bool search(string word) {  
        Trie *node = searchNode(word);  
        return node != nullptr && node->end;  
    }  
  
    bool startsWith(string prefix) {  
        return searchNode(prefix)!= nullptr;  
    }  
};

146. LRU 缓存

list+unordered_map,使用库函数更简单,但是常规写法还是手写双向链表,速度更快。

#include <iostream>  
#include <unordered_map>  
#include <list>  
  
using namespace std;  
  
class LRUCache {
  public:
    struct Node {
        int key;
        int val;
        Node *pre;
        Node *next;
        Node(int x, int y) : key(x), val(y) {
        }
    };
    Node *head, *last;
    unordered_map<int, Node *> map;
    int capacity;
    int len;

    LRUCache(int capacity) : capacity(capacity), len(0) {
        head = new Node(-1, -1);
        last = new Node(-1, -1);
        head->next = last;
        last->pre = head;
    }
    int get(int key) {
        if (map.count(key)) {
            Node *node = map[key];
            moveToHead(node);
            return node->val;
        }
        return -1;
    }

    void put(int key, int value) {
        if (map.count(key)) {
            Node *node = map[key];
            node->val = value;
            moveToHead(node);
        } else {
            len++;
            if (len > capacity) {
                len--;
                Node *delNode = last->pre;
                remove(delNode);
                map.erase(delNode->key);
            }
            Node *newNode = new Node(key, value);
            map[key] = newNode;
            newNode->next = head->next;
            head->next->pre = newNode;
            newNode->pre = head;
            head->next = newNode;
        }
    }

    void moveToHead(Node *node) {
        node->pre->next = node->next;
        node->next->pre = node->pre;
        node->next = head->next;
        head->next->pre = node;
        node->pre = head;
        head->next = node;
    }

    void remove(Node *node) {
        node->pre->next = node->next;
        node->next->pre = node->pre;
    }
};
  
int main() {  
    LRUCache lru(2);  
    lru.put(2,1);  
    lru.put(2,2);  
    cout<<lru.get(2)<<endl;  
    lru.put(1,1);  
    lru.put(4,1);  
    cout<<lru.get(2)<<endl;  
}

341. 扁平化嵌套列表迭代器

二叉树

104. 二叉树的最大深度

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

226. 翻转二叉树

TreeNode *invertTree(TreeNode *root) {  
    if (root == nullptr) return nullptr;  
    TreeNode *left = invertTree(root->left);  
    TreeNode *right = invertTree(root->right);  
  
    root->left = right;  
    root->right = left;  
    return root;  
}

101. 对称二叉树

bool isSymmetric(TreeNode *root) {
	return dfs(root,root);
}

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

543. 二叉树的直径

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

114. 二叉树展开为链表

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 *p = root;  
    while(p->right){  
        p=p->right;  
    }  
    p->right = right;  
}

437. 路径总和 III

int rootSum(TreeNode *root, long targetSum) {  
    if (!root) {  
        return 0;  
    }  
  
    int ret = 0;  
    if (root->val == targetSum) {  
        ret++;  
    }  
  
    ret += rootSum(root->left, targetSum - root->val);  
    ret += rootSum(root->right, targetSum - root->val);  
    return ret;  
}  
  
int pathSum(TreeNode *root, int targetSum) {  
    if (!root) {  
        return 0;  
    }  
  
    int ret = rootSum(root, targetSum);  
    ret += pathSum(root->left, targetSum);  
    ret += pathSum(root->right, targetSum);  
    return ret;  
}

236. 二叉树的最近公共祖先

func lowestCommonAncestor236(root, p, q *TreeNode) *TreeNode {
    if root == nil || root == q || root == p {
            return root
    }
    left := lowestCommonAncestor236(root.Left, p, q)
    right := lowestCommonAncestor236(root.Right, p, q)
    if left != nil {
            if right != nil {
                    return root
            }
            return left
    }
    return right
}

124. 二叉树中的最大路径和 🌟🌟

最简单的困难题

class Solution {
public:
    int res=INT_MIN;
    int maxPathSum(TreeNode* root) {
        maxSide(root);
        return res;
    }
    int maxSide(TreeNode* root){
        if(!root) return 0;
        int left = maxSide(root->left);
        left = max(0,left);
        int right = maxSide(root->right);
        right = max(0,right);

        res=max(res,left+right+root->val);
        return root->val+max(left,right);
    }
};

层序遍历

102. 二叉树的层序遍历 必须掌握

vector<vector<int>> levelOrder(TreeNode *root) {  
	vector<vector<int>> res;  
	if (root == nullptr) return res;  
	queue<TreeNode *> q;  
	q.push(root);  
	while (!q.empty()) {  
		int n = q.size();  
		vector<int> temp;  
		for(int i=0;i<n;i++){  
			TreeNode* cur = q.front();  
			q.pop();  
			temp.push_back(cur->val);  
			if(cur->left) q.push(cur->left);  
			if(cur->right) q.push(cur->right);  
		}  
		res.push_back(temp);  
	}
	return res;  
}  

扩展dfs方法 107. 二叉树的层序遍历 II

vector<vector<int>> levelOrderBottom(TreeNode *root) {  
	vector<vector<int>> ans;  
	dfs(ans, root, 0);  
	reverse(ans.begin(), ans.end());// 反转  
	return ans;  
}  

void dfs(vector<vector<int>> &ans, TreeNode *root, int level) {  
	if (root == nullptr)  
		return;  
	// 初始化下一层的集合  
	if (level == ans.size()) {  
		vector<int> tmp;  
		ans.emplace_back(tmp);  
	}  
	// 把节点的值添加到对应的集合中  
	ans[level].push_back(root->val);  
	// 递归左右子树  
	dfs(ans, root->left, level + 1);  
	dfs(ans, root->right, level + 1);  
}

构造

105. 从前序与中序遍历序列构造二叉树 🌟🌟

重建二叉树_牛客题霸_牛客网 前中序构造二叉树, 太经典了

TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
	unordered_map<int,int>map;
	for(int i=0;i<inorder.size();i++){
		map[inorder[i]] = i;
	}
	
	function<TreeNode*(int,int,int)> build = [&](int i,int j,int n)->TreeNode*{
		if(n<=0) return nullptr;
		TreeNode* root = new TreeNode(preorder[i]);
		int mid = map[preorder[i]];
		int l = mid - j;
		root->left = build(i+1,j,l);
		root->right = build(i+l+1,mid+1,n-l-1);
		return root; 
	};
	return build(0,0,preorder.size());
}

二叉搜索树

108. 将有序数组转换为二叉搜索树

TreeNode *sortedArrayToBST(vector<int> &nums) {  
    return dfs(0,nums.size()-1,nums);  
}  
  
TreeNode *dfs(int l, int r, vector<int> &nums) {  
    if (l > r) return nullptr;  
    int mid = (l + r) >> 1;  
    TreeNode *root = new TreeNode(nums[mid]);  
    root->left = dfs(l, mid - 1,nums);  
    root->right = dfs(mid + 1, r,nums);  
    return root;  
}

98. 验证二叉搜索树

class Solution {  
public:  
    long pre = LONG_MIN;  
  
    bool isValidBST(TreeNode *root) {  
        if(root== nullptr) return true;  
        if(!isValidBST(root->left)) return false;  
        if(root->val<=pre) return false;  
        pre = root->val;  
        if(!isValidBST(root->right)) return false;  
        return true;  
    }  
};

230. 二叉搜索树中第K小的元素 🌟

迭代遍历更加简单

int kthSmallest(TreeNode *root, int k) {  
    stack<TreeNode *> st;  
    while (root || !st.empty()) {  
        if (root) {  
            st.push(root);  
            root = root->left;  
        } else {  
            root = st.top();  
            st.pop();  
            if (--k == 0) return root->val;  
            root = root->right;  
        }  
    }  
    return 0;  
}

// 递归法
class Solution {
public:
    int k;
    int kthSmallest(TreeNode* root, int k) {
        this->k= k;
        inorder(root);
        return res;
    }

    int kth = 0;
    int res;
    void  inorder(TreeNode* root){
        if(!root) return;
        inorder(root->left);
        kth++;
        if(kth==k){
            res = root->val;
            return;
        }
        inorder(root->right);
    }
};

岛屿问题 dfs 版

如果面试出现图的问题,90%的可能性是岛屿问题
网格dfs遍历框架代码

// - 0 —— 海洋格子 - 1 —— 陆地格子(未遍历过) - 2 —— 陆地格子(已遍历过)
int dirs[5] = {-1, 0, 1, 0, -1};  
function<void(int, int)> dfs = [&](int i, int j) {  
    grid[i][j] = '2';  
    for (int k = 0; k < 4; ++k) {  
        int x = i + dirs[k], y = j + dirs[k + 1];  
        if (x >= 0 && x < grid.size() && y >= 0 && y < grid[0].size() && grid[x][y] == '1') {  
            dfs(x, y);  
        }  
    }  
};

200. 岛屿数量

class Solution {  
public:  
    int numIslands(vector<vector<char>>& grid) {  
        int m = grid.size();  
        int n = grid[0].size();  
        int ans = 0;  
        int dirs[5] = {-1, 0, 1, 0, -1};  
        function<void(int, int)> dfs = [&](int i, int j) {  
            grid[i][j] = '2';  
            for (int k = 0; k < 4; ++k) {  
                int x = i + dirs[k], y = j + dirs[k + 1];  
                if (x >= 0 && x < grid.size() && y >= 0 && y < grid[0].size() && grid[x][y] == '1') {  
                    dfs(x, y);  
                }  
            }  
        };  
        for (int i = 0; i < m; ++i) {  
            for (int j = 0; j < n; ++j) {  
                if (grid[i][j] == '1') {  
                    dfs(i, j);  
                    ++ans;  
                }  
            }  
        }  
        return ans;  
    }  
};

994. 腐烂的橘子

int orangesRotting(vector<vector<int>> &grid) {
	int rows = grid.size(), cols = grid[0].size(), count = 0;
	int dis[10][10];
	memset(dis, -1, sizeof(dis));

	vector<pair<int, int>> directions = {{1,  0},
										 {-1, 0},
										 {0,  1},
										 {0,  -1}};

	queue<pair<int, int>> que;

	for (int i = 0; i < rows; i++) {
		for (int j = 0; j < cols; j++) {
			if (grid[i][j] == 2) {
				que.push(make_pair(i, j));
				dis[i][j] = 0;
			}
			if (grid[i][j] == 1) {
				count++;
			}
		}
	}

	int time=0;
	while (!que.empty()) {
		auto [i, j] = que.front();
		time = dis[i][j];
		que.pop();
		for (const auto &dir: directions) {
			int ni = i + dir.first;
			int nj = j + dir.second;

			if (ni >= 0 && ni < rows && nj >= 0 && nj < cols && grid[ni][nj] == 1) {
				grid[ni][nj] = 2;
				count--;
				que.push(std::make_pair(ni, nj));
				dis[ni][nj] = time+1;
			}
		}
	}
	return count == 0 ? time : -1;
}

207. 课程表

bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
	vector<int> indegrees(numCourses,0);
	vector<vector<int>> edges(numCourses);
	for(const auto& info:prerequisites){
		edges[info[1]].push_back(info[0]);
		++indegrees[info[0]];
	}

	queue<int> que;
	for(int i=0;i<numCourses;i++){
		if(indegrees[i]==0)
			que.push(i);
	}

	while(!que.empty()){
		int pre = que.front();que.pop();
		numCourses--;
		for(auto &cur:edges[pre]){
			if(--indegrees[cur]==0)
				que.push(cur);
		}
	}
	return numCourses==0;
}

210. 课程表 II

class Solution {
private:
    // 存储有向图
    vector<vector<int>> edges;
    // 标记每个节点的状态:0=未搜索,1=搜索中,2=已完成
    vector<int> visited;
    // 用数组来模拟栈,下标 0 为栈底,n-1 为栈顶
    vector<int> result;
    // 判断有向图中是否有环
    bool valid = true;

public:
    void dfs(int u) {
        // 将节点标记为「搜索中」
        visited[u] = 1;
        // 搜索其相邻节点
        // 只要发现有环,立刻停止搜索
        for (int v: edges[u]) {
            // 如果「未搜索」那么搜索相邻节点
            if (visited[v] == 0) {
                dfs(v);
                if (!valid) {
                    return;
                }
            }
            // 如果「搜索中」说明找到了环
            else if (visited[v] == 1) {
                valid = false;
                return;
            }
        }
        // 将节点标记为「已完成」
        visited[u] = 2;
        // 将节点入栈
        result.push_back(u);
    }

    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        edges.resize(numCourses);
        visited.resize(numCourses);
        for (const auto& info: prerequisites) {
            edges[info[1]].push_back(info[0]);
        }
        // 每次挑选一个「未搜索」的节点,开始进行深度优先搜索
        for (int i = 0; i < numCourses && valid; ++i) {
            if (!visited[i]) {
                dfs(i);
            }
        }
        if (!valid) {
            return {};
        }
        // 如果没有环,那么就有拓扑排序
        // 注意下标 0 为栈底,因此需要将数组反序输出
        reverse(result.begin(), result.end());
        return result;
    }
};

回溯

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

排列、组合|子集 🌟🌟

img
// 78 子集
vector<vector<int>> subsets(vector<int> &nums) {
    backtrace(nums, 0);
    return res;
}
vector<vector<int>> res;
vector<int> track;
void backtrace(vector<int> &nums, int start) {
    res.push_back(track);
    for (int i = start; i < nums.size(); i++) {
        track.push_back(nums[i]);
        backtrace(nums, i + 1);
        track.pop_back();
    }
}
// 77 组合
vector<vector<int>> combine(int n, int k) {
    backtrace(n, k, 0);
    return res;
}
vector<vector<int>> res;
vector<int> track;
void backtrace(int n, int k, int start) {
    if (track.size() == k) {
        res.push_back(track);
        return;
    }
    for (int i = start; i < n; i++) {
        track.push_back(i + 1);
        backtrace(n, k, i + 1);
        track.pop_back();
    }
}
img
// 46 全排列
vector<vector<int>> permute(vector<int> &nums) {
    visited = vector<bool>(nums.size());
    backtrace(nums);
    return res;
}

vector<vector<int>> res;
vector<int> track;
vector<bool> visited;

void backtrace(vector<int> nums) {
    if (nums.size() == track.size()) {
        res.push_back(track);
        return;
    }
    for (int i = 0; i < nums.size(); i++) {
        if (visited[i]) continue;
        visited[i] = true;
        track.push_back(nums[i]);
        backtrace(nums);
        track.pop_back();
        visited[i] = false;
    }
}

39. 组合总和

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    vector<int> candidates;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        this->candidates = candidates;
        backtrace(0, target);
        return res;
    }
    void backtrace(int start, int target) {
        if (target == 0) {
            res.push_back(path);
            return;
        }
        if (target < 0) {
            return;
        }
        for (int i = start; i < candidates.size(); i++) {
            path.push_back(candidates[i]);
            backtrace(i, target - candidates[i]);
            path.pop_back();
        }
    }
};

小于n的最大数

数组A中给定可以使用的1~9的数,返回由A数组中的元素组成的小于n的最大数。例如A={1, 2, 4, 9},x=2533,返回2499

#include <iostream>
#include <vector>
using namespace std;

vector<int> choice={1, 2, 4, 9};
int num = 0,res=0;
int target=2533;

void backtrace(int num) {
    for(int i=0;i<choice.size();i++){
        if(num*10+choice[i]>target){
            return;
        }else{
            res = max(res,num*10+choice[i]);
            backtrace(num*10+choice[i]);
        }
    }
}

int main() {
    backtrace(0);
    cout<<res;
}

17. 电话号码的字母组合

class Solution {
public:
    unordered_map<char, string> phoneMap{
            {'2', "abc"},
            {'3', "def"},
            {'4', "ghi"},
            {'5', "jkl"},
            {'6', "mno"},
            {'7', "pqrs"},
            {'8', "tuv"},
            {'9', "wxyz"}
    };

    void backtrace(vector<string> &res, const string &digits, string &combination) {
        int index = combination.size();
        if (index == digits.size()) {
            res.push_back(combination);
            return;
        }

        char digit = digits[index++];
        const string &letters = phoneMap[digit];
        for (const char &letter: letters) {
            combination.push_back(letter);
            backtrace(res,digits,combination);
            combination.pop_back();
        }
    }

    vector<string> letterCombinations(string digits) {
        vector<string> res;
        if (digits == "")return res;
        string combination = "";
        backtrace(res, digits, combination);
        return res;
    }
};

22. 括号生成

void backtrace(string &cur, vector<string> &res, int n, int open, int close) {
	if (cur.size() == 2 * n) {
		res.push_back(cur);
		return;
	}
	if (open < n) {
		cur.push_back('(');
		backtrace(cur, res, n, open + 1, close);
		cur.pop_back();
	}
	if (close < open) {
		cur.push_back(')');
		backtrace(cur, res, n, open, close + 1);
		cur.pop_back();
	}
}

vector<string> generateParenthesis(int n) {
	vector<string> res;
	string cur;
	backtrace(cur, res, n, 0, 0);
	return res;
}

79. 单词搜索

class Solution {
public:
    string word;
    vector<vector<char>> board;
    bool valid(int x,int y,int k){
        if(x>=0&&x<board.size()&&y>=0&&y<board[0].size()&&board[x][y]==word[k]) return true;
        return false;
    }
    bool dfs(int x,int y,int k){
        if(k>=word.size()) return true;
        if(!valid(x,y,k)) return false;
        // cout<<x<<" "<<y<<" "<<board[x][y]<<endl;
        board[x][y]='.';
        int dirs[5]={-1,0,1,0,-1};
        for(int i=0;i<4;i++){
            int ax = x+dirs[i],ay = y+dirs[i+1];
            if(dfs(ax,ay,k+1)) return true;
        }
        board[x][y]=word[k];
        return false;
    }
    bool exist(vector<vector<char>>& board, string word) {
        this->board = board;
        this->word = word;
        int m = board.size(),n=board[0].size();
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                if(dfs(i,j,0)) return true;
            }
        }
        return false;
    }
};

131. 分割回文串

class Solution {
public:
    vector<vector<string>> partition(string s) {
        int n = s.size();
        bool f[n][n];
        memset(f, true, sizeof(f));
        for (int i = n - 1; i >= 0; --i) {
            for (int j = i + 1; j < n; ++j) {
                f[i][j] = s[i] == s[j] && f[i + 1][j - 1];
            }
        }
        vector<vector<string>> ans;
        vector<string> t;
        function<void(int)> dfs = [&](int i) {
            if (i == n) {
                ans.push_back(t);
                return;
            }
            for (int j = i; j < n; ++j) {
                if (f[i][j]) {
                    t.push_back(s.substr(i, j - i + 1));
                    dfs(j + 1);
                    t.pop_back();
                }
            }
        };
        dfs(0);
        return ans;
    }
};

//或
bool isPalindrome(const string &s, int start, int end) {
	for (int i = start, j = end; i < j; i++, j--) {
		if (s[i] != s[j])
			return false;
	}
	return true;
}

51. N 皇后

class Solution {
public:
    vector<vector<string >> res;

    bool isValid(vector<string> &board, int row, int col) {
        int n = board.size();
        for (int i = 0; i < row; i++) {
            if (board[i][col] == 'Q') return false;
        }
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
            if(board[i][j]=='Q' ) return false;
        }
        for(int i=row-1,j=col+1;i>=0&&j<n;i--,j++){
            if(board[i][j]=='Q' ) return false;
        }
        return true;
    }

    void backtrack(vector<string> &board, int row) {
        if (row == board.size()) {
            res.push_back(board);
            return;
        }

        int n = board[row].size();
        for (int col = 0; col < n; col++) {
            if (isValid(board, row, col)) {
                board[row][col] = 'Q';
                backtrack(board, row + 1);
                board[row][col] = '.';
            }
        }
    }

    vector<vector<string >> solveNQueens(int n) {
        vector<string> board(n, string(n, '.'));
        backtrack(board, 0);
        return res;
    }
};

排序与搜索

35. 搜索插入位置

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

215. 数组中的第K个最大元素 🌟🌟🌟

经典快速选择排序 或 使用 堆(手写堆)

int findKthLargest(vector<int> &nums, int k) {  
    int n = nums.size();  
    return quicksort(nums, 0, n - 1, n - k);  
}  
  
int quicksort(vector<int> &nums, int l, int r, int k) {  
    if (l == r) return nums[l];  
    int i = l - 1, j = r + 1;  
    int x = nums[l + r >> 1];  
    while (i < j) {  
        do i++; while(nums[i] < x); 
        do j--; while(nums[j] > x);  
        if (i < j) swap(nums[i], nums[j]);  
    }  
    return k<=j?quicksort(nums, l, j, k):quicksort(nums, j + 1, r, k);  
}

快速排序

void quick_sort(int q[], int l, int r)
{
    if (l >= r) return;
    int i = l - 1, j = r + 1, x = q[l + r >> 1];
    while (i < j)
    {
        do i ++ ; while (q[i] < x);
        do j -- ; while (q[j] > x);
        if (i < j) swap(q[i], q[j]);
    }
    quick_sort(q, l, j), quick_sort(q, j + 1, r);
}

归并排序 🌟🌟

void merge_sort(int q[], int l, int r)
{
    if (l >= r) return;

    int mid = l + r >> 1;
    merge_sort(q, l, mid);
    merge_sort(q, mid + 1, r);

    int k = 0, i = l, j = mid + 1, tmp[r-l+1];
    while (i <= mid && j <= r)
        if (q[i] <= q[j]) tmp[k ++ ] = q[i ++ ];
        else tmp[k ++ ] = q[j ++ ];

    while (i <= mid) tmp[k ++ ] = q[i ++ ];
    while (j <= r) tmp[k ++ ] = q[j ++ ];

    for (i = l, j = 0; i <= r; i ++, j ++ ) q[i] = tmp[j];
}

二分搜索🌟

lower_bound(row.begin(), row.end(), target) // cpp  二分查找找到第一个大于等于 target 的元素
sort.SearchInts(row, target) // go

// 红蓝分区 神中神
int l = -1,r = n;
while(l+1 != r)
{
    int mid = l + (r-l >>1);
    if(check()) l=mid;
    else r=mid;
    //最后根据你所分左右两边区间的结果
    //选取L或者R作为结果
}


// 经典方法 双闭区间
int binary_search(vector<int>& nums, int target) {
    int left = 0, right = nums.size()-1;
    while(left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else if(nums[mid] == target) {
            return mid;
        }
    }
    return -1;
}

int left_bound(vector<int>& nums, int target) {
    int left = 0, right = nums.size()-1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 别返回,锁定左侧边界
            right = mid - 1;
        }
    }
    // 判断 target 是否存在于 nums 中
    if (left < 0 || left >= nums.size()) {
        return -1;
    }
    // 判断一下 nums[left] 是不是 target
    return nums[left] == target ? left : -1;
}

int right_bound(vector<int>& nums, int target) {
    int left = 0, right = nums.size()-1;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid - 1;
        } else if (nums[mid] == target) {
            // 别返回,锁定右侧边界
            left = mid + 1;
        }
    }
    if (right < 0 || right >= nums.size()) {
        return -1;
    }
    return nums[right] == target ? right : -1;
}

154. 寻找旋转排序数组中的最小值 II

class Solution {
public:
    int findMin(vector<int>& nums) {
        int i = 0, j = nums.size() - 1;
        while (i < j) {
            int m = (i + j) / 2;
            if (nums[m] > nums[j]) i = m + 1;
            else if (nums[m] < nums[j]) j = m;
            else j--;
        }
        return nums[i];
    }
};

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

class Solution {
public:
    int left_bound(vector<int>& nums, int target) {
        int left = 0, right = nums.size()-1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] == target) {
                // 别返回,锁定左侧边界
                right = mid - 1;
            }
        }
        // 判断 target 是否存在于 nums 中
        if (left < 0 || left >= nums.size()) {
            return -1;
        }
        // 判断一下 nums[left] 是不是 target
        return nums[left] == target ? left : -1;
    }
    int right_bound(vector<int>& nums, int target) {
        int left = 0, right = nums.size()-1;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] < target) {
                left = mid + 1;
            } else if (nums[mid] > target) {
                right = mid - 1;
            } else if (nums[mid] == target) {
                // 别返回,锁定右侧边界
                left = mid + 1;
            }
        }
        if (right < 0 || right >= nums.size()) {
            return -1;
        }
        return nums[right] == target ? right : -1;
    }
    vector<int> searchRange(vector<int>& nums, int target) {
        int left = left_bound(nums,target);
        int right = right_bound(nums,target);
        return {left,right};
    }
};

33. 搜索旋转排序数组

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

155. 最小栈

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

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

394. 字符串解码

string decodeString(string s) {
	stack<int> nums;
	stack<string> strs;
	string res = "";

	int num = 0, n = s.size();

	for (auto c : s) {
		if (isdigit(c)) {
			num = num * 10 + c - '0';
		} else if (c >= 'a' && c <= 'z') {
			res = res + c;
		} else if (c == '[') // 将‘[’前的数字压入nums栈内,
							 // 字母字符串压入strs栈内
		{
			nums.push(num);
			num = 0;
			strs.push(res);
			res = "";
		} else // 遇到‘]’时,操作与之相配的‘[’之间的字符,使用分配律
		{
			int k = nums.top();
			nums.pop();
			for (int j = 0; j < k; ++j)
				strs.top() += res;
			res =
				strs.top(); // 之后若还是字母,就会直接加到res之后,因为它们是同一级的运算
			// 若是左括号,res会被压入strs栈,作为上一层的运算
			strs.pop();
		}
	}
	return res;
}

20. 有效的括号🌟🌟🌟

栈的最典型应用题

class Solution {
public:
    bool isValid(string s) {
        stack<char> st;
        for (const char c: s) {
            if(c=='('){
                st.push(')');
            }else if(c=='['){
                st.push(']');
            }else if(c=='{'){
                st.push('}');
            }else{
                if(st.empty()||st.top()!=c){
                    return false;
                }else{
                    st.pop();
                }
            }
        }
        return st.empty();
    }
};

出栈合法性

典中典

#include <iostream>  
#include <stack>  
using namespace std;  
  
int main() {  
    int n;  
    int nums[105];  
    while (cin >> n) {  
        if (n == 0) break;  
        for (int i = 0; i < n; i++) {  
            cin >> nums[i];  
        }  
  
        stack<int> st;  
        int index = 0;  
        for (int i = 1; i <= n; i++) {  
            st.push(i);  
            while (!st.empty() && st.top() == nums[index]) {  
                st.pop();  
                index++;  
            }  
        }  
        if (st.empty() && index == n) {  
            cout << "Yes" << endl;  
        } else {  
            cout << "No" << endl;  
        }  
    }  
}

739. 每日温度

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

84. 柱状图中最大的矩形

int largestRectangleArea(vector<int> &heights)
{
	int n = heights.size();
	stack<int> st;
	int res = 0;
	heights.insert(heights.begin(), 0);
	heights.push_back(0);

	for (int i = 0; i < heights.size(); i++)
	{
		while(!st.empty()&&heights[st.top()]>heights[i]){
			int cur = st.top();
			st.pop();
			int w = i-1-st.top();
			res = max(res,w*heights[cur]);
		}
		st.push(i);
	}
	return res;
}

priority_queue<int> max_heap; // 默认为最大堆
priority_queue<int, vector<int>, greater<int>> min_heap; // 最小堆
// 比较函数是子与父比较

703. 数据流中的第 K 大元素

优先级队列

class KthLargest {
public:
    priority_queue<int, vector<int>, greater<int>> q;
    int kth;

    KthLargest(int k, vector<int> &nums) {
        kth = k;
        for (auto &x: nums) {
            add(x);
        }
    }

    int add(int val) {
        q.push(val);
        if(q.size()>kth){
            q.pop();
        }
        return q.top();
    }
};

347. 前 K 个高频元素

vector<int> topKFrequent(vector<int> &nums, int k) {
	unordered_map<int, int> occurrences;
	for (auto &v: nums) {
		occurrences[v]++;
	}

	auto myComparison = [](const pair<int, int> &a, const pair<int, int> &b) {
		return a.second > b.second; // 升序排序
	};
	
	// pair 的第一个元素代表数组的值,第二个元素代表了该值出现的次数
	priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(myComparison)> q(myComparison);
	for (auto &[num, count]: occurrences) {
		if (q.size() == k) {
			if (q.top().second < count) {
				q.pop();
				q.emplace(num, count);
			}
		} else {
			q.emplace(num, count);
		}
	}
	vector<int> ret;
	while (!q.empty()) {
		ret.emplace_back(q.top().first);
		q.pop();
	}
	return ret;
}

295. 数据流的中位数

class MedianFinder {
public:
    /** initialize your data structure here. */
    MedianFinder() {
    }

    void addNum(int num) {
        q1.push(num);
        q2.push(q1.top());
        q1.pop();
        if (q2.size() - q1.size() > 1) {
            q1.push(q2.top());
            q2.pop();
        }
    }

    double findMedian() {
        if (q2.size() > q1.size()) {
            return q2.top();
        }
        return (double) (q1.top() + q2.top()) / 2;
    }

private:
    priority_queue<int, vector<int>, greater<int>> q1;
    priority_queue<int> q2;
};

贪心

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

可能是最简单的贪心题了,也可以用动态规划,稍难

int maxProfit(vector<int> &prices) {  
    int n = prices.size();  
    int ans=0;  
    for (int i = 0; i < n - 1; i++) {  
        ans+=max(0,(prices[i+1]-prices[i]));  
    }  
    return ans;  
}
// 动规
int maxProfit(vector<int>& prices) {
	int n = prices.size();
	int f[n][2];
	f[0][0] = -prices[0];
	f[0][1] = 0;
	for (int i = 1; i < n; ++i) {
		f[i][0] = max(f[i - 1][0], f[i - 1][1] - prices[i]);
		f[i][1] = max(f[i - 1][1], f[i - 1][0] + prices[i]);
	}
	return f[n - 1][1];
}

55. 跳跃游戏

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

45. 跳跃游戏 II

class Solution {
public:
    int jump(vector<int>& nums) {
       int n=nums.size();
       if(n<2){
           return 0;
       }
       int curr_max=nums[0];
       int pre_max=nums[0];
       int jumpMin=1;
       for(int i=0;i<n;i++){
           if(i>curr_max){
                curr_max=pre_max;
                jumpMin++;
                if(curr_max>=n-1){
                    return jumpMin;
                }
           }
           if(pre_max<i+nums[i]){
               pre_max=i+nums[i];
           }
       }
       return jumpMin;
    }
};

763. 划分字母区间

class Solution {
public:
    vector<int> partitionLabels(string s) {
        int last[26];
        int length = s.size();
        for (int i = 0; i < length; i++) {
            last[s[i] - 'a'] = i;
        }
        vector<int> partition;
        int start = 0, end = 0;
        for (int i = 0; i < length; i++) {
            end = max(end, last[s[i] - 'a']);
            if (i == end) {
                partition.push_back(end - start + 1);
                start = end+1;
            }
        }
        return partition;
    }
};

动态规划

70. 爬楼梯

唯一会的动态规划😅

class Solution {  
public:  
    int climbStairs(int n) {  
        if (n <= 2) return n;  
  
        int dp[2] = {1, 2};  
        for (int i = 3; i <= n; i++) {  
            int next = dp[0] + dp[1];  
            dp[0] = dp[1];  
            dp[1] = next;  
        }  
        return dp[1];  
    }  
};

118. 杨辉三角

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> ret(numRows);
        for (int i = 0; i < numRows; ++i) {
            ret[i].resize(i + 1);
            ret[i][0] = ret[i][i] = 1;
            for (int j = 1; j < i; ++j) {
                ret[i][j] = ret[i - 1][j] + ret[i - 1][j - 1];
            }
        }
        return ret;
    }
};

198. 打家劫舍

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

279. 完全平方数

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

322. 零钱兑换

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

139. 单词拆分

bool wordBreak(string s, vector<string>& wordDict) {  
    unordered_set<string> words(wordDict.begin(), wordDict.end());  
    int n = s.size();  
    bool f[n + 1];  
    memset(f, false, sizeof(f));  
    f[0] = true;  
    for (int i = 1; i <= n; ++i) {  
        for (int j = 0; j < i; ++j) {  
            if (f[j] && words.count(s.substr(j, i - j))) {  
                f[i] = true;  
                break;  
            }  
        }  
    }  
    return f[n];  
}

140. 单词拆分 II

class Solution {
private:
    unordered_map<int, vector<string>> ans;
    unordered_set<string> wordSet;

public:
    vector<string> wordBreak(string s, vector<string>& wordDict) {
        wordSet = unordered_set(wordDict.begin(), wordDict.end());
        backtrack(s, 0);
        return ans[0];
    }

    void backtrack(const string& s, int index) {
        if (!ans.count(index)) {
            if (index == s.size()) {
                ans[index] = {""};
                return;
            }
            ans[index] = {};
            for (int i = index + 1; i <= s.size(); ++i) {
                string word = s.substr(index, i - index);
                if (wordSet.count(word)) {
                    backtrack(s, i);
                    for (const string& succ: ans[i]) {
                        ans[index].push_back(succ.empty() ? word : word + " " + succ);
                    }
                }
            }
        }
    }
};

1143. 最长公共子序列

class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        int m = text1.length(), n = text2.length();
        vector<vector<int>> dp(m + 1, vector<int>(n + 1));
        for (int i = 1; i <= m; i++) {
            char c1 = text1.at(i - 1);
            for (int j = 1; j <= n; j++) {
                char c2 = text2.at(j - 1);
                if (c1 == c2) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        return dp[m][n];
    }
};

32. 最长有效括号

class Solution {
public:
    int longestValidParentheses(string s) {
        int size = s.length();
        vector<int> dp(size, 0);

        int maxVal = 0;
        for(int i = 1; i < size; i++) {
            if (s[i] == ')') {
                if (s[i - 1] == '(') {
                    dp[i] = 2;
                    if (i - 2 >= 0) {
                        dp[i] = dp[i] + dp[i - 2];
                    }
                } else if (dp[i - 1] > 0) {
                    if ((i - dp[i - 1] - 1) >= 0 && s[i - dp[i - 1] - 1] == '(') {
                        dp[i] = dp[i - 1] + 2;
                        if ((i - dp[i - 1] - 2) >= 0) {
                            dp[i] = dp[i] + dp[i - dp[i - 1] - 2];
                        }
                    }
                }
            }
            maxVal = max(maxVal, dp[i]);
        }
        return maxVal;
    }
};

300. 最长递增子序列

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

91. 解码方法

int numDecodings(string s) {  
    int n = s.size();  
    int f[n + 1];  
    memset(f, 0, sizeof f);  
    f[0] = 1;  
    for (int i = 1; i <= n; i++) {  
        if (s[i - 1] != '0') {  
            f[i] = f[i - 1];  
        }  
        if (i > 1 && (s[i - 2] == '1' || s[i - 2] == '2' && s[i - 1] <= '6')) {  
            f[i] += f[i - 2];  
        }  
    }  
    return f[n];  
}

121. 买卖股票的最佳时机

枚举+维护前缀最小值 (也可以使用单调栈),子问题见 贪心:122

class Solution {
public:
    int maxProfit(vector<int> &prices) {
        int ans = 0, mi = prices[0];
        for (auto v: prices) {
            ans = max(ans,v-mi);
            mi = min(mi,v);
        }
        return ans;
    }
};

10. 正则表达式匹配 🌟

虽然是困难题,但是面试特别容易考,不会写就背
定义 f[i][j] 表示字符串s的前 i 个字符和字符串p的前 j 个字符是否匹配。那么答案就是fmn。初始化f[0][0]= true,表示
空字符串和空正则表达式是匹配的。

  • 如果 $p[j - 1]$ 是 '*',那么我们可以选择匹配 $0$ 个 $s[i - 1]$ 字符,那么就是 $f[i][j] = f[i][j - 2]$。如果此时 $s[i - 1]$ 和 $p[j - 2]$ 匹配,那么我们可以选择匹配 $1$ 个 $s[i - 1]$ 字符,那么就是 $f[i][j] = f[i][j] \lor f[i - 1][j]$。
  • 如果 $p[j - 1]$ 不是 '*',那么如果 $s[i - 1]$ 和 $p[j - 1]$ 匹配,那么就是 $f[i][j] = f[i - 1][j - 1]$。否则匹配失败。
bool isMatch(string s, string p) {  
    int m = s.size(), n = p.size();  
    bool f[m + 1][n + 1];  
    memset(f, false, sizeof f);  
    f[0][0] = true;  
    for (int i = 0; i <= m; i++) {  
        for (int j = 1; j <= n; j++) {  
            if (p[j - 1] == '*') {  
                f[i][j] = f[i][j - 2];  
                if (i && (p[j - 2] == '.' || p[j - 2] == s[i - 1])) {  
                    f[i][j] |= f[i - 1][j];  
                }  
            } else if (i && (p[j - 1] == '.' || p[j - 1] == s[i - 1])) {  
                f[i][j] = f[i - 1][j - 1];  
            }  
        }  
    }  
    return f[m][n];  
}

72. 编辑距离

leetcode 解题

int minDistance(string word1, string word2) {  
    int m = word1.size(), n = word2.size();  
    int f[m + 1][n + 1];  
    for (int i = 0; i <= n; i++) {  
        f[0][i] = i;  
    }  
    for (int j = 0; j <= m; j++) {  
        f[j][0] = j;  
    }  
    for (int i = 1; i <= m; i++) {  
        for (int j = 1; j <= n; j++) {  
            if(word1[i-1]==word2[j-1]){  
                f[i][j] = f[i - 1][j - 1];  
            } else {  
                f[i][j] = min({f[i - 1][j], f[i][j - 1], f[i - 1][j - 1]}) + 1;  
            }  
        }  
    }  
    return f[m][n];  
}

152. 乘积最大子数组

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int f = nums[0], g = nums[0], ans = nums[0];
        for (int i = 1; i < nums.size(); ++i) {
            int ff = f, gg = g;
            f = max({nums[i], ff * nums[i], gg * nums[i]});
            g = min({nums[i], ff * nums[i], gg * nums[i]});
            ans = max(ans, f);
        }
        return ans;
    }
};

416. 分割等和子集

class Solution {
public:
    bool canPartition(vector<int>& nums) {
        int s = accumulate(nums.begin(), nums.end(), 0);
        if (s % 2 == 1) {
            return false;
        }
        int n = nums.size();
        int m = s >> 1;
        bool f[n + 1][m + 1];
        memset(f, false, sizeof(f));
        f[0][0] = true;
        for (int i = 1; i <= n; ++i) {
            int x = nums[i - 1];
            for (int j = 0; j <= m; ++j) {
                f[i][j] = f[i - 1][j] || (j >= x && f[i - 1][j - x]);
            }
        }
        return f[n][m];
    }
};

62. 不同路径

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

64. 最小路径和

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

背包问题🌟

0-1 背包

func knapsack(volumes, values []int, n, cap int) int {
    dp := make([]int, cap+1)
    for i := 0; i < n; i++ {
       for j := cap; j >= volumes[i]; j-- {
          dp[j] = max(dp[j],dp[j-volumes[i]]+values[i])
       }
    }
    return dp[cap]
}

数学与数字

7. 整数反转

在 C++ 中,负数的取余运算的结果会保持与被除数的符号相同。

int reverse(int x) {  
    int ans = 0;  
    for (; x; x /= 10) {  
        if (ans < INT_MIN / 10 || ans > INT_MAX / 10) {  
            return 0;  
        }  
        ans = ans * 10 + x % 10;  
    }  
    return ans;  
}

13. 罗马数字转整数

int romanToInt(string s) {  
    unordered_map<char, int> nums{  
            {'I', 1},  
            {'V', 5},  
            {'X', 10},  
            {'L', 50},  
            {'C', 100},  
            {'D', 500},  
            {'M', 1000},  
    };  
    int ans = nums[s.back()];  
    for (int i = 0; i < s.size() - 1; ++i) {  
        int sign = nums[s[i]] < nums[s[i + 1]] ? -1 : 1;  
        ans += sign * nums[s[i]];  
    }  
    return ans;  
}

172. 阶乘后的零

int trailingZeroes(int n) {  
    int ans=0;  
    while(n){  
        n/=5;  
        ans+=n;  
    }  
    return ans;  
}

模拟

2. 两数相加

常考题

ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {  
    ListNode *dummy = new ListNode();  
    int carry = 0;  
    ListNode *cur = dummy;  
    while (l1 || l2 || carry) {  
        int s = (l1 ? l1->val : 0) + (l2 ? l2->val : 0) + carry;  
        carry = s / 10;  
        cur->next = new ListNode(s % 10);  
        cur = cur->next;  
        l1 = l1?l1->next: nullptr;  
        l2 = l2?l2->next: nullptr;  
    }  
    return dummy->next;  
}

ACM

cpp

全局变量默认初值是0。如果 int sum[N]; 在这不被定义成全局,而是主函数内定义且不赋初值,则是随机数。

string s;
getline(cin, s); // 接受一整行字符串
gets(s)
cin >> s;

cin >> n;  
getchar(); // 吸收一个回车,因为输入n之后,要输入一个回车 (如果后面有getline的话,可能会影响)

构造链表

struct ListNode {  
    int val;  
    ListNode *next;  
    ListNode() : val(0), next(nullptr) {}  
    explicit ListNode(int x) : val(x), next(nullptr) {}  
    ListNode(int x, ListNode *next) : val(x), next(next) {}  
};  

// 构造
ListNode *constructList(vector<int> vec) {  
    ListNode *dummy = new ListNode(0);  
    ListNode *cur = dummy;  
    int n = vec.size();  
    for (int i = 0; i < n; i++) {  
        ListNode *newNode = new ListNode(vec[i]);  
        cur->next = newNode;  
        cur = cur->next;  
    }  
    return dummy->next;  
}  

// 打印链表
void printListNode(ListNode *head) {  
    if (head == nullptr) {  
        cout << "list is empty" << endl;  
        return;  
    }  
    ListNode *cur = head;  
    while (cur != nullptr) {  
        cout << cur->val << " ";  
        cur = cur->next;  
    }  
    cout << endl;  
}

构造二叉树

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

方法一

// 通用的构造二叉树的方法  
TreeNode *constructBinaryTree(const vector<int> &nodes, int index, int nullValue) {  
    if (index >= nodes.size() || nodes[index] == nullValue) {  
        // 如果当前节点为空,或者已经超出节点数组的范围,返回空指针  
        return nullptr;  
    }  
  
    // 创建当前节点  
    TreeNode *current = new TreeNode(nodes[index]);  
  
    // 递归构造左子树和右子树  
    current->left = constructBinaryTree(nodes, 2 * index + 1, nullValue);  
    current->right = constructBinaryTree(nodes, 2 * index + 2, nullValue);  
  
    return current;  
}

方法二

TreeNode *buildTree(const vector<int> &nums, int nullValue) {  
    if (nums.empty()) {  
        return nullptr;  
    }  
  
    TreeNode *root = new TreeNode(nums[0]);  
    queue<TreeNode *> q;  
    q.push(root);  
  
    for (int i = 1; i < nums.size(); i += 2) {  
        TreeNode *current = q.front();  
        q.pop();  
  
        if (nums[i] != nullValue) {  
            current->left = new TreeNode(nums[i]);  
            q.push(current->left);  
        }  
  
        if (i + 1 < nums.size() && nums[i + 1] != nullValue) {  
            current->right = new TreeNode(nums[i + 1]);  
            q.push(current->right);  
        }  
    }  
  
    return root;  
}  
  
  
// 层序遍历验证  
void levelOrderTraversal(TreeNode *root) {  
    if (!root) {  
        return;  
    }  
    queue<TreeNode *> q;  
    q.push(root);  
  
    while (!q.empty()) {  
        TreeNode *current = q.front();  
        q.pop();  
        cout << current->val << " ";  
        if (current->left) {  
            q.push(current->left);  
        }  
        if (current->right) {  
            q.push(current->right);  
        }  
    }  
}  
  
int main() {  
    vector<int> nums = {3, 9, 20, -1, -1, 15, 7};  
    TreeNode *root = buildTree(nums, -1);  
  
    cout << "层序遍历结果: ";  
    levelOrderTraversal(root);  
    return 0;  
}

// 建图
vector<vector<int>> g(n + 1, vector<int>(n + 1, 0x3f3f3f3f));
for (int i = 0; i < m; ++i) {
	int a, b, w;
	cin >> a >> b >> w;
	g[a][b] = min(g[a][b], w);
	g[b][a] = min(g[b][a], w);
}

Go

Golang 主要掌握 fmt.Scan()fmt.Scanln()bufio.NewScanner(os.Stdin) 的使用

scanner := bufio.NewScanner(os.Stdin)
scanner.Scan()
scanner.Split(scanner.Text(),",")
n, _ := strconv.Atoi(scanner.Text())

奇技淫巧

for(;~j;) 等价于 for(;j>=0;) ~j = -j - 1
__builtin_popcount(x ^ y) 计算二进制表达中 111 的数量的函数 (求汉明距离)

1--n的平方和 = n(n+1)(2n+1)/6

快速幂算法: 核心思想是将幂指数 n 拆分为若干个二进制位上的 1 的和,然后将 xn 次幂转化为 x 的若干个幂的乘积。

long long fastExponentiationIterative(long long base, long long exponent) {
    long long result = 1;
    while (exponent > 0) {
        if (exponent % 2 == 1) {
            result *= base;
        }
        base *= base;
        exponent /= 2;
    }
    return result;
}
posted @ 2024-02-25 12:15  叒狗  阅读(66)  评论(0编辑  收藏  举报