【leetcode】剑指offer专项突击版详解(C++)

题目所在链接:

leetcode剑指offer专项突击版

1. 整数

剑指 Offer II 001. 整数除法
class Solution {
public:
    int divide(int a, int b) {
        // 很直观的想法是基于减法实现除法
        if(a == INT_MIN && b == -1){
            return INT_MAX;
        }
        int negative = 2;
        if(a > 0){
            negative--;
            a = -a;
        }
        if(b > 0){
            negative--;
            b = -b;
        }
        unsigned int res = dividecore(a, b);
        return negative == 1?-res:res;
    }
    unsigned int dividecore(int a, int b){
        int res = 0;
        // 注意: a,b都是负数
        while(a <= b){
            int value = b;
            unsigned int mi = 1;
            while(value >= 0xc0000000 && a <= (value + value)){
                mi += mi;
                value += value;
            }
            res += mi;
            a -= value;
        }
        return res;
    }
};
剑指 Offer II 002. 二进制加法
class Solution {
public:
    string addBinary(string a, string b) {
        int carry = 0;
        string res;
        int i = a.size() - 1;
        int j = b.size() - 1;
        while(i >= 0 || j>=0){
            int digita = i >=0? a[i] - '0':0;
            int digitb = j >= 0 ? b[j] -'0':0;
            int sum = digita + digitb + carry;
            carry = sum>=2 ? 1:0;
            sum = sum>=2 ? sum-2:sum;
            res.append(to_string(sum));
            i--;
            j--; 
        }
        if(carry == 1){
            res.append("1");
        }
        reverse(res.begin(), res.end());
        return res;
        
    }
};
剑指 Offer II 003. 前 n 个数字二进制中 1 的个数
class Solution {
public:
    vector<int> countBits(int n) {
        vector<int> res(n+1);
        for(int i=0;i<=n;i++){
            int sub_res = 0;
            for(int j=0;j<32;j++){
                if(i & (1<<j)){
                    sub_res += 1;
                }
            }
            res[i] = sub_res;
        }
        return res;
    }
};
剑指 Offer II 004. 只出现一次的数字
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        // map
        int res = 0;
        unordered_map<int, int> time_map;
        for(int i=0;i<nums.size();i++){
            if(!time_map.count(nums[i])){
                time_map.emplace(nums[i], 1);
            }else{
                int time = time_map[nums[i]];
                time_map[nums[i]] = time + 1;
            }
        }
        for(auto & v:time_map){
            if(v.second == 1){
                res = v.first;
            }
        }
        return res;

    }
};
剑指 Offer II 005. 单词长度的最大乘积
class Solution {
public:
    int maxProduct(vector<string>& words) {
        // 英文和小写字母
        // 字符串直接用26位二进制数表示,这个idea可真牛!
        int res = 0;
        for(int i=0;i<words.size()-1;i++){
            for(int j= i+1;j<words.size();j++){
                if((init_word(words[i]) & init_word(words[j])) == 0){
                    int len1 = words[i].size();
                    int len2 = words[j].size();
                    res = max(res, len1 * len2);
                }
            }
        }
        return res;
    }
    int init_word(string word){
        int num_word = 0;
        for(int i=0;i<word.size();i++){
            num_word = num_word | 1 << (word[i] - 'a');           
        }
        return num_word;
    }
};
剑指 Offer II 006. 排序数组中两个数字之和
/*
class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        // 升序排列
        // 只存在一对符合条件的数字
        // 二分查找 遍历一遍 O(nlogn)
        vector<int> res;
        for(int i=0;i<numbers.size()-1;i++){
            int new_target = target - numbers[i];
            int index = erfenchazhao(numbers, new_target, i+1, numbers.size() -1);
            if(index != -1){
                res.push_back(i);
                res.push_back(index);
                return res;
            }
        }
        return res;
    }
    int erfenchazhao(vector<int>& numbers, int new_target, int left, int right){
        while(left <= right){
            int mid = left + (right - left) / 2;
            if(numbers[mid] == new_target){
                return mid;
            }
            else if(numbers[mid] < new_target){
                left = mid + 1;
            }
            else{
                right = mid - 1;
            }
        }
        return -1;
    }
};
*/

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        // 双指针
        int left = 0;
        int right = numbers.size() - 1;
        while(left < right){
            int sum = numbers[left] + numbers[right];
            if(sum == target){
                return {left, right};
            }
            else if(sum < target){
                left++;
            }
            else{
                right--;
            }
        }
        return {};
    }
};

2. 数组

剑指 Offer II 007. 数组中和为 0 的三个数
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        if(nums.size() < 3){
            return res;
        }
        // 排序 + 双指针问题
        sort(nums.begin(), nums.end());
        for(int i=0;i<nums.size()-2;i++){
            if(i>0 && nums[i] == nums[i-1]) continue;

            int target = -nums[i];
            int left = i + 1;
            int right = nums.size() - 1;
            while(left < right){
                int sum = nums[left] + nums[right];
                if(sum == target){
                    res.push_back({nums[i], nums[left], nums[right]});
                    // 去重
                    while(left < right && nums[left] == nums[++left]);
                    while(left < right && nums[right] == nums[--right]);
                }else if(sum < target){
                    left++;
                }else{
                    right--;
                }
            }
        }
        return res;

    }
};
剑指 Offer II 008. 和大于等于 target 的最短子数组
class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        // 滑动窗口
        // 连续可是不能排序哦!!
        int res = INT_MAX;

        int left = 0;
        int right = 0;
        int sum = 0;
        while(left <= right && left < nums.size()){
            if(right < nums.size() && sum >= target){
                res = min(res, right - left);
                sum -= nums[left];
                left++;
            }
            if(right < nums.size() && sum < target){
                sum += nums[right];
                right++;
            }
            if(right >= nums.size()){
                if(sum >= target){
                    res = min(res, right - left);
                    sum -= nums[left];
                    left++;
                }else{
                    break;
                }
            }
        }
        if(res == INT_MAX){
            return 0;
        }
        return res;

    }
};
剑指 Offer II 009. 乘积小于 K 的子数组(阿果猪yyds!)

class Solution {
public:
    int numSubarrayProductLessThanK(vector<int>& nums, int k) {
        // 连续子数组 滑动窗口
        // 正整数数组
        if(nums.size() <=0){
            return 0;
        }
        int res = 0; 
        int sum = 1;
        int left = 0;
        int right = 0;
        while(left <= right && left < nums.size()){
            if(right < nums.size() && sum >= k){
                sum /= nums[left];
                left++;
            }
            if(right < nums.size() && sum < k){
                if(right - left >= 1) res = res + right - left;
                sum *= nums[right];
                right++;
            }
            if(right >= nums.size()){
                if(sum >= k){
                    sum /= nums[left];
                    left++;
                }
                if(sum < k){
                    if(right - left >= 1) res = res + right - left;
                    break;
                }
            }
        }
        return res;
    }
};

剑指 Offer II 011. 0 和 1 个数相同的子数组(前缀和+hash)
class Solution {
public:
    int findMaxLength(vector<int>& nums) {
        // 转化问题,将0看做-1,就是找到1+(-1)=0的最长连续数组
        // 要求连续 同时未说明是正整数 也不是排过序的数组,因此滑动窗口不可行
        // 选择前缀和的原因是无法判断窗口的变化引起的影响
        // 哈希表+前缀和
        unordered_map<int, int> help_map;
        int count_sum = 0;
        int length = 0;
        help_map.emplace(0, -1);
        for(int i=0;i<nums.size();i++){
            if(nums[i] == 1){
                count_sum += 1;
            }
            if(nums[i] == 0){
                count_sum = count_sum - 1;
            }
            if(help_map.count(count_sum)){
                int prelength = help_map[count_sum];
                length = max(length, i - prelength);
            }
            if(!help_map.count(count_sum)){
                help_map[count_sum] = i;
            }
        }
        return length;
    }
};
剑指 Offer II 010. 和为 k 的子数组(前缀和+hash)
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        // 整数数组 又得是连续 就代表有正、有负数,还不能排序 就不能用滑动窗口了
        // 遍历o(n2) 完成但是时间超时了
        if(nums.size() <= 0){
            return -1;
        }
        int res = 0;
        for(int i=0;i<nums.size()-1;i++){
            int sum = nums[i];
            int j = i + 1;
            while(j < nums.size()){
                sum = sum + nums[j];
                if(sum == k){
                    res++;
                }
                j++;
            }
        }
        for(int i=0;i<nums.size();i++){
            if(nums[i] == k){
                res++;
            }
        }
        return res;

    }
};
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        // 前缀和+哈希表
        // o(n) 其中: unordered_map o(1)
        // 数组的前i个数字之和记为x,如果存在一个j(j<i),数组的前j个数字之和为x-k,那么数组中从第i+1个数字开始到第j个数字结束的子数组之和为k
        // (sum, 次数)
        unordered_map<int, int> help_map;
        int sum = 0;
        int count = 0;
        help_map.emplace(0, 1);
        for(int num:nums){
            sum = sum + num;
            if(help_map.count(sum - k)){
                int time = help_map[sum - k];
                count = count + time;
            }
            if(help_map.count(sum)){
                int times = help_map[sum];
                help_map[sum] = times + 1;
            }
            else if(!help_map.count(sum)){
                help_map[sum] = 1;
            }
        }
        return count;
    }
};
剑指 Offer II 012. 左右两边子数组的和相等(前缀和)
class Solution {
public:
    int pivotIndex(vector<int>& nums) {
        // 前缀和
        int sum = 0;
        int total = 0;
        for(int i=0;i<nums.size();i++){
            total = total + nums[i];
        }
        for(int i=0;i<nums.size();i++){
            if(sum * 2 + nums[i] == total){
                return i;
            }
            sum = sum + nums[i];
        }
        return -1;
    }
};
剑指 Offer II 013. 二维子矩阵的和(一维前缀和)
class NumMatrix {
public:
// 暴力法超出时间限制
// 一维前缀和
    vector<vector<int>> sum;
    NumMatrix(vector<vector<int>>& matrix) {
        int m = matrix.size();
        if(m > 0){
            int n = matrix[0].size();
            sum.resize(m, vector<int>(n + 1));
            for(int i=0;i<m;i++){
                for(int j=0;j<n;j++){
                    sum[i][j+1] = sum[i][j] + matrix[i][j];
                }
            }
        }
    }
    
    int sumRegion(int row1, int col1, int row2, int col2) {
        int res = 0;
        for(int i=row1;i<=row2;i++){
            res += sum[i][col2+1] - sum[i][col1];
        }
        return res;
    }
};

/**
 * Your NumMatrix object will be instantiated and called as such:
 * NumMatrix* obj = new NumMatrix(matrix);
 * int param_1 = obj->sumRegion(row1,col1,row2,col2);
 */

3. 字符串

剑指 Offer II 014. 字符串中的变位词(双指针)
class Solution {
public:
    bool checkInclusion(string s1, string s2) {
        // s1 和 s2 仅包含小写字母
        // 双指针 遍历s2 o(n1*n2)
        // 位运算 用26个数字来表示字符串,对于包含重复字母的没办法体现次数,不可行
        // 用vector来储存
        vector<int> s1_vec(26);
        for(int i =0;i<s1.size();i++){
            s1_vec[ s1[i] - 'a'] += 1;
        }

        for(int i=0;i<s2.size();i++){
            string s = s2.substr(i, s1.size());
            vector<int> s_vec(26);
            for(int j=0;j<s.size();j++){
                s_vec[ s[j] - 'a' ] += 1;
            }
            if(s1_vec == s_vec){
                return true;
            }
        }
        return false;
    }


};
剑指 Offer II 015. 字符串中的所有变位词
class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        // 双指针 遍历s2 o(n2)
        vector<int> res;

        vector<int> p_vec(26);
        for(int i =0;i<p.size();i++){
            p_vec[ p[i] - 'a'] += 1;
        }

        for(int i=0;i<s.size();i++){
            string s1 = s.substr(i, p.size());
            vector<int> s1_vec(26);
            for(int j=0;j<s1.size();j++){
                s1_vec[ s1[j] - 'a' ] += 1;
            }
            if(s1_vec == p_vec){
                res.push_back(i);
            }
        }
        return res;
    }
};
剑指 Offer II 016. 不含重复字符的最长子字符串(滑动窗口)
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        // 滑动窗口
        set<char> substring;
        int left = 0;
        int right = 0;
        int res = 0;
        if(s.size() == 0){
            return 0;
        }
        while(left <= right && right < s.size()){
            if(!substring.count(s[right])){
                substring.emplace(s[right]);
                right++;
            }else{
                int len = substring.size();
                res = max(res, len);
                substring.erase(s[left]);
                left++;
            }
        }
        return max<int>(res, substring.size());
    }
};
剑指 Offer II 017. 含有所有字符的最短字符串(滑动窗口 + map)
class Solution {
public:
    bool smaller(map<char, int>& map_a, map<char, int>& map_b){
        if(map_a.size() < map_b.size()){
            return true;
        }
        else{
            int k = 0;
            for(auto sub_map:map_b){
                if(map_a.count(sub_map.first)){
                    if(map_a[sub_map.first] < map_b[sub_map.first]){
                        return true;
                    }
                }else{
                    return true;
                }
            }
        }
        return false;
    }

