力扣算法题解笔记

15. 三数之和

42. 接雨水

方法1:动态规划

  1. 预计算左侧最大值数组 ldp 和右侧最大值数组 rdp
  2. 对每个位置取 min(ldp[i], rdp[i]) - height[i] 累加。

方法2:双指针优化

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

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int j=0;
        int maxLength =0;
        unordered_set<char> charIndexSet;
        for(int i=0; i<s.size();i++){
            while(charIndexSet.find(s[i]) != charIndexSet.end()){
                charIndexSet.erase(s[j]);
                j++;
            }
            charIndexSet.insert(s[i]);
            maxLength =max(maxLength,i-j+1);
        }
        return maxLength;
    }
};

239. 滑动窗口最大值

  1. 队列保存索引,且队列对应值单调递减。
  2. 窗口滑动时,移除超出窗口范围的队首元素。
  3. 新元素入队前,移除队尾小于它的元素。
  4. 当窗口形成后(i >= k-1),队首即为最大值。

76. 最小覆盖子串

41. 缺失的第一个正数

  1. 遍历数组,将正整数 nums[i] 交换到索引 nums[i]-1 的位置。
  2. 再次遍历,找到第一个不满足 nums[i] == i+1 的位置。
  3. 边界:若全匹配,返回 n+1

48. 旋转图像

  1. 转置矩阵:交换 matrix[i][j]matrix[j][i]
  2. 水平翻转每行:交换 matrix[i][j]matrix[i][n-j-1]

17. 电话号码的字母组合

39. 组合总和

79. 单词搜索

74. 搜索二维矩阵

20. 有效的括号

394. 字符串解码

  1. 遇到数字时解析完整数值入数字栈。
  2. 遇到 [ 时,当前字符串入栈并重置。
  3. 遇到 ] 时,弹出数字和字符串,拼接后更新结果。

739. 每日温度

347. 前K个高频元素

  1. 统计频率存入哈希表。
  2. 维护大小为 k 的最小堆,按频率排序。
  3. 遍历哈希表,堆满后弹出较小频率元素。
class Solution {

    struct myComparison{
        bool operator()(pair<int,int>&p1,pair<int,int>&p2){
            return p1.second>p2.second;//小顶堆是大于号
        }
    };

public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> map;
        for(int i=0; i<nums.size(); i++){
            map[nums[i]]++;  
        }

        priority_queue<pair<int, int>, vector<pair<int, int>>,myComparison> pri_que;
        for(auto& it: map){
            pri_que.push(it);
            if(pri_que.size() >k){
                pri_que.pop();
            }
        }

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

146 LRU缓存

  1. 设计一个双向链表,实现push_fornt函数、remove_node函数、get_node函数
  2. push_fornt函数是在链表头部添加一个结点,实现操作如下所示: x->pre =dummy; x->next=dummy->next;x->pre->next =x;x->next->pre =x;
  3. remove_node函数是删除一个结点,实现如下: x->pre->next = x->next;x->next->pre = x->pre;
  4. get_node函数是获取key对应的节点,同时把该节点移动到链表头部,操作如下所示: 用hashmap通过key找到node,先remove结点node,然后再push_front结点
  5. LRU的每次Put操作,需要先判断结点是否存在该hashmap中,存在则直接替换key对应的值,不存在则放到链表头部,如果hashmap的大小大于了LRU缓存大小,那么则删除hashmap中对应的key,删除链表中最后一个结点。注用DLinkedNode* back_node = dummy->pre;获取最后一个结点
struct Dlinked {
    int key, value;
    Dlinked *next, *pre;
    Dlinked(int _key, int _value) : key(_key), value(_value), next(nullptr), pre(nullptr) {}
};

class LRUCache {
private:
    int capacity;
    Dlinked* dummy;
    unordered_map<int, Dlinked*> key_to_node;

    void remove_node(Dlinked* x) {
        x->pre->next = x->next;
        x->next->pre = x->pre;
    }

    void push_front(Dlinked* x) {
        x->next = dummy->next;
        x->pre = dummy;
        x->pre->next = x;
        x->next->pre = x;
    }

    Dlinked* get_node(int key) {
        auto it = key_to_node.find(key);
        if (it == key_to_node.end()) return nullptr;
        Dlinked* node = it->second;
        remove_node(node);
        push_front(node);
        return node;
    }

public:
    LRUCache(int capacity) {
        this->capacity = capacity;
        dummy = new Dlinked(0, 0);
        dummy->pre = dummy;
        dummy->next = dummy;
    }

    int get(int key) {
        Dlinked* node = get_node(key);
        return node ? node->value : -1;
    }

    void put(int key, int value) {
        Dlinked* node = get_node(key);
        if (node) {
            node->value = value;
            return;
        }
        node = new Dlinked(key, value);
        key_to_node[key] = node;
        push_front(node);
        if (key_to_node.size() > capacity) {
            Dlinked* back_node = dummy->pre;
            remove_node(back_node);
            key_to_node.erase(back_node->key);
            delete back_node;
        }
    }
};

5 最长回子串

    pair<int,int> expandFormCenter(string s,int left, int right){
        while(left >=0 && right < s.size()&& s[left] == s[right]){
            left --;
            right ++;
        }
        return {left+1, right-1};
    } 