    string minWindow(string s, string t) {
        // 滑动窗口 + map
        // o(mn)
        map<char, int> map_ss;
        map<char, int> map_tt;
        int left = 0;
        int right = 0;
        string res = s + t;
        vector<string> res_total;
        for(char c:t){
            if(map_tt.count(c)){
                map_tt[c] += 1;
            }
            else{
                map_tt.emplace(c, 1);
            }
        }

        while(left <= right && right <= s.size()){
            if(smaller(map_ss, map_tt)){
                if(map_ss.count(s[right])){
                    map_ss[s[right]] += 1;
                }
                else{
                    map_ss.emplace(s[right], 1);
                }
                right++;
            }else{
                string res11 = s.substr(left, right - left);
                if(res11.size() < res.size()){
                    res = res11;
                }
                if(map_ss.count(s[left]) && map_ss[s[left]] > 1){
                    map_ss[s[left]] -= 1;
                }
                else{
                    map_ss.erase(s[left]);
                }
                left++;               
            }
        }


        if(res == s+t){
            return "";
        }
        return res;
    }
};
剑指 Offer II 018. 有效的回文(双指针)
class Solution {
public:
    bool isPalindrome(string s) {
        // 1.  处理字符串,只保留字母,并且全部转换为小写字母
        // 2. 双指针
        // O(n)
        string ss;
        for(int i=0;i<s.size();i++){
            if(s[i] >= 'a' && s[i] <= 'z'){
                ss.push_back(s[i]);
            }
            else if(s[i] >= 'A' && s[i] <='Z'){
                ss.push_back(s[i]+32);
            }
            else if(s[i] >= '0' && s[i] <='9'){
                ss.push_back(s[i]);
            }
            else{
                continue;
            }
        }
        int left = 0;
        int right = ss.size() - 1;
        while(left <= right){
            if(ss[left] != ss[right]){
                return false;
            }
            left++;
            right--;
        }

        return true;
    }
};
剑指 Offer II 019. 最多删除一个字符得到回文
class Solution {
public:
    bool validPalindrome(string s) {
        // 双指针 
        /*
        设定左右指针,将二者分别指向字符串的两边。
        依次比较左右指针对应的字符是否相等。
        如果相等,继续比较剩下的字符。
        如果不相等,则分两种情况,只要有一种情况是回文字符串即可:
        删除左边的 left 指针指向的元素,判断 s[left+1, right] 是否回文。
        删除右边的 right 指针指向的元素,判断 s[left, right-1] 是否回文。
        */
        int left = 0;
        int right = s.size() - 1;
        while(left < right){
            if(s[left] != s[right]){
                return isPalindrome(s, left+1, right) || isPalindrome(s, left, right-1);
            }
            left++;
            right--;
        }
        return true;



    }
    bool isPalindrome(string s, int left, int right){
        while(left < right){
            if(s[left] != s[right]){
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
};
剑指 Offer II 020. 回文子字符串的个数
class Solution {
public:
    int countSubstrings(string s) {
        // 暴力法:得到所有子串,判断每个字串是否是回文
        // 动态规划:dp[i] = dp[i-1] + {当前位置到0的每个回文判断} o(n3)
        if(s.size() == 0) return 0;
        vector<int>memo(s.size()+1);
        memo[0] = 0;
        memo[1] = 1;
        for(int i=2;i<=s.size();i++){
            int num = 0;
            for(int j=0;j<i;j++){
                if(isPalindrome(s, j, i-1)){
                    num++;
                }            
            }
            memo[i] = memo[i-1] + num;
        }
        return memo[s.size()];


    }
    bool isPalindrome(string s, int left, int right){
        while(left < right){
            if(s[left] != s[right]){
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
};

4. 链表

剑指 Offer II 021. 删除链表的倒数第 n 个结点
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        // 快慢指针 需要一个新的预置节点,为了某些情况能够保证能好好删除头结点
        ListNode *dummy = new ListNode(0, head);
        ListNode* slow = dummy;
        ListNode* fast = head;

        while(n > 0){
            fast = fast->next;
            n--;
        }
        while(fast != nullptr){
            slow = slow->next;
            fast = fast->next;
        }
        ListNode* node = slow->next->next;
        slow->next = node;

        return dummy->next;
    }
};
剑指 Offer II 022. 链表中环的入口节点(重点题*)
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        //1. 哈希表
        // 2. 快慢指针 f=2s f=s+nb
        ListNode* slow = head;
        ListNode* fast = head;
        while(true){
            if(fast == NULL || fast->next == NULL) return NULL;
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow) break;
        }
        fast = head;
        while(slow != fast){
            slow = slow->next;
            fast = fast->next;
        }
        return slow;
    }
};
剑指 Offer II 023. 两个链表的第一个重合节点
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        // 1. hash表 o(n+m) o(n)
        // 2. 快慢指针
        ListNode* pa = headA;
        ListNode* pb = headB;
        while(pa != NULL || pb != NULL){
            if(pa == pb){
                return pa;
            }
            pa = (pa == NULL)?headB:pa->next;
            pb = (pb == NULL)?headA:pb->next;
        }
        return NULL;

    }
};
剑指 Offer II 024. 反转链表(递归和迭代都得掌握呦~)
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        // 1. 迭代的手段
        // 需要一个前节点、需要保留好后节点
        /*
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while(cur != nullptr){
            ListNode* next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
        */

        // 2. 递归的手段
        if(head == nullptr || head->next == nullptr){
            return head;
        }
        ListNode* newnode = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return newnode;
    }
};
剑指 Offer II 025. 链表中的两数相加
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverse(ListNode* l){
        if(l == nullptr || l->next == nullptr){
            return l;
        }
        ListNode* newnode = reverse(l->next);
        l->next->next = l;
        l->next = nullptr;
        return newnode;
    }
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        // 翻转链表,链表转为数字,数字相加,再转回链表,还得翻转一次
        ListNode* newl1 = reverse(l1);
        ListNode* newl2 = reverse(l2);

        // 此情况下,对于长于longlong长度的无法计算
        /*
        unsigned long long num1 = 0;
        unsigned long long num2 = 0;
        int len1 = 0;
        int len2 = 0;
        while(newl1 != nullptr){
            num1 = num1 + (newl1->val) * pow(10, len1);
            newl1 = newl1->next;
            len1++;
        }

        while(newl2 != nullptr){
            num2 += (newl2->val) * pow(10, len2);
            newl2 = newl2->next;
            len2++;
        }

        unsigned long long num = 0;
        num = num1 + num2;

        unsigned long long yushu = num % 10;
        num = num / 10;
        ListNode* head = new ListNode(yushu);
        ListNode* other = head;
        while(num != 0){
            unsigned long long yushu1 = num % 10;
            num = num / 10;
            ListNode* next = new ListNode(yushu1);
            other->next = next;
            other = next; 
        }
        ListNode* newhead = reverse(head);
        return newhead;
        */

        // 考虑翻转链表之后,直接逐位相加
        int num1 = newl1->val + newl2->val;
        int carry = (newl1->val + newl2->val)>=10?(newl1->val + newl2->val)/10:0;
        num1 = carry>=1?num1%10:num1;
        ListNode* head = new ListNode(num1);
        ListNode* root = head;
        newl1 = newl1->next;
        newl2 = newl2->next;
        while(newl1 != nullptr || newl2 != nullptr){
            int num = 0;
            if(newl1 != nullptr && newl2 != nullptr){
                num = newl1->val + newl2->val;
            }
            else if(newl1 == nullptr){
                num = newl2->val;
            }else{
                num = newl1->val;
            }
            num = num + carry;
            carry = num>=10?num/10:0;
            num = carry>=1?num%10:num;
            ListNode* next = new ListNode(num);
            root->next = next;
            root = next;
            if(newl1 != nullptr && newl2 != nullptr){
                newl1 = newl1->next;
                newl2 = newl2->next;
            }
            else if(newl1 == nullptr){
                newl2 = newl2->next;
            }else{
                newl1 = newl1->next;
            }
        }
        // 判断最后会不会有一个多出来的进位
        if(carry >= 1){
            ListNode* next = new ListNode(carry);
            root->next = next;
            root = next;
        }
        ListNode* newhead = reverse(head);
        return newhead;

    }
};
剑指 Offer II 026. 重排链表
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    void reorderList(ListNode* head) {
        // 寻找中间节点-快慢指针
        // 后半段 反转链表
        // 前后归并

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

        ListNode* mid = slow;
        ListNode* newmid = reverse(mid);

        ListNode* l1_tmp;
        ListNode* l2_tmp;
        while(head!= nullptr && newmid != nullptr){
            l1_tmp = head->next;
            l2_tmp = newmid->next;

            head->next = newmid;
            head = l1_tmp;

            newmid->next = head;
            newmid = l2_tmp;
        }
        return;


    }
    ListNode* reverse(ListNode* node){
        if(node == nullptr || node->next == nullptr){
            return node;
        }
        ListNode* newnode = reverse(node->next);
        node->next->next = node;
        node->next = nullptr;
        return newnode;
    }
};
剑指 Offer II 027. 回文链表
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    bool isPalindrome(ListNode* head) {
        // 1.快慢指针找到中间点 o(n) o(1)
        // 2.迭代翻转后半部分 o(n) o(1)
        // 3. 比较节点都相似就代表回文 o(n)

        if(head->next == nullptr) return true;
        ListNode* midnode = find_mid(head);
        ListNode* newmidnode = reverse(midnode);

        while(head!= nullptr && newmidnode != nullptr){
            if(head->val != newmidnode->val){
                return false;
            }
            head = head->next;
            newmidnode = newmidnode->next;
        }
        return true;

    }

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

    ListNode* reverse(ListNode* head){
        ListNode* pre = nullptr;
        ListNode* cur = head;
        while(cur != nullptr){
            ListNode* nextnode = cur->next;
            cur->next = pre;
            pre = cur;
            cur = nextnode;
        }
        return pre;

    }
};
剑指 Offer II 028. 展平多级双向链表
/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* prev;
    Node* next;
    Node* child;
};
*/

class Solution {
public:
    Node* flatten(Node* head) {
        // dfs
        // 将 node 与 node 的下一个节点 next 断开:
        // 将 node 与 child 相连;
        // 将 child_last 与 next 相连。
        function<Node*(Node*)> dfs = [&](Node* node){
            Node* cur = node;
            // 记录链表的最后一个节点
            Node* last = nullptr;

            while(cur){
                Node* next = cur->next;
                // 如果存在子节点,那么先处理子节点
                if(cur->child){
                    Node* child_last = dfs(cur->child);

                    next = cur->next;
                    // node与child相连
                    cur->next = cur->child;
                    cur->child->prev = cur;

                    // 如果next不为空,就将last与next相连
                    if(next){
                        child_last->next = next;
                        next->prev = child_last;
                    }
                    cur->child = nullptr;
                    last = child_last;
                }
                else{
                    last = cur;
                }
                cur = next;
            }
            return last;
        };

        dfs(head);
        return head;

    }
};
剑指 Offer II 029. 排序的循环链表
/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;

    Node() {}

    Node(int _val) {
        val = _val;
        next = NULL;
    }

    Node(int _val, Node* _next) {
        val = _val;
        next = _next;
    }
};
*/

class Solution {
public:
    Node* insert(Node* head, int insertVal) {
        // 循环 单调 非递减列表
        //遍历一遍链表,找到最大值节点p,此时p.next即为最小值节点;若链表节点值均相等,则p为head的前置节点
        // 1) 若p.val > insertVal && p.next.val < insertVal, 则从p.next开始向后遍历,找到最后一个val小于insertVal的节点p,在p之后插入新节点
        // 2) 1)步结束后,可以判定p.val <= insertVal,在p之后插入新节点;若链表节点值均相等,也在p后插入
        if(head == NULL){
            head = new Node(insertVal);
            head->next = head;
            return head;
        }
        Node* p = head;
        while(p->next != head && p->next->val >= p->val){
            p = p->next;
        }

        if(p->val > insertVal && p->next->val <insertVal){
            while(p->next->val < insertVal){
                p = p->next;
            }
        }
        Node* res = p->next;
        p->next = new Node(insertVal, res);
        return head;
        
    }
};

5. 哈希表

剑指 Offer II 030. 插入、删除和随机访问都是 O(1) 的容器
class RandomizedSet {
public:
    /** Initialize your data structure here. */
    // 变长数组 + 哈希表(值,索引)
    // 哈希表可以在 O(1)的时间内完成插入和删除操作,但是由于无法根据下标定位到特定元素,因此不能在 O(1) 的时间内完成随机访问元素操作。
    vector<int> nums;
    unordered_map<int, int> indices;
    RandomizedSet() {

    }
    
    /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
    bool insert(int val) {
        if (indices.count(val)) {
            return false;
        }
        int index = nums.size();
        nums.emplace_back(val);
        indices[val] = index;
        return true;
    }
    
    /** Removes a value from the set. Returns true if the set contained the specified element. */
    bool remove(int val) {
        if(!indices.count(val)){
            return false;
        }
        int index = indices[val];
        int last = nums.back();
        nums[index] = last;
        indices[last] = index;
        nums.pop_back();
        indices.erase(val);
        return true;

    }
    
    /** Get a random element from the set. */
    int getRandom() {
        int random_index = rand() % (nums.size());
        return nums[random_index];

    }
};

/**
 * Your RandomizedSet object will be instantiated and called as such:
 * RandomizedSet* obj = new RandomizedSet();
 * bool param_1 = obj->insert(val);
 * bool param_2 = obj->remove(val);
 * int param_3 = obj->getRandom();
 */
剑指 Offer II 031. 最近最少使用缓存(hashmap+双向链表)
struct DLinkNode{
    int key, value;
    DLinkNode* prev;
    DLinkNode* next;
    DLinkNode():key(0), value(0), prev(nullptr), next(nullptr) {}
    DLinkNode(int _key, int _value): key(_key), value(_value), prev(nullptr), next(nullptr) {}
};

class LRUCache {
public:
    unordered_map<int, DLinkNode*> lru_map;
    // 添加、删除 o(1) 并且能得到新的最久未使用的数据 利用hashmap+双向链表来配合
    // 访问哈希表的时间复杂度为 O(1)O(1),在双向链表的头部添加节点、在双向链表的尾部删除节点的复杂度也为 O(1)O(1)。而将一个节点移到双向链表的头部,
    // 可以分成「删除该节点」和「在双向链表的头部添加节点」两步操作,都可以在 O(1)O(1) 时间内完成。
    DLinkNode* head;
    DLinkNode* tail;
    int capacity;
    int size;
    LRUCache(int capacity) {
        this->capacity = capacity;
        size = 0;
        head = new DLinkNode();
        tail = new DLinkNode();
        head->next = tail;
        tail->prev = head;
    }
    
    int get(int key) {
        if(!lru_map.count(key)){
            return -1;
        }
        // 如果key存在,先通过hash表定位,再移到头部
        DLinkNode* node = lru_map[key];
        moveTohead(node);
        return node->value;
    }
    
    void put(int key, int value) {
        if(!lru_map.count(key)){
            // 如果key不存在,创建一个新的节点
            DLinkNode* node = new DLinkNode(key, value);
            // 添加进hash表
            lru_map.emplace(key, node);
            // 添加到双向链表的头部
            addToHead(node);
            size++;
            if(size > capacity){
                // 超出容量,删去双向链表尾部节点
                DLinkNode* remove_node = removeTail();
                // 删除hashmap中对应的项
                lru_map.erase(remove_node->key);
                // 防止内存泄漏
                delete remove_node;
                size--;
            }
        }
        else{
            // 如果key存在,先通过hash表定位,修改value,再移到头部
            DLinkNode* node = lru_map[key];
            node->value = value;
            moveTohead(node);
        }
    }

    void removeNode(DLinkNode* node){
        node->prev->next = node->next;
        node->next->prev = node->prev;
    }

    void addToHead(DLinkNode* node){
        node->prev = head;
        node->next = head->next;
        node->next->prev = node;
        head->next = node;
    }

    void moveTohead(DLinkNode* node){
        removeNode(node);
        addToHead(node);
    }

    DLinkNode* removeTail(){
        DLinkNode* node = tail->prev;
        removeNode(node);
        return node;
    }

};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */
剑指 Offer II 032. 有效的变位词
class Solution {
public:
    bool isAnagram(string s, string t) {
        // 小写字母 数组或者hashmap都可以
        // 排除字符顺序完全相同的情况
        if(s == t || s.size() != t.size()){
            return false;
        }
        unordered_map<int, int> s_map;

        for(int i=0;i<s.size();i++){
            if(s_map.count(s[i])){
                int tmp = s_map[s[i]];
                s_map[s[i]] = tmp+1;
            }else{
                s_map.emplace(s[i], 1);
            }
        }


        for(int i=0;i<t.size();i++){
            if(s_map.count(t[i])){
                int tmp_t = s_map[t[i]];
                s_map[t[i]] = tmp_t - 1;
            }else{
                return false;
            }
        }

        for(auto &smap_pair:s_map){
            if(smap_pair.second != 0){
                return false;
            }
        }

        return true;

    }
};
剑指 Offer II 033. 变位词组
class Solution {
public:
    vector<vector<string>> res;
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        // 仅包含小写字母 暴力法o(n3)
        // hashmap {key:变位词,value:vector<string>}
        unordered_map<string, vector<string>> res_map;
        for(int i=0;i<strs.size();i++){
            string ss = strs[i];
            sort(ss.begin(), ss.end());
            res_map[ss].emplace_back(strs[i]);
        }
        for(auto &s_key:res_map){
            res.push_back(s_key.second);
        }
        return res;
    }
};
剑指 Offer II 034. 外星语言是否排序
class Solution {
public:
    bool isAlienSorted(vector<string>& words, string order) {
        // 英文小写字母
        // 1. 对order建hashmap(key:字母,value:string字典顺序值(因为26个字母会大于10,需要占2个字节))
        // 2. 得到words中长度最长的单词
        // 3. 按照这种方式将每个单词映射成一个数字形式的字符串,空位是00
        // 4. 看看是否是依照升序排列的
        unordered_map<char, string> order_map;
        for(int i=0;i<order.size();i++){
            string si;
            if(i+1<10){
                si = "0" + to_string(i+1);
            }else{
                si = to_string(i+1);
            }
            order_map.emplace(order[i], si);
        }

        int max_len = 0;
        for(int i=0;i<words.size();i++){
            max_len = max<int>(words[i].size(), max_len);
        }

        vector<string> num_words(words.size());
        for(int i=0;i<words.size();i++){
            string s;
            for(int j=0;j<words[i].size();j++){
                s = s + order_map[words[i][j]];
            }
            for(int j=words[i].size();j<max_len;j++){
                s = s + "00";
            }
            num_words.push_back(s);
        }

        for(int i=0;i<num_words.size()-1;i++){
            if(num_words[i] > num_words[i+1]){
                return false;
            }
        }
        return true;
    }
};
剑指 Offer II 035. 最小时间差
class Solution {
public:
    int getMinutes(string & s){
        return ((int)(s[0] - '0') * 10 + (int)(s[1] - '0')) * 60 + (int)(s[3] - '0') * 10 + (int)(s[4] - '0');
    }
    int findMinDifference(vector<string>& timePoints) {
        // 任意两个时间的最小时间差
        // 1. 暴力法o(n2)
        // 2. 给数组排序,最小的时间差肯定是在相邻的两个时间之间o(nlogn),要么就是首尾时间差
        sort(timePoints.begin(), timePoints.end());
        int res = INT_MAX;
        cout<<res<<endl;
        int t0Minutes = getMinutes(timePoints[0]);
        int preMinutes = t0Minutes;
        for(int i=1;i<timePoints.size();i++){
            int minutes = getMinutes(timePoints[i]);
            res = min(res, minutes - preMinutes);
            preMinutes = minutes;
        }
        
        res = min(res, t0Minutes + 1440 - preMinutes);

        return res;

        
    }
};

6. 栈

剑指 Offer II 036. 后缀表达式
class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        // 遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中
        // 栈
        stack<int> st;
        for(auto &token:tokens){
            // 记得考虑负数
            if(isdigit(token[0]) || token.size() > 1){
                st.emplace(stoi(token));
            }else{
                int y = st.top();
                st.pop();
                int x = st.top();
                st.pop();

                if(token == "+") x = x + y;
                else if(token == "-") x = x - y;
                else if(token == "*") x = x * y;
                else if(token == "/") x = x / y;
                st.emplace(x);
            }
        }
        return st.top();
        
    }
};
剑指 Offer II 037. 小行星碰撞
class Solution {
public:
    vector<int> asteroidCollision(vector<int>& asteroids) {
        // 绝对值表示小行星的大小,正负表示小行星的移动方向(正表示向右移动,负表示向左移动
        // 维护一个小行星的 栈
        stack<int> st;
        vector<int> res;
        int flag = 0;
        for(auto& asteroid:asteroids){
            flag = 0;
            if(!st.empty() && st.top()>0 && asteroid<0){
                while(!st.empty() && st.top()>0 && asteroid<0 && abs(asteroid)>=st.top()){
                    if(abs(asteroid)>st.top()){
                        st.pop();
                        flag = 1;
                    }
                    else if(abs(asteroid)==st.top()){
                        st.pop();
                        flag = 0;
                        break;
                    }
                }

                if((st.empty() && flag == 1) || (!st.empty() && st.top()<0 && flag == 1)){
                    st.emplace(asteroid);
                }
            }
            else{
                st.emplace(asteroid);
            }
        }

        while(!st.empty()){
            res.push_back(st.top());
            st.pop();
        }

        reverse(res.begin(), res.end());
        return res;
    }
};
剑指 Offer II 038. 每日温度
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        // 要想观测到更高的气温,至少需要等待的天数
        // 暴力法 o(n2)
        // 维护一个单调栈 O(n)
        stack<int> st_temperatures;
        vector<int> res(temperatures.size());
        for(int i=0;i<temperatures.size();i++){
            while(!st_temperatures.empty() && temperatures[i] > temperatures[st_temperatures.top()]){
                int pre_index = st_temperatures.top();
                res[pre_index] = i - pre_index;
                st_temperatures.pop();
            }
            st_temperatures.emplace(i);

        }
        return res;
    }
};
剑指 Offer II 039. 直方图最大矩形面积
class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        // 非负整数数组 heights
        // 暴力法 o(n2) 超出时间限制
        /*
        int min_height = INT_MAX;
        int sum = heights[0];
        for(int i=0;i<heights.size()-1;i++){
            min_height = heights[i];
            sum = max(heights[i], sum);
            for(int j=i+1;j<heights.size();j++){
                min_height = min(min_height, heights[j]);
                sum = max(sum, (j+1-i) * min_height);
            }
        }
        return max(sum, heights[heights.size() - 1]);
        */
        // 单调栈 nb 根本想不到 o(n)
        stack<int> st;
        st.push(-1);
        int maxsum = 0;
        for(int i=0;i<heights.size();i++){
            while(st.top() !=-1 && heights[st.top()] >= heights[i]){
                int height = heights[st.top()];
                st.pop();
                int width = i - st.top() - 1;
                maxsum = max(maxsum, height * width);
            }
            st.emplace(i);

        }
        while(st.top() != -1){
            int height = heights[st.top()];
            st.pop();
            int width = heights.size() - st.top() - 1;
            maxsum = max(maxsum, height * width);
        }
        return maxsum;



        
    }
};