    string longestPalindrome(string s) {
        
        int start = 0,end = 0;
        for (int i = 0; i < s.size(); i++) {
            auto [left1, right1] = expandFormCenter(s, i, i);
            auto [left2, right2] = expandFormCenter(s, i, i + 1);
            
            int len1 = right1 - left1 + 1;
            int len2 = right2 - left2 + 1;
            int curr_len = end - start + 1; 
    
            if (left1 <= right1 && len1 > curr_len) {
                start = left1;
                end = right1;
            }
            if (left2 <= right2 && len2 > curr_len) {
                start = left2;
                end = right2;
            }
        }
        return s.substr(start,end-start + 1);
    }

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

class Solution {
    public:
        int getKthElement(const vector<int>& nums1, const vector<int>& nums2, int k) {
            /* 主要思路:要找到第 k (k>1) 小的元素,那么就取 pivot1 = nums1[k/2-1] 和 pivot2 = nums2[k/2-1] 进行比较
             * 这里的 "/" 表示整除
             * nums1 中小于等于 pivot1 的元素有 nums1[0 .. k/2-2] 共计 k/2-1 个
             * nums2 中小于等于 pivot2 的元素有 nums2[0 .. k/2-2] 共计 k/2-1 个
             * 取 pivot = min(pivot1, pivot2),两个数组中小于等于 pivot 的元素共计不会超过 (k/2-1) + (k/2-1) <= k-2 个
             * 这样 pivot 本身最大也只能是第 k-1 小的元素
             * 如果 pivot = pivot1,那么 nums1[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums1 数组
             * 如果 pivot = pivot2,那么 nums2[0 .. k/2-1] 都不可能是第 k 小的元素。把这些元素全部 "删除",剩下的作为新的 nums2 数组
             * 由于我们 "删除" 了一些元素(这些元素都比第 k 小的元素要小),因此需要修改 k 的值,减去删除的数的个数
             */
    
            int m = nums1.size();
            int n = nums2.size();
            int index1 = 0, index2 = 0;
    
            while (true) {
                // 边界情况
                if (index1 == m) {
                    return nums2[index2 + k - 1];
                }
                if (index2 == n) {
                    return nums1[index1 + k - 1];
                }
                if (k == 1) {
                    return min(nums1[index1], nums2[index2]);
                }
    
                // 正常情况
                int newIndex1 = min(index1 + k / 2 - 1, m - 1);
                int newIndex2 = min(index2 + k / 2 - 1, n - 1);
                int pivot1 = nums1[newIndex1];
                int pivot2 = nums2[newIndex2];
                if (pivot1 <= pivot2) {
                    k -= newIndex1 - index1 + 1;
                    index1 = newIndex1 + 1;
                }
                else {
                    k -= newIndex2 - index2 + 1;
                    index2 = newIndex2 + 1;
                }
            }
        }
    
        double findMedianSortedArrays(vector<int>& nums1, vector<int>& nums2) {
            int totalLength = nums1.size() + nums2.size();
            if (totalLength % 2 == 1) {
                return getKthElement(nums1, nums2, (totalLength + 1) / 2);
            }
            else {
                return (getKthElement(nums1, nums2, totalLength / 2) + getKthElement(nums1, nums2, totalLength / 2 + 1)) / 2.0;
            }
        }
}

2 两数相加

class Solution {
    public:
        ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
            ListNode* dummy = new ListNode(0);
            ListeNode* cur = dummy;
            int carry = 0;
            while(l1 || l2 || carry){
                int sum = carry;
                sum += l1 ? l1->val:0;
                sum += l2 ? l2->val:0;
                if(l1) l1=l1->next;
                if(l2) l2=l2->next;
                carry = sum /10;
                cur->next = new ListNode(sum % 10);
                cur = cur->next;
            }
        }
}

快速排序

void quickSort(std::vector<int>& arr, int left, int right) {
    if (left >= right) return;

    int pivot = arr[left];
    int i = left + 1;
    int j = right;

    while (i <= j) {
        while (i <= j && arr[i] <= pivot) i++;
        while (i <= j && arr[j] >= pivot) j--;
        if (i < j) std::swap(arr[i], arr[j]);
    }
    std::swap(arr[left], arr[j]);

    quickSort(arr, left, j - 1);
    quickSort(arr, j + 1, right);
}

归并排序

void merge(std::vector<int>& arr, int left, int mid, int right) {
    std::vector<int> temp(right - left + 1);
    int i = left, j = mid + 1, k = 0;

    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }

    while (i <= mid) {
        temp[k++] = arr[i++];
    }

    while (j <= right) {
        temp[k++] = arr[j++];
    }

    for (i = left, k = 0; i <= right; ++i, ++k) {
        arr[i] = temp[k];
    }
}

void mergeSort(std::vector<int>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}

堆排序

void heapify(std::vector<int>& arr, int n, int i) {
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;

    if (left < n && arr[left] > arr[largest]) {
        largest = left;
    }

    if (right < n && arr[right] > arr[largest]) {
        largest = right;
    }

    if (largest != i) {
        std::swap(arr[i], arr[largest]);
        heapify(arr, n, largest);
    }
}

void heapSort(std::vector<int>& arr) {
    int n = arr.size();

    for (int i = n / 2 - 1; i >= 0; i--) {
        heapify(arr, n, i);
    }

    for (int i = n - 1; i > 0; i--) {
        std::swap(arr[0], arr[i]);
        heapify(arr, i, 0);
    }
}