剑指 Offer II 040. 矩阵中最大的矩形(***)
class Solution {
public:
    int maximalRectangle(vector<string>& matrix) {
        // 《剑指offer 2 面试题39》 书中算法C++实现。以题中的例子为例,分析一下使其变为上一题。
        // 每一行转化为直方图寻找最大矩阵面积
        // 因为最大矩阵一定是以矩阵的某一行为底边的,所以可以遍历各行寻找答案。
        // 单调栈 o(mn)
        if(matrix.size() == 0) return 0;
        int max_sum = 0;
        vector<int> heights(matrix[0].size(), 0);
        for(int i=0;i<matrix.size();i++){
            for(int j=0;j<matrix[0].size();j++){
                if(matrix[i][j] == '0'){
                    heights[j] = 0;
                }else{
                    int tmp = heights[j];
                    heights[j] = tmp + (matrix[i][j] - '0');
                }
            }
            max_sum = max(max_sum, largestRectangleArea(heights));
        }
        return max_sum;

    }
    int largestRectangleArea(vector<int>& heights){
        stack<int> st;
        st.push(-1);
        int max_sum = 0;
        for(int i=0;i<heights.size();i++){
            while(st.top() != -1 && heights[st.top()] >= heights[i]){
                int height = heights[st.top()];
                st.pop();
                int width = i - st.top() - 1;
                max_sum = max(max_sum, height * width);
            }
            st.push(i);
        }
        while(st.top() != -1){
            int height = heights[st.top()];
            st.pop();
            int width = heights.size() - st.top() - 1;
            max_sum = max(max_sum, height * width);
        }
        return max_sum;
    }
};

7. 队列

剑指 Offer II 041. 滑动窗口的平均值
class MovingAverage {
public:
    /** Initialize your data structure here. */
    // 利用队列
    queue<int> que_moving;
    int size;
    double sum = 0;
    MovingAverage(int size) {
        this->size = size;
    }
    
    double next(int val) {
        if(que_moving.size() < size){
            que_moving.push(val);
            sum = sum + (double)val;
            return sum / (double)que_moving.size();
        }
        int delet_num = que_moving.front();
        que_moving.pop();
        que_moving.push(val);
        sum = sum - (double)delet_num + (double)val;
        return sum / (double)size;
    }
};

/**
 * Your MovingAverage object will be instantiated and called as such:
 * MovingAverage* obj = new MovingAverage(size);
 * double param_1 = obj->next(val);
 */
剑指 Offer II 042. 最近请求次数
class RecentCounter {
public:
    queue<int> que_recent;
    int diff = 0;
    RecentCounter() {

    }
    
    int ping(int t) {
        if(que_recent.empty()){
            que_recent.push(t);
            return 1;
        }
        int first_num = que_recent.front();
        diff = t - first_num;
        while(!que_recent.empty() && diff > 3000){
            que_recent.pop();
            diff = t - que_recent.front();
        }
        que_recent.push(t);
        return que_recent.size();

    }
};

/**
 * Your RecentCounter object will be instantiated and called as such:
 * RecentCounter* obj = new RecentCounter();
 * int param_1 = obj->ping(t);
 */
剑指 Offer II 043. 往完全二叉树添加节点
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class CBTInserter {
    // 队列中的存储顺序为:首先「从左往右」存储倒数第二层最右侧的节点,再「从左往右」存储最后一层的全部节点。这一步可以使用广度优先搜索来完成,因为广度优先搜索就是按照层优先进行遍历的。
    queue<TreeNode*> que_canditate;
    TreeNode* root;
public:
    CBTInserter(TreeNode* root) {
        this->root = root;

        queue<TreeNode*> que;
        que.push(root);

        while(!que.empty()){
            TreeNode* node = que.front();
            que.pop();
            if(node->left){
                que.push(node->left);
            }
            if(node->right){
                que.push(node->right);
            }
            if(node->left == nullptr || node->right == nullptr){
                que_canditate.push(node);
            }
        }

    }
    
    int insert(int v) {
        TreeNode* children = new TreeNode(v);
        TreeNode* node = que_canditate.front();
        int res = node->val;
        if(node->left == nullptr){
            node->left = children;
        }
        else{
            node->right = children;
            que_canditate.pop();
        }
        que_canditate.push(children);
        return res;
    }
    
    TreeNode* get_root() {
        return root;
    }
};

/**
 * Your CBTInserter object will be instantiated and called as such:
 * CBTInserter* obj = new CBTInserter(root);
 * int param_1 = obj->insert(v);
 * TreeNode* param_2 = obj->get_root();
 */
剑指 Offer II 044. 二叉树每层的最大值
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> largestValues(TreeNode* root) {
        // 层序遍历
        vector<int> res;
        queue<TreeNode*> que;
        if(root == nullptr){
            return res;
        }
        que.push(root);
        while(!que.empty()){
            int max_num = INT_MIN;
            // 一定要注意,下面的循环过程中que的size会变化,所以得提前定义、确定好本层que的长度
            int length = que.size();
            for(int i=0;i<length;i++){
                TreeNode* node = que.front();
                max_num = max(max_num, node->val);
                que.pop();
                if(node->left != nullptr){
                    que.push(node->left);
                }
                if(node->right != nullptr){
                    que.push(node->right);
                }

            }
            res.push_back(max_num);
        }
        return res;

    }
};
剑指 Offer II 045. 二叉树最底层最左边的值
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        // 层序遍历 记录每一层 第一个值
        queue<TreeNode*> que;
        que.push(root);
        int res = 0;
        while(!que.empty()){
            int len = que.size();
            for(int i=0;i<len;i++){
                TreeNode* node = que.front();
                if(i == 0) res = node->val;
                que.pop();
                if(node -> left != nullptr){
                    que.push(node->left);
                }
                if(node -> right != nullptr){
                    que.push(node->right);
                }

            }

        }
        return res;
    }
};
剑指 Offer II 046. 二叉树的右侧视图
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> rightSideView(TreeNode* root) {
        // 层序遍历 保留每一层最右侧的点
        vector<int> res;
        if(root == nullptr){
            return res;
        }
        queue<TreeNode*> que;
        que.push(root);
        while(!que.empty()){
            int len = que.size();
            for(int i=0;i<len;i++){
                TreeNode* node = que.front();
                if(i == len-1) res.push_back(node->val);
                que.pop();
                if(node->left != nullptr){
                    que.push(node->left);
                }
                if(node->right != nullptr){
                    que.push(node->right);
                }
            }
        }
        return res;
    }
};

8. 树

剑指 Offer II 047. 二叉树剪枝
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* pruneTree(TreeNode* root) {
        // 递归 后序遍历
        if(root == nullptr){
            return root;
        }
        root->left = pruneTree(root->left);
        root->right = pruneTree(root->right);
        if(root->val == 0 && root->left == nullptr && root->right == nullptr){
            root = nullptr;
        }
        return root;
    }
};
剑指 Offer II 048. 序列化与反序列化二叉树
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Codec {
public:

    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {
        // BFS层序遍历 空值插入NULL
        if(root == NULL){
            return "";
        }
        string res;
        queue<TreeNode*> que;
        que.emplace(root);
        while(!que.empty()){
            TreeNode* node = que.front();
            que.pop();
            if(node == NULL) res += "None,";
            else{
                res += to_string(node->val) + ",";
                que.emplace(node->left);
                que.emplace(node->right);
            }
        }
        return res;
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string data) {
        if(data =="") return NULL;
        // 字符串重组为链表形式
        list<string> data_array;
        string str;
        for(char& c:data){
            if(c == ','){
                data_array.emplace_back(str);
                str.clear();
            }else{
                str.push_back(c);
            }
        }
        // 二叉树反序列化
        queue<TreeNode*> que;
        TreeNode* root = new TreeNode(stoi(data_array.front()));
        data_array.pop_front();
        que.emplace(root);
        while(!que.empty() && !data_array.empty()){
            TreeNode* node = que.front();
            que.pop();
            // 左节点
            if(data_array.front() != "None"){
                TreeNode* node1 = new TreeNode(stoi(data_array.front()));
                node->left = node1;
                que.emplace(node1);
            }
            data_array.pop_front();
            // 右节点
            if(data_array.front() != "None"){
                TreeNode* node2 = new TreeNode(stoi(data_array.front()));
                node->right = node2;
                que.emplace(node2);
            }
            data_array.pop_front();
        }
        return root;
    }
};

// Your Codec object will be instantiated and called as such:
// Codec ser, deser;
// TreeNode* ans = deser.deserialize(ser.serialize(root));
剑指 Offer II 049. 从根节点到叶节点的路径数字之和
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<string> total_res;
    int sumNumbers(TreeNode* root) {
        // dfs所有值 加起来
        int res = 0;
        if(root == nullptr){
            return res;
        }
        // 都是小于9的所以可以用string
        string pre;
        dfs(root, pre);
        for(string s:total_res){
            res = res + stoi(s);
        }
        return res;

        
    }
    void dfs(TreeNode* node, string pre){
        if(node == nullptr){
            return;
        }

        pre.append(to_string(node->val));
        if(node->left == nullptr && node->right == nullptr){
            total_res.push_back(pre);
        }
        dfs(node->left, pre);
        dfs(node->right, pre);
        pre.pop_back();
    }
};
剑指 Offer II 050. 向下的路径节点之和
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int res = 0;
    int pathSum(TreeNode* root, int targetSum) {
        // dfs 注意不是从根节点开始
        // 前序遍历任意一个节点均考虑
        // 思路:对树中的每一个节点进行的dfs,观察如果到有target==0的情况,就记录
        if(root == nullptr){
            return res;
        }
        search(root, targetSum);
        return res;

    }

    void search(TreeNode* node, int target){
        if(node == nullptr) return;
        vector<int> pre;
        dfs(node, target, pre);
        search(node->left, target);
        search(node->right,target);
    }

    void dfs(TreeNode* node, int target, vector<int>& pre){
        if(node == nullptr){
            return;
        }
        pre.push_back(node->val);
        target = target - node->val;
        if(target == 0){
            res++;
        }
        dfs(node->left, target, pre);
        dfs(node->right, target, pre);
        pre.pop_back();
    }
};
剑指 Offer II 051. 节点之和最大的路径(*)
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int res; 
    int maxPathSum(TreeNode* root) {
        // 一个子树内部的最大路径和 = 左子树提供的最大路径和 + 根节点值 + 右子树提供的最大路径和
        // 后序遍历
        res = INT_MIN;
        dfs(root);
        return res;
    }
    int dfs(TreeNode* root){
        if(root == nullptr){
            return 0;
        }
        int left = max(0, dfs(root->left));
        int right = max(0, dfs(root->right));
        res = max(res, left + right + root->val);

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

    }
};
剑指 Offer II 052. 展平二叉搜索树
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> res;
    TreeNode* increasingBST(TreeNode* root) {
        // 中序遍历 左根右 之后生成新的树
        inorder(root);

        TreeNode* dummynode = new TreeNode(-1);
        TreeNode* currnode = dummynode;
        for(int value:res){
            currnode->right = new TreeNode(value);
            currnode = currnode->right;
        }
        return dummynode->right;

    }
    void inorder(TreeNode* root){
        if(root == nullptr){
            return;
        }
        inorder(root->left);
        res.push_back(root->val);
        inorder(root->right);
    }
};
剑指 Offer II 053. 二叉搜索树中的中序后继
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<TreeNode*> help_vec;
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        // 二叉搜索树
        // 中序遍历是递增的
        // 树中各节点的值均保证唯一
        // 中序遍历+循环找到
        
        inorder(root, p);
        for(int i=0;i<help_vec.size();i++){
            if(help_vec[i]->val == p->val && i+1<help_vec.size()){
                return help_vec[i+1];
            }
        }
        return NULL;
    }
    void inorder(TreeNode* root, TreeNode* p){
        if(root == NULL) return;
        inorder(root->left, p);
        help_vec.push_back(root);
        inorder(root->right, p);
    }
};
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        // 二叉搜索树
        // 中序遍历是递增的
        // 树中各节点的值均保证唯一
        // 1. 中序遍历+循环找到
        // 2. while 利用搜索树特性
        TreeNode* res = NULL;
        while(root != NULL){
            if(root->val > p->val){
                res = root;
                root = root->left;
            }
            else if(root->val <= p->val){
                root = root->right;
            }
        }
        return res;
    }

};
剑指 Offer II 054. 所有大于等于节点的值之和
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> res_inorder;
    TreeNode* convertBST(TreeNode* root) {
        // 1. 中序遍历 获得结果
        // 2. 再将结果值赋值回树里面
        if(root == nullptr) return nullptr;
        inorder(root);

        int sum = 0;
        for(int i=0;i<res_inorder.size();i++){
            sum += res_inorder[i];
        }

        vector<int> final_order(res_inorder.size());
        final_order[0] = sum;
        for(int i=1; i<res_inorder.size();i++){
            final_order[i] = sum - res_inorder[i-1];
            sum = final_order[i];
        }
        TreeNode* cur = root;
        give_val(cur, final_order);
        return root;

    }
    void inorder(TreeNode* root){
        if(root == nullptr) return;
        inorder(root->left);
        res_inorder.push_back(root->val);
        inorder(root->right);
    }
    int k = 0;
    void give_val(TreeNode* node, vector<int>& final_order){
        if(node == nullptr) return;
        give_val(node->left, final_order);
        node->val = final_order[k];
        k++;
        give_val(node->right, final_order);
    }

};

方法二:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
/*
    vector<int> res_inorder;
    TreeNode* convertBST(TreeNode* root) {
        // 1. 中序遍历 获得结果
        // 2. 再将结果值赋值回树里面
        if(root == nullptr) return nullptr;
        inorder(root);

        int sum = 0;
        for(int i=0;i<res_inorder.size();i++){
            sum += res_inorder[i];
        }

        vector<int> final_order(res_inorder.size());
        final_order[0] = sum;
        for(int i=1; i<res_inorder.size();i++){
            final_order[i] = sum - res_inorder[i-1];
            sum = final_order[i];
        }
        TreeNode* cur = root;
        give_val(cur, final_order);
        return root;

    }
    void inorder(TreeNode* root){
        if(root == nullptr) return;
        inorder(root->left);
        res_inorder.push_back(root->val);
        inorder(root->right);
    }
    int k = 0;
    void give_val(TreeNode* node, vector<int>& final_order){
        if(node == nullptr) return;
        give_val(node->left, final_order);
        node->val = final_order[k];
        k++;
        give_val(node->right, final_order);
    }
    */
    int add = 0;
    TreeNode* convertBST(TreeNode* root) {
    // 方法二:反序进行中序遍历
    if(root == nullptr) return nullptr;
    convertBST(root->right);
    root->val += add;
    add = root->val;
    convertBST(root->left);
    return root;
    }
};
剑指 Offer II 056. 二叉搜索树中两个节点之和
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> res_help;
    bool findTarget(TreeNode* root, int k) {
        // 二叉搜索树
        // 两个节点
        // 每个节点值唯一
        // 中序遍历后 递增序列 双指针
        if(root == nullptr){
            return false;
        }
        inorder(root);
        int left = 0;
        int right = res_help.size() - 1;
        while(left < right){
            if(res_help[left] + res_help[right] == k){
                return true;
            }
            else if(res_help[left] + res_help[right] < k){
                left++;
            }else{
                right--;
            }
        }
        return false;
    }
    void inorder(TreeNode* root){
        if(root == nullptr) return;
        inorder(root->left);
        res_help.push_back(root->val);
        inorder(root->right);
    }
};
剑指 Offer II 055. 二叉搜索树迭代器
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class BSTIterator {
public:
// 中序遍历
    vector<int> res;
    void inorder(TreeNode* root){
        if(root ==nullptr) return;
        inorder(root -> left);
        res.push_back(root->val);
        inorder(root->right);
    }

    vector<int> arr;
    int index;
    BSTIterator(TreeNode* root) {
        inorder(root);
        index = 0;
        arr = res;
    }
    
    int next() {
        return arr[index++];
    }
    
    bool hasNext() {
        return (index < arr.size());
    }
};

/**
 * Your BSTIterator object will be instantiated and called as such:
 * BSTIterator* obj = new BSTIterator(root);
 * int param_1 = obj->next();
 * bool param_2 = obj->hasNext();
 */

二叉树中序遍历:迭代法

// 初始化
stack<TreeNode*> sta;
TreeNode* cur = root;
while (cur != nullptr || !sta.empty()) {  // hasNext 操作
//  next 操作
    while (cur != nullptr) {
        sta.push(cur);
        cur = cur->left;
    }
    cur = sta.top();  // 当前遍历到的节点 cur 
    sta.pop();

    cur = cur->right;
}
剑指 Offer II 056. 二叉搜索树中两个节点之和
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> res_help;
    bool findTarget(TreeNode* root, int k) {
        // 二叉搜索树
        // 两个节点
        // 每个节点值唯一
        // 中序遍历后 递增序列 双指针
        if(root == nullptr){
            return false;
        }
        inorder(root);
        int left = 0;
        int right = res_help.size() - 1;
        while(left < right){
            if(res_help[left] + res_help[right] == k){
                return true;
            }
            else if(res_help[left] + res_help[right] < k){
                left++;
            }else{
                right--;
            }
        }
        return false;
    }
    void inorder(TreeNode* root){
        if(root == nullptr) return;
        inorder(root->left);
        res_help.push_back(root->val);
        inorder(root->right);
    }
};
剑指 Offer II 057. 值和下标之差都在给定的范围内(难!和树唯一的关系大概是因为用了set,set的底层实现是红黑树,所以插入和删除的时间复杂度是logn)
class Solution {
public:
    bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
        // 滑动窗口 + 有序集合
        // 对于元素 x,当我们希望判断滑动窗口中是否存在某个数 y 落在区间[x−t,x+t] 中,只需要判断滑动窗口中所有大于等于 x - t的元素中的最小元素是否小于等于x+t 即可
        int n = nums.size();
        set<int> rec;
        for(int i=0;i<n;i++){
            auto iter = rec.lower_bound(max(nums[i], INT_MIN+t) - t);
            if(iter != rec.end() && *iter <= min(nums[i], INT_MAX - t) + t){
                return true;
            }
            rec.emplace(nums[i]);
            if(i >= k){
                rec.erase(nums[i - k]);
            }
        }
        return false;
    }
};
剑指 Offer II 058. 日程表(利用map底层是一个红黑树)
class MyCalendar {
public:
    map<int, int> hash;
    MyCalendar() {
        // C++中的map,底层是红黑树,即平衡的二叉搜索树
        // 所以直接用map就可以
        // iterator lower_bound( const key_type &key ): 返回一个迭代器,指向键值>= key的第一个元素
        // iterator upper_bound( const key_type &key ):返回一个迭代器,指向键值> key的第一个元素

    }
    
    bool book(int start, int end) {
        map<int, int>::iterator event = hash.lower_bound(start);

        if(event != hash.end() && event->first < end) return false;

        if(event != hash.begin() && (--event)->second > start) return false;
        
        hash[start] = end;
        return true;
    }
};

/**
 * Your MyCalendar object will be instantiated and called as such:
 * MyCalendar* obj = new MyCalendar();
 * bool param_1 = obj->book(start,end);
 */

9. 堆

剑指 Offer II 059. 数据流的第 K 大数值
class KthLargest {
public:
    priority_queue<int, vector<int>, greater<int>> Q;
    int k;
    KthLargest(int k, vector<int>& nums) {
        // 排序后的第k大元素,不是第k个不同元素,是第k大,从大往小排序的
        // 维持一个k个数的小根堆,用优先队列
        // 题目数据保证,在查找第 k 大元素时,数组中至少有 k 个元素
        this->k = k;
        if(nums.size() == 0) return;
        if(nums.size() >= k){
            for(int i=0;i<k;i++){
                Q.push(nums[i]);
            }
        }else{
            for(int i=0;i<nums.size();i++){
                Q.push(nums[i]);
            }
        }

        for(int i=k;i<nums.size();i++){
            if(nums[i] > Q.top()){
                Q.pop();
                Q.push(nums[i]);
            }
        }
        
    }
    
    int add(int val) {
        if(!Q.empty() && Q.size() == k){
            if(val <= Q.top()){
                return Q.top();
            }else{
                Q.pop();
                Q.push(val);
                return Q.top();
            }
        }else{
            Q.push(val);
            return Q.top();
        }

    }
};

/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest* obj = new KthLargest(k, nums);
 * int param_1 = obj->add(val);
 */
剑指 Offer II 060. 出现频率最高的 k 个数字
class Solution {
public:
    static bool cmp(pair<int, int>& m, pair<int, int>& n){
        return m.second > n.second;
    }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        // nb! map + 小顶堆
        // O(Nlogk)
        unordered_map<int, int> occur_times;
        for(auto& v:nums){
            occur_times[v]++;
        }

        // pair 的第一个元素代表数组的值,第二个元素代表该值出现的次数
        priority_queue<pair<int,int>, vector<pair<int, int>>, decltype(&cmp)> q_heap(cmp);
        for(auto& [num, count] : occur_times){
            if(q_heap.size() == k){
                if(q_heap.top().second < count){
                    q_heap.pop();
                    q_heap.emplace(num, count);
                }
            }else{
                q_heap.emplace(num, count);
            }
        }

        vector<int> res;
        while(!q_heap.empty()){
            res.push_back(q_heap.top().first);
            q_heap.pop();
        }
        return res;

    }
};
class Solution {
public:
    typedef pair<int, int> pair;
    vector<int> topKFrequent(vector<int>& nums, int k) {
        // map + map利用value排序
        map<int, int> freq_map;
        for(auto& num:nums){
            freq_map[num]++; 
        }

        vector<pair> vec;
        vector<int> res;
        copy(freq_map.begin(), freq_map.end(), back_inserter<vector<pair>>(vec));

        sort(vec.begin(), vec.end(),
        [](const pair &l, const pair &r){
            return l.second > r.second;
        });

        for(int i=0;i<k;i++){
            res.push_back(vec[i].first);
        }
        return res;
    }
};
剑指 Offer II 061. 和最小的 k 个数对
class Solution {
public:
    static bool cmp(pair<int, int>& m, pair<int,int>& n){
    // 这里这个参数其实很傲娇,表示的意思是如果!cmp,则先出列,不管这样实现的目的是啥,大家只能接受这个实现
        return (m.first + m.second) < (n.first + n.second);
    }
    vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        // 维持k的大根堆 O(k^2logk)
        // 时间复杂度:O(k^2logk) ,其中容量为k的堆的添加与删除是O(logK),循环k^2次,为O(k^2logk)
        // 忽略升序排序数组
        priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(&cmp)> q_heap(cmp);
        for(int i=0;i<nums1.size() && i<k;i++){
            for(int j=0;j<nums2.size() && j<k;j++){
                if(q_heap.size() == k){
                    if(nums2[j] + nums1[i] < q_heap.top().first + q_heap.top().second){
                        q_heap.pop();
                        q_heap.emplace(nums1[i], nums2[j]);
                    }
                }else{
                    q_heap.emplace(nums1[i], nums2[j]);
                }
            }
        }

        vector<vector<int>> res;
        while(!q_heap.empty()){
            res.push_back({q_heap.top().first, q_heap.top().second});
            q_heap.pop();
        }
        return res;

    }
};
class Solution {
public:
    static bool cmp(pair<int, int>& m, pair<int,int>& n){
        return (m.first + m.second) < (n.first + n.second);
    }
    vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {
        // 维持k的数组 O(k^2 + klogk)
        // 然后排序
        vector<pair<int,int>> vec;
        for(int i=0;i<nums1.size() && i<k;i++){
            for(int j=0;j<nums2.size() && j<k;j++){
                pair<int, int> tmp = {nums1[i], nums2[j]};
                vec.push_back(tmp);
            }
        }

        sort(vec.begin(), vec.end(),
        [](const pair<int, int> &l, const pair<int, int> &r){
            return (l.first+l.second) < (r.first + r.second);
        }
        );

        vector<vector<int>> res;
        if(vec.size() <= k){
            for(int i=0;i<vec.size();i++){
            res.push_back({vec[i].first, vec[i].second});
            }
        }else{
            for(int i=0;i<k;i++){
            res.push_back({vec[i].first, vec[i].second});
            }
        }
        return res;

    }
};

10. 前缀树(*和前缀和不一样呦)

剑指 Offer II 062. 实现前缀树

在这里插入图片描述

class Trie {
private:
    vector<Trie*> children;
    bool isEnd;

    Trie* searchPrefix(string prefix) {
        Trie* node = this;
        for (char ch : prefix) {
            ch -= 'a';
            if (node->children[ch] == nullptr) {
                return nullptr;
            }
            node = node->children[ch];
        }
        return node;
    }

public:
    Trie() : children(26), isEnd(false) {}

    void insert(string word) {
        Trie* node = this;
        for (char ch : word) {
            ch -= 'a';
            if (node->children[ch] == nullptr) {
                node->children[ch] = new Trie();
            }
            node = node->children[ch];
        }
        node->isEnd = true;
    }

    bool search(string word) {
        Trie* node = this->searchPrefix(word);
        return node != nullptr && node->isEnd;
    }

    bool startsWith(string prefix) {
        return this->searchPrefix(prefix) != nullptr;
    }
};


/**
 * Your Trie object will be instantiated and called as such:
 * Trie* obj = new Trie();
 * obj->insert(word);
 * bool param_2 = obj->search(word);
 * bool param_3 = obj->startsWith(prefix);
 */
剑指 Offer II 063. 替换单词
class Trie {
private:
    vector<Trie*> children;
    bool isEnd;

public:
    /** Initialize your data structure here. */
    Trie():children(26), isEnd(false) {

    }
    
    /** Inserts a word into the trie. */
    void insert(string word) {
        Trie* node = this;
        for(char ch:word){
            ch -= 'a';
            if(node->children[ch] == nullptr){
                node->children[ch] = new Trie();
            }
            node = node->children[ch];
        }
        node->isEnd = true;
    }
    
    // 计算字符串的前缀长度
    int countPreLen(string &str){
        Trie* node = this;
        int len = 0;
        for(auto& ch:str){
            if(node->children[ch- 'a'] == nullptr){
                return 0;
            }
            node = node->children[ch - 'a'];
            len++;
            if(node->isEnd){
                return len;
            }
        }
        return 0;
    }
};

class Solution {
public:
    string replaceWords(vector<string>& dictionary, string sentence) {
        Trie* root = new Trie();
        for(string& word:dictionary){
            root->insert(word);
        }

        // 分割
        vector<string> words;
        sentence +=" ";
        string ss = "";
        for(auto& ch:sentence){
            if(ch != ' '){
                ss.push_back(ch);
            }else{
                words.push_back(ss);
                ss = "";
            }
        }

        // 处理
        vector<string> tmp;
        for(auto& word:words){
            int len = root->countPreLen(word);
            if(len == 0){
                tmp.push_back(word);
            }
            else{
                tmp.push_back(word.substr(0, len));
            }
        }

        // 拼接
        string res;
        for(auto& word:tmp){
            res = res + word;
            res = res + " ";
        }

        res.pop_back();
        return res;
    }
};
剑指 Offer II 064. 神奇的字典
class MagicDictionary {
private:
    vector<string> words;
public:
    /** Initialize your data structure here. */
    MagicDictionary() {

    }
    
    void buildDict(vector<string> dictionary) {
        words = dictionary;
    }
    
    bool search(string searchWord) {
        for(auto& word:words){
            if(word.size() != searchWord.size()){
                continue;
            }

            int diff = 0;
            for(int i=0;i<word.size();i++){
                if(word[i] != searchWord[i]){
                    diff++;
                }
                if(diff > 1){
                    break;
                }
            }
            if(diff == 1){
                return true;
            }
        }
        return false;
    }
};

/**
 * Your MagicDictionary object will be instantiated and called as such:
 * MagicDictionary* obj = new MagicDictionary();
 * obj->buildDict(dictionary);
 * bool param_2 = obj->search(searchWord);
 */
剑指 Offer II 065. 最短的单词编码
class Trie{
public:
    vector<Trie*> children;
    Trie():children(26,nullptr){}
};

class Solution {
private:
    // 结果
    int total;

    // 构造前缀树
    Trie* buildTrie(const vector<string>& words){
        Trie* root = new Trie();
        for (auto& str : words) {
            Trie* node = root;
            for (int i = str.size() - 1; i >= 0; --i) {
                char ch = str[i];
                if (node->children[ch - 'a'] == nullptr) {
                    node->children[ch - 'a'] = new Trie();
                }
                node = node->children[ch - 'a'];
            }
        }
        return root;
    }

    // dfs 查找
    void dfs(Trie* root, int len) {
        bool isLeaf = true;
        for (auto& child : root->children) {
            if (child != nullptr) {
                isLeaf = false;
                dfs(child, len + 1);
            }
        }
        if (isLeaf) {
            total += len;
        }
    }


public:
    int minimumLengthEncoding(vector<string>& words) {
        // 目标就是保留所有不是其他单词后缀的单词,最后的结果就是这些单词长度加一的总和,因为每个单词编码后后面还需要跟一个 # 符号
        // 构造前缀树
        Trie* root = buildTrie(words);

        total = 0;
        dfs(root, 1);
        return total;
    }
};
剑指 Offer II 066. 单词之和
// 暴力法
class MapSum {
public:
    MapSum() {

    }
    
    void insert(string key, int val) {
        cnt[key] = val;
    }
    
    int sum(string prefix) {
        int res = 0;
        for (auto & [key,val] : cnt) {
            if (key.substr(0, prefix.size()) == prefix) {
                res += val;
            }
        }
        return res;
    }
private:
    unordered_map<string, int> cnt;
};
// 构造前缀树节点
class Trie {
public:
    int val;
    vector<Trie*> children;
    Trie () : val(0), children(26, nullptr) {}

    // 实现插入字符串
    void insert(string& str, int m) {
        Trie* node = this;
        for (auto& ch : str) {
            if (node->children[ch - 'a'] == nullptr) {
                node->children[ch - 'a'] = new Trie();
            }
            node = node->children[ch - 'a'];
        }
        node->val = m;
    }

    // 实现返回所有以该前缀 prefix 开头的键 key 的值的总和
    int coutSum(string &prefix) {
        Trie* node = this;
        for (auto& ch : prefix) {
            if (node->children[ch - 'a'] == nullptr) {
                return 0;
            }
            node = node->children[ch - 'a'];
        }

        // BFS
        int count = 0;
        queue<Trie*> que;
        que.push(node);
        while (!que.empty()) {
            Trie* node = que.front();
            que.pop();
            count += node->val;
            for (int i = 0; i < node->children.size(); ++i) {
                if (node->children[i] != nullptr) {
                    que.push(node->children[i]);
                }
            }
        }
        return count;
    }
};

class MapSum {
private:
    Trie* root;
public:
    MapSum() {
        root = new Trie();
    }
    
    void insert(string key, int val) {
        root->insert(key, val);
    }
    
    int sum(string prefix) {
        return root->coutSum(prefix);
    }
};


/**
 * Your MapSum object will be instantiated and called as such:
 * MapSum* obj = new MapSum();
 * obj->insert(key,val);
 * int param_2 = obj->sum(prefix);
 */
剑指 Offer II 067. 最大的异或
/*
class Solution {
public:
    int findMaximumXOR(vector<int>& nums) {
        // 暴力法 o(n2)--> 超时
        
        int res = INT_MIN;

        for(int i=0;i<nums.size();i++){
            for(int j=i;j<nums.size();j++){
                int tmp = nums[i] ^ nums[j];
                res = max(tmp, res);
            }
        }
        return res;
        

        // 前缀树
        
    }
};
*/

// 构造前缀树节点
class Trie {
public:
    vector<Trie*> children;
    Trie () : children(2, nullptr) {}

    // 插入
    void insert(int n) {
        Trie* node = this;
        for (int i = 31; i >= 0; --i) {
            int bit = (n >> i) & 1;
            if (node->children[bit] == nullptr) {
                node->children[bit] = new Trie();
            }
            node = node->children[bit];
        }
    }

    // 返回最大XOR结果
    int maxXOR(int n) {
        Trie* node = this;
        int ret = 0;
        for (int i = 31; i >= 0; --i) {
            int bit = (n >> i) & 1;
            if (node->children[!bit] != nullptr) {
                ret |= (1 << i);
                node = node->children[!bit];
            }
            else {
                node = node->children[bit];
            }
        }
        return ret;
    }
};

class Solution {
public:
    int findMaximumXOR(vector<int>& nums) {
        Trie* root = new Trie();
        for (auto& num : nums) {
            root->insert(num);
        }
        int maxVal = INT_MIN;
        for (auto& num : nums) {
            int cur = root->maxXOR(num);
            maxVal = max(cur, maxVal);
        }
        return maxVal;
    }
};

11. 二分查找

剑指 Offer II 068. 查找插入位置
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        // 排序 整数数组 无重复元素
        // 二分查找

        int left = 0;
        int right = nums.size() - 1;
        while(left <= right){
            int mid = left + (right - left)/2;
            if(nums[mid] == target){
                return mid;
            }
            if(left == right){
                if(nums[left] < target){
                    return left + 1;
                }
                if(nums[left] > target){
                    return left;
                }
            }
            if(nums[mid] < target){
                left = mid + 1;
            }
            if(nums[mid] > target){
                right = mid - 1;
            }
        }
        return left;

    }
};
剑指 Offer II 069. 山峰数组的顶部
class Solution {
public:
    int peakIndexInMountainArray(vector<int>& arr) {
        // 找山峰数组 顶部
        if(arr.size() < 3) return -1;

        int left = 0;
        int right = arr.size() - 1;
        while(left <= right){
            int mid = left + (right - left)/2;
            if(arr[mid] > arr[mid-1] && arr[mid] > arr[mid+1]){
                return mid;
            }
            if(arr[mid] > arr[mid-1] && arr[mid] < arr[mid+1]){
                left = mid;
            }
            if(arr[mid] < arr[mid-1] && arr[mid] > arr[mid+1]){
                right = mid;
            }
        }
        return left;

    }
};
剑指 Offer II 070. 排序数组中只出现一次的数字
/*
class Solution {
public:
    int singleNonDuplicate(vector<int>& nums) {
        // 整数数组 遍历 异或 o(n)
        if(nums.size() == 0) return -1;
        int res = nums[0];
        for(int i=1;i<nums.size();i++){
            res = res ^ nums[i];
        }
        return res;
        // o(logn) 二分查找
        
    }
};
*/
// 二分查找
class Solution {
public:
    int singleNonDuplicate(vector<int>& nums) {
        int left = 0;
        int right = nums.size() / 2 - 1;
        while (left <= right) {
            int mid = left + ((right - left) >> 1);
            int i = 2 * mid;
            if (nums[i] != nums[i + 1]) {
                if (i == 0 || nums[i - 1] == nums[i - 2]) {
                    return nums[i];
                }
                right = mid - 1;
            }
            else {
                left = mid + 1;
            }
        }
        return nums.back();
    }
};
剑指 Offer II 073. 狒狒吃香蕉
class Solution {
private:
// 速度k吃完香蕉需要的时间
    int countTime(vector<int>& piles, int k){
        int t = 0;
        for(auto& p:piles){
            t += p/k;
            t += ((p%k) > 0);
        }
        return t;
    }

public:
    int minEatingSpeed(vector<int>& piles, int h) {
        /*
        二分查找
        虽然目前不清楚狒狒吃香蕉的速度,但是可以得知狒狒的速度肯定需要大于等于 1,同时也要小于等于最大的一堆香蕉数量 max,因为若大于 max 每小时也只能吃一堆,所以更大的速度是没有意义的。这时候可以得到狒狒吃香蕉的速度应该在 [1, max],可以使用二分查找算法确定速度。取 1 和 max 的中间值 mid,计算出速度为 mid 时吃完香蕉所需时间 t。

        如果时间 t 大于时间 H,则说明狒狒吃的太慢了,需要在 [mid + 1, max] 中搜索速度。
        如果时间 t 小于等于时间 H,则还需要判断 mid - 1 的速度吃完香蕉的时间 t2。如果 t2 大于 H,那么说明 mid 为最慢吃完香蕉所需时间(同时需要说明 若 mid 为 1,则无需判断 mid - 1 的情况,直接确定 1 为最慢吃完香蕉所需时间)。如果 t2 小于 H,则说明当前的速度太快,需要在 [1, mid - 1] 中搜索速度。
整个过程其实就是在 1 根到 max 根之间做二分查找。如果香蕉的堆数为 m,最大的堆数的香蕉数量为 n,那么算法的时间复杂度为 O(mlogn)。

        说明一下 countTime 函数计算狒狒当前速度吃香蕉的时间,因为题目中说 “如果这堆香蕉少于 K 根,她将吃掉这堆的所有香蕉,然后这一小时内不会再吃更多的香蕉,下一个小时才会开始吃另一堆的香蕉” ,那么狒狒无论选择何种策略吃香蕉,其吃完所有香蕉的时间都与吃香蕉堆的选择顺序无关。
        */

        int left = 1;
        int right = *max_element(piles.begin(), piles.end());

        while(left <= right){
            int mid = left + (right - left)/2;
            if(countTime(piles, mid) <= h){
                if(mid == 1 || countTime(piles, mid-1)>h){
                    return mid;
                }
                right = mid - 1;
            }
            else{
                left = mid + 1;
            }
        }

        return -1;


    }
};
剑指 Offer II 072. 求平方根
class Solution {
public:
    int mySqrt(int x) {
        // 除了0以外,所有数的平方根都在1到它本身之间
        int left = 1;
        int right = x;
        while(left <= right){
            int mid = left + (right - left) / 2;
            if(mid <= x / mid){
                if((mid+1)  > x/(mid+1)) return mid;
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
        return 0;

    }
};
剑指 Offer II 071. 按权重生成随机数(*)
class Solution {
public:

/*
前缀和+二分查找 一开始都完全读不懂题意
以权重数组 [1, 2, 3, 4] 为例,那么选择下标 0 的概率为 10% (1/10),选择下标 1 、2 和 3 的概率一次为 20% 、30% 和40%。考虑如何根据权重比例随机选择一个下标,先按等概率生成 1 ~ 10,则每个数字的概率的都为 10%。如果生成 1 则选择下标 0,概率为 10%;如果生成 2 或 3 则选择下标 1,概率为 20%;如果生成 4、5 或 6 则选择下标 2,概率为 30%;如果生成 7、8、9 或 10 则选择下标 3,概率为 40%。

通过上面的例子可以发现,可以创建一个和权重数组一样长度的数组 acc,新数组的第 i 个数值 acc[i] 就是权重数组的前 i 个数组之和。有了这个数组就可以很方便的根据随机等概生成的数字选则对应的下标。方式如下:

等概率在区间 [1, acc.back()] 上随机生成数字 n;
找到区间 n <= acc[m] && (m == 0 || n > acc[m - 1]),则下标 m 就是输出的下标值。
因为数组 acc 是一个递增数组,所以可以使用二分查找找到目标区间。

完整的代码如下,函数 pickIndex 的时间复杂度为 O(logn)。
*/
    vector<int> pre_sum;
    Solution(vector<int>& w) {
        pre_sum.resize(w.size(), 0);
        int sum = 0;
        for(int i=0;i<w.size();i++){
            sum += w[i];
            pre_sum[i] = sum;
        }
    }
    
    int pickIndex() {
        int rannd_num = rand() % pre_sum.back() + 1;
        int left = 0;
        int right = pre_sum.size() - 1;
        while(left <= right){
            int mid = left + (right - left) /2;
            if(rannd_num <= pre_sum[mid]){
                if(mid == 0 || rannd_num > pre_sum[mid-1]){
                    return mid;
                }
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
        return -1;

    }
};

/**
 * Your Solution object will be instantiated and called as such:
 * Solution* obj = new Solution(w);
 * int param_1 = obj->pickIndex();
 */

12. 排序

剑指 Offer II 074. 合并区间
class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        // 1. v[1][0] >= v[0][0] && v[1][0] <= v[0][1]才能合并
        // 2. 排序,保证了v[1][0] >= v[0][0]这个条件,因此只需要考虑v[1][0] <= v[0][1]这部分
        vector<vector<int>> res;
        sort(intervals.begin(), intervals.end());
        for(int i=0;i<intervals.size();i++){
            int left = intervals[i][0];
            int right = intervals[i][1];
            if(res.size() == 0 || res.back()[1] < left){
                res.push_back(intervals[i]);
            }
            else{
                int tmp = max(res.back()[1], right);
                res.back()[1] = tmp;
            }
        }

        return res;
    }
};
剑指 Offer II 075. 数组相对排序
class Solution {
public:
    vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) {
        // 0. 时间复杂度o(nlogn)
        // 1. 构建辅助hashmap,遍历arr2构建基础map
        unordered_map<int, int> help_map;
        vector<int> res;
        for(int i=0;i<arr2.size();i++){
            help_map.emplace(arr2[i], 0);
        }

        // 2. 遍历arr1确定每个数字出现的次数
        vector<int> sub_res;
        for(int i=0;i<arr1.size();i++){
            if(help_map.count(arr1[i])){
                int value = help_map[arr1[i]];
                help_map[arr1[i]] = value + 1;
            }else{
                sub_res.push_back(arr1[i]);
            }
        }

        // 3. 组成结果数组
        for(int i=0;i<arr2.size();i++){
            int tmp = help_map[arr2[i]];
            while(tmp > 0){
                res.push_back(arr2[i]);
                tmp--;
            }
        }
        sort(sub_res.begin(), sub_res.end());
        for(int i=0;i<sub_res.size();i++){
            res.push_back(sub_res[i]);
        }

        return res;

    }
};

计数排序
题目中明确数组内数字的范围为 0 ~ 1000,据此可以考虑使用计数排序。首先使用一个长度为 1001 的数组 counts 统计数组 arr1 内每个数字的出现次数,之后根据题目要求先排序数组 arr2 内出现的数字,最后排序 counts 内剩下的数字。

由于题目中已经明确辅助数组的长度,所以空间复杂度可以认为是 O(1),若数组 arr1 和 arr2 的长度分别为 m 和 n,那么算法的总时间复杂度为 O(n+m)。

class Solution {
public:
    vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) {
        vector<int> counts(1001, 0);
        for (auto& n : arr1) {
            counts[n]++;
        }

        int i = 0;
        // 排序 arr2 内的数字
        for (auto& n : arr2) {
            while (counts[n] > 0) {
                arr1[i++] = n;
                counts[n]--;
            }
        }
        // 排序剩下的数字
        for (int j = 0; j < counts.size(); ++j) {
            while (counts[j] > 0) {
                arr1[i++] = j;
                counts[j]--;
            }
        }
        return arr1;
    }
};

13. 回溯法

剑指 Offer II 079. 所有子集
class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> subsets(vector<int>& nums) {
        // 数组中的元素 互不相同--> 组合问题
        // 回溯法
        if(nums.size() <= 0) return res;
        
        vector<int> pre;
        findnextcombine(nums, 0, pre);
        return res;

    }
    void findnextcombine(vector<int>& nums, int depth, vector<int>& pre){
        res.push_back(pre);

        for(int i=depth;i<nums.size();i++){
            pre.push_back(nums[i]);
            findnextcombine(nums, i+1, pre);
            pre.pop_back();
        }
    }
};
剑指 Offer II 080. 含有 k 个元素的组合
class Solution {
public:
    vector<vector<int>> res;
    // 回溯法解决组合问题,先完成树状结构
    //和全排列问题相比不再需要判断是否使用过,因为每次遍历不是从数组的开头开始的
    // 一开始对于最后一个测试用例一直显示超时,在于每次返回的是vector<vector<int>>,而不是直接返回viod方法
    // vector<bool> used;

    void findnextcombine(vector<int>& nums, int k, int depth, vector<int>& pre) {
        if(pre.size() == k){
            res.push_back(pre);
            return;
        }
        // 不使用剪枝
        // 剪枝j<nums.size() 改成:nums.size() - (k - pre.size()) + 1
        for(int j = depth;j<nums.size();j++){
            pre.push_back(nums[j]);
            findnextcombine(nums, k, j+1, pre);
            pre.pop_back();
        }
        return;
    }
    vector<vector<int>> combine(int n, int k) {
        if(k>n || n<=0 || k<=0){
            return res;
        }
        res.clear();

        vector<int> nums;
        for(int i =0;i<n;i++){
            nums.push_back(i+1);
        }

        vector<int> pre;
        findnextcombine(nums, k, 0, pre);
        return res;

    }
};
剑指 Offer II 081. 允许重复选择元素的组合
class Solution {
public:
    vector<vector<int>> res;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        // 无重复元素的正整数数组 candidates
        vector<int> pre;
        findnextcombine(candidates, 0, target, pre);
        return res;
    }

    void findnextcombine(vector<int>& candidates,int depth, int sum, vector<int>& pre){
        if(sum < 0) return;
        else if(sum == 0){
            res.push_back(pre);
            return;
        }
        for(int i=depth;i<candidates.size();i++){
            sum = sum - candidates[i];
            pre.push_back(candidates[i]);
            // 可以重复,因此从i开始
            findnextcombine(candidates, i, sum, pre);
            pre.pop_back();
            sum += candidates[i];
            
        }

    }
};
剑指 Offer II 082. 含有重复元素集合的组合
class Solution {
public:

    vector<vector<int>> res;
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        // 可能有重复的数字,每个数字在每个组合中只能用一次
        // 组合问题
        vector<int> pre;
        sort(candidates.begin(), candidates.end());
        findnextcombine(candidates, 0, target, pre);
        return res;

    }

    void findnextcombine(vector<int>& candidates, int depth, int sum, vector<int>& pre){
        if(sum < 0) return;
        if(sum == 0){
            res.push_back(pre);
            return;
        }

        for(int i=depth;i<candidates.size();i++){
        // 一样的层里一样的数字面对的情况一样
            if(i > depth && candidates[i] == candidates[i-1]){
                continue;
            }
            sum -= candidates[i];
            pre.push_back(candidates[i]);
            findnextcombine(candidates, i+1, sum, pre);
            pre.pop_back();
            sum += candidates[i];
        }
    }
};
剑指 Offer II 083. 没有重复元素集合的全排列
class Solution {
public:
    vector<vector<int>> res;
    vector<bool> used;
    // 回溯法
    // perms(nums[0...n-1]) = {取出一个数字} + perms(nums[{0...n-1} - 这个数字])
    // index代表的更多是一种层级的概念
    vector<vector<int>> findnextpermute(vector<int>& nums, int index, vector<int>& pre) {
        cout<<"index的值:"<<index<<endl;
        if(index == nums.size()){
            res.push_back(pre);
            return res;
            
        }
        for(int i = 0;i<nums.size();i++){
            if(!used[i]){
                pre.push_back(nums[i]);
                used[i] = true;
                cout<<"递归前nums[i]的值:"<<nums[i]<<endl;
                findnextpermute(nums, index+1, pre);
                cout<<"递归后nums[i]的值:"<<nums[i]<<endl;
                pre.pop_back();
                used[i] = false;              
            }
        }
        return res;

    } 
    vector<vector<int>> permute(vector<int>& nums) {
        if(nums.size() <= 0){
            return res;
        }
        used = vector<bool>(nums.size(), false);
        vector<int> pre;
        findnextpermute(nums, 0, pre);
        return res;

    }
};

剑指 Offer II 084. 含有重复元素集合的全排列
class Solution {
public:
    vector<vector<int>> res;
    vector<bool> used;
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        if(nums.size() <= 0) return res;

        vector<int> pre;
        used = vector<bool>(nums.size(), false);
        sort(nums.begin(), nums.end());
        generatePermute(nums, 0, pre);
        return res;
    }

    void generatePermute(vector<int>& nums, int depth, vector<int>& pre){
        if(depth == nums.size()){
            res.push_back(pre);
            return;
        }

        for(int i=0;i<nums.size();i++){
            if(used[i] || (i > 0 && nums[i] == nums[i-1] && !used[i-1])){
                continue;
            }
            used[i] = true;
            pre.push_back(nums[i]);
            generatePermute(nums, depth+1, pre);
            used[i] = false;
            pre.pop_back();
        }
    }
};
剑指 Offer II 085. 生成匹配的括号
class Solution {
    bool valid(const string& str) {
        int balance = 0;
        for (char c : str) {
            if (c == '(') {
                ++balance;
            } else {
                --balance;
            }
            if (balance < 0) {
                return false;
            }
        }
        return balance == 0;
    }

    void generate_all(string& current, int n, vector<string>& result) {
        // 我们可以生成所有 2^2n个‘(’ 和‘)’ 字符构成的序列,然后我们检查每一个是否有效即可。
        if (n == current.size()) {
            if (valid(current)) {
                result.push_back(current);
            }
            return;
        }
        current += '(';
        generate_all(current, n, result);
        current.pop_back();
        current += ')';
        generate_all(current, n, result);
        current.pop_back();
    }
public:
    vector<string> generateParenthesis(int n) {
        vector<string> result;
        string current;
        generate_all(current, n * 2, result);
        return result;
    }
};

剑指 Offer II 086. 分割回文子字符串
class Solution {
public:
    vector<vector<string>> res;
    vector<vector<string>> partition(string s) {
        // 不能回头就是组合问题
        vector<string> pre;
        findcombine(s, 0, pre);
        return res;


    }
    void findcombine(string s, int depth, vector<string>& pre){
        if(depth == s.size()){
            res.push_back(pre);
            return;
        }
        for(int i=depth;i<s.size();i++){
            if(isPalindrom(s, depth, i)){
                string sub_s = s.substr(depth, i-depth+1);
                pre.push_back(sub_s);
                findcombine(s, i+1, pre);
                pre.pop_back();
            }
        }
    }
    bool isPalindrom(string s, int left, int right){
        while(left < right){
            if(s[left] != s[right]){
                return false;
            }
            left++;
            right--;
        }
        return true;
    }
};
剑指 Offer II 087. 复原 IP (*)
class Solution {
public:
    vector<string> res;
    vector<bool> used;
    vector<string> restoreIpAddresses(string s) {
        // 暴力法 全排列
        // 得到包含...的全部排列形式,然后判断是否有效,严重超时
        if(s.size() > 12 || s.size() < 4) return res;
        string pre;
        for(int i=0;i<3;i++){
            s += '.';
        }
        used = vector<bool>(s.size(), false);
        generatePermute(s, 0, pre);
        return res;
    }
    void generatePermute(string s, int depth, string pre){
        if(depth > s.size()) return;
        if(depth == s.size()){
            if(isValid(pre)){
                res.push_back(pre);
            }
            return;
        }
        for(int i=0;i<s.size();i++){
            if(used[i] || (i > 0 && s[i] == s[i-1] && s[i]=='.' && !used[i-1])){
                continue;
            }
            used[i] = true;
            pre += s[i];
            generatePermute(s, depth+1, pre);
            used[i] = false;
            pre.pop_back();
        }
    }
    bool isValid(string ss){
        if(ss[0] == '0' && ss[1] != '.') return false;
        if(ss[0] == '.' || ss[ss.size() - 1] == '.') return false;
        vector<int> index;
        for(int i=0;i<ss.size();i++){
            if(ss[i] == '.' && ss[i+1] == '0'){
                return false;
            }
            if(ss[i] == '.'){
                index.push_back(i);
            }
        }

        if(index[0] > 3) return false;
        if(index[1] - index[0] > 3 || index[1] - index[0] <= 0) return false;
        if(index[2] - index[1] > 3 || index[2] - index[1] <= 0) return false;
        if(ss.size() - index[2] > 4 || ss.size() - index[2] <= 1) return false;

        int flag = 0;
        string sub_s1 = ss.substr(0, index[0] + 1);
        if(stoi(sub_s1) >= 0 && stoi(sub_s1) <=255){
            flag += 1;
        }
        
        string sub_s2 = ss.substr(index[0], index[1] - index[0] + 1);
        if(stoi(sub_s2) >= 0 && stoi(sub_s2) <=255){
            flag += 1;
        }

        string sub_s3 = ss.substr(index[1], index[2] - index[1] + 1);
        if(stoi(sub_s3) >= 0 && stoi(sub_s3) <=255){
            flag += 1;
        }

        string sub_s4 = ss.substr(index[2], ss.size() - index[2]);
        if(stoi(sub_s4) >= 0 && stoi(sub_s4) <=255){
            flag += 1;
        }

        if(flag == 4) return true;
        cout<<"======="<<endl;
        return false;

    }
};
class Solution {
private:
    // 回溯
    void helper(string& s, int i, int segI, string seg, string ip, vector<string>& ret) {
        // 当前 ip + seg 符合要求
        if (i == s.size() && segI == 3 && isValidSeg(seg)) {
            ret.push_back(ip + seg);
        }
        else if (i < s.size() && segI <= 3) {
            string temp = seg + s[i];
            // 将当前字符拼接到当前分段之后,且拼接后的分段合法
            if (isValidSeg(temp)) {
                helper(s, i + 1, segI, temp, ip, ret);
            }

            // 将当前作为新的分段数字的开始,但是必须满足一个 IP 地址的分段最多只有 4 个,
            // 并且当开始一个新的分段数字时前一个分段不能为空
            if (seg.size() > 0 && segI < 3) {
                string str{s[i]};
                helper(s, i + 1, segI + 1, str, ip + seg + ".", ret);
            }
        }
    }

    // 判断分段合法性
    bool isValidSeg(string& seg) {
        return stoi(seg) <= 255 && (seg == "0" || seg[0] != '0');
    }
public:
    vector<string> restoreIpAddresses(string s) {
        vector<string> ret;
        helper(s, 0, 0, "", "", ret);
        return ret;
    }
};

14. 动态规划

剑指 Offer II 088. 爬楼梯的最少成本
class Solution {
public:
    int minCostClimbingStairs(vector<int>& cost) {
        vector<int> memo(cost.size()+1, 0);
        if(cost.size() == 1){
            return cost[0];
        }
        memo[1] = 0;
        for(int i=2;i<=cost.size();i++){
            memo[i] = min(memo[i-2] + cost[i-2], memo[i-1] + cost[i-1]);
        }
        return memo[cost.size()];
    }
};
剑指 Offer II 089. 房屋偷盗
class Solution {
public:
    int rob(vector<int>& nums) {
        // 动态规划
        int n = nums.size();
        vector<int> memo = vector<int>(n+1, -1);
        if(n == 1){
            return nums[0];
        }
        
        memo[1] = nums[0];
        memo[2] = max(nums[0], nums[1]);
        for(int i =3;i<=n;i++){
            memo[i] = max(memo[i-1],memo[i-2]+nums[i-1]);
        }
        return memo[n];
    }
};

剑指 Offer II 090. 环形房屋偷盗
class Solution {
public:
    enum{
        MAX = 0x3f3f3f3f
    };
    int robrange(vector<int>& nums, int start, int end){
        vector<int> memo(end+1, 0);
        memo[start+1] = nums[start];
        memo[start+2] = max(nums[start], nums[start + 1]);
        for(int i=start+3;i<=end;i++){
            memo[i] = max(memo[i-1], memo[i-2] + nums[i-1]);
        }
        return memo[end];
    }
    int rob(vector<int>& nums) {
        // 一个是求小偷从下标为 0 的房屋开始到下标为 n - 2 的房屋结束能偷得的最大财物,另一个是求小偷从下标为 1 的房屋开始到下标为 n - 1 的房屋结束能偷得的最大财物。

        if(nums.size() == 1) return nums[0];
        if(nums.size() == 2) return max(nums[0], nums[1]);

        return max(robrange(nums, 0, nums.size()-1), robrange(nums, 1, nums.size()));

    }
};
剑指 Offer II 091. 粉刷房子
class Solution {
public:
    int minCost(vector<vector<int>>& costs) {
        // dp[i][j] = min(dp[i−1][(j+1)mod3],dp[i−1][(j+2)mod3])+costs[i][j]
        int m = costs.size();
        int n = costs[0].size();
        vector<vector<int>> memo(m, vector<int>(n,0));

        for(int j=0;j<3;j++){
            memo[0][j] = costs[0][j];
        }

        for(int i=1;i<m;i++){
            for(int j=0;j<3;j++){
                
                memo[i][j] = min(memo[i-1][(j+1)%3], memo[i-1][(j+2)%3]) + costs[i][j];
            }
        }

        return min(min(memo[m - 1][0], memo[m - 1][1]), memo[m - 1][2]);

    }
};
剑指 Offer II 092. 翻转字符(*)
class Solution {
public:
    int minFlipsMonoIncr(string s) {
        // 求解问题有多个步骤,每个步骤又有若干个选择的问题,若是要求过程步骤中所有得到的结果,则应想到用回溯法思想解决该问题,
        // 但这是要求问题的最优解,我们应该想到运用动态规划算法。
        /*
        一.若当前字符i下标的字符是‘0’:
            zero[i]不需要转换,zero[i] = zero[i-1].
            one[i]需要将‘0’转换为‘1’,有两种情况:
                1.若前一位是‘0’转换的‘1’,则one[i] = one[i-1] + 1;
                2.若前一位本身就是'1',则one[i] = zero[i-1] + 1;
            即one[i]为两数组保存的前一位的最小值+1,one[i] = Math.min(one[i-1], zero[i-1]) + 1.

        二.若当前下标i的字符为‘1’:
            zero[i]需要把'1'转换成'0',当前位的最小转换次数应该比前一位转换次数多一次,即zero[i] = zero[i-1] + 1.
            one[i]不需要转换, 但也有两种情况:
                1.若前一位是‘0’,则one[i] = zero[i-1];
                2.若前一位是‘1’,则one[i] = one[i-1];
            即one[i]为两数组保存的前一位的最小值,one[i] = Math.min(one[i-1], zero[i-1]).

        而它们的最小子问题的解即初始状态下标i=0的字符是‘0’则zero[0] = 0, one[0] = 1, i=0的字符是‘1’则zero[0] = 1, one[0] = 0, 
        */
        int n = s.size();
        vector<int> zero(n);
        vector<int> one(n);

        zero[0] = s[0] == '0'?0:1;
        one[0] = s[0] == '0'?1:0;

        for(int i=1;i<s.size();i++){
            if(s[i] == '0'){
                zero[i] = zero[i-1];
                one[i] = min(one[i-1], zero[i-1]) + 1;
            }
            if(s[i] == '1'){
                zero[i] = zero[i-1] + 1;
                one[i] = min(one[i-1], zero[i-1]);
            }
        }
        return min(one[n-1], zero[n-1]);
    }
};
剑指 Offer II 093. 最长斐波那契数列(*)
class Solution {
public:
    int lenLongestFibSubseq(vector<int>& arr) {
        unordered_map<int, int> indices;
        int n = arr.size();
        for (int i = 0; i < n; i++) {
            indices[arr[i]] = i;
        }
        vector<vector<int>> dp(n, vector<int>(n));
        int ans = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i - 1; j >= 0 && arr[j] * 2 > arr[i]; j--) {
                int k = -1;
                if (indices.count(arr[i] - arr[j])) {
                    k = indices[arr[i] - arr[j]];
                }
                if (k >= 0) {
                    dp[j][i] = max(dp[k][j] + 1, 3);
                }
                ans = max(ans, dp[j][i]);
            }
        }
        return ans;
    }
};
剑指 Offer II 094. 最少回文分割(*)
class Solution {
public:
    int minCut(string s) {
        vector<vector<bool>> isPalindrome(s.size(), vector<bool>(s.size(), false));
        // 确定是否是回文,很巧妙,具体的做法就是先确定回文的中心,之后从中心开始扩散确定回文的情况。
        int cenL = 0;
        int cenR = 0;
        while(cenR < s.size()){
            int left = cenL;
            int right = cenR;
            while(left >=0 && right<s.size() && s[left]==s[right]){
                isPalindrome[left][right] = true;
                left--;
                right++;
            }
            cenR > cenL?cenL++:cenR++;
        }

        // 动态规划
        vector<int> memo(s.size(), 0);
        for(int i=0;i<s.size();i++){
            // s[0] ~ s[i] 本来就是回文串
            if(isPalindrome[0][i]){
                memo[i] = 0;
            }
            else{
                // dp[i] 初始化为 s[0] ~ s[i] 的最大切割次数
                memo[i] = i;
                for(int j=i;j>0;j--){
                    if(isPalindrome[j][i]){
                        memo[i] = min(memo[i], memo[j-1]+1);
                    }
                }
            }
        }
        return memo[s.size() - 1];
    }
};
剑指 Offer II 095. 最长公共子序列
class Solution {
public:
    int longestCommonSubsequence(string text1, string text2) {
        // 最长公共子序列问题是典型的二维动态规划问题
        int m = text1.size();
        int n = text2.size();
        vector<vector<int>> memo(m+1, vector<int>(n+1,0));
        for(int i=1;i<=m;i++){
            char a = text1[i-1];
            for(int j=1;j<=n;j++){
                char b = text2[j-1];
                if(a == b){
                    memo[i][j] = memo[i-1][j-1] + 1;
                }
                else{
                    memo[i][j] = max(memo[i-1][j], memo[i][j-1]);
                }
            }
        }
        return memo[m][n];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值