LeetCode解题思路(每日更新)

LeetCode解题思路(每日更新)

标准函数

0.1 algorithm

排序:std::sort 基础语法

0.2 cmath

幂运算:std::pow 基础语法

一、数组

  • 存放在连续内存空间上的相同类型数据的集合
  • 因为数组在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址
  • 数组的元素是不能删的,只能覆盖。

1.1 二分法

  • 需要是有序数组

LeetCode704 | 二分查找

1) 左闭右开
  • 在确定候选区间时,右区间需要多给一位,因此,初始化时,right给到nums.size();
  • while条件中,left==right时,[left,right)为空集,无意义;
  • 二分缩小区间时,nums[middle]不符合条件时(nums[middle] > target),right取不到,所以right=middle;
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0, right = nums.size();
        while(left < right) {
            int middle = left + ((right-left) >> 1);
            if(target > nums[middle]) {
            	% target落在右半边,更新左边界
                left = middle + 1;
            } else if(target < nums[middle]) {
            	% target落在左半边,更新右边界
                right = middle;
            } else {
                return middle;
            }
        }
        return -1;
    }
};
2) 左闭右闭

相反的,

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

LeetCode35 | 搜索插入位置

二分法解法(左闭右开)

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

暴力解法

  • 依次查询
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] >= target) {
                return i;
            }
        }
        return nums.size();
        // 错误返回:numshttps://www.yuque.com/chengxuyuancarl/wnx1np/ktwax2.size()-1
    }
};

1.2 双指针

LeetCode27 | 移除元素

双指针解法

  • 通过快指针判断条件,慢指针记录数组
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slow = 0;
        for (int fast = 0; fast < nums.size(); fast++) {
            if(nums[fast] != val) {
                nums[slow] = nums[fast];
                slow ++;
                // nums[slow++] = nums[fast];
            }
        }
        return slow;
    }
};

暴力解法

  • 因为整体前移,所以下标需要前移一位
  • 数组移除元素等价于将后面的元素覆盖当前位置元素(下标前移、数组个数缩小)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size = nums.size();
        for (int i = 0; i < size; i++) {
            if (nums[i] == val) {
                for (int j = i+1; j < nums.size(); j++) {
                    nums[j-1] = nums[j];
                }

                i--; // 因为整体前移,所以下标需要前移一位
                size--; // 数组大小手动缩小(类似ector的计数器)
            }
        }
        return size;
    }
};
------------------------------------------------------------
错误解法(没有移动下标):
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int res  = 0;
        for (int i = 0; i < nums.size(); i++) {
            if (nums[i] == val) {
                for (int j = i+1; j < nums.size(); j++) {
                    nums[j-1] = nums[j];
                }
            } else {
                res ++;
            }
        }
        return res;
    }
};

LeetCode977 | 有序数组的平方

双指针

  • 通过判断首尾指针对应数的大小确定填入哪个
  • for循环终止条件是首位指针相遇
#include <cmath>

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> res(nums.size(), 0);
        int n = nums.size() - 1;
        for (int i = 0, j = nums.size()-1; i <= j;) {
            if (pow(nums[i],2) > pow(nums[j],2)) {
                res[n--] = pow(nums[i],2);
                i++;
            } else {
                res[n--] = pow(nums[j],2);
                j--; 
            }
        }
        return res;
    }
};

暴力解法

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for(int i = 0; i < nums.size(); i++) {
            nums[i] *= nums[i];
        }
        sort(nums.begin(), nums.end());
        return nums;
    }
};

1.3 滑动窗口

  • 关注窗口左右边界更新条件及窗口内容
  • 核心思路:不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果(窗口内容相关)

LeetCode209 | 长度最小的子数组

在这道题里,窗口内求和,右边界为for循环下标(可以遍历到所有情况),通过判断窗口内的和与目标值的关系确定是否需要更新左边界

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int res = INT32_MAX; 
        int sum = 0; // 滑动窗口数值之和
        int i = 0; // 滑动窗口左边界
        int subLength = 0; // 当前滑动窗口大小
        for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            // 如果总和超过target,则缩小滑动窗口
            while (sum >= target) {
                subLength = j - i + 1;
                res = subLength < res ? subLength : res;
                sum -= nums[i++];
            }
        }
        return res < INT32_MAX ? res : 0;
    }
};

LeetCode59 | 螺旋矩阵II

以左闭右闭的思路写,以填入数的上限为while循环条件,因为不确定是从哪个方向闭合最后一段,所以在每个方向for循环后都设置判断条件,如果上下或左右相接说明结束填写

  • 关键点:遍历和更新矩阵的四个顶点,类似四个滑动窗口,边界点变为四个顶点,通过窗口内容填写矩阵
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n, 0));
        int l = 0, r = n - 1;
        int u = 0, d = n - 1;
        int num = 1;
        while (num < pow(n,2)+1) {
            for (int i = l; i <= r; ++i) { 
                res[u][i] = num;                
                ++ num;
            }
            if (++u > d) break;
            for (int i = u; i <= d; ++i) {
                res[i][r] = num;
                ++num;
            }
            if (--r < l) break;
            for (int i = r; i >= l; --i) {
                res[d][i] = num;                
                ++num;
            }
            if (--d < u) break;
            for (int i = d; i >= u; --i) {
                res[i][l] = num;        
                ++num;
            }
            if (++l > r) break;
        }
        return res;
    }
};

二、链表

  • 分类:单链表、双链表、循环链表

  • 节点(数据域+指针域)

  • 通过指针域的指针链接在内存中各个节点,非连续

  • while中条件为作用域内需要查找下一个节点的节点不为空指针
    在这里插入图片描述

  • 链表定义:

//单链表
struct ListNode {
	int val;
	ListNode *next;
	ListNode(int x) : val(x), next(NULL) {}
};

2.1 移除链表

LeetCode203 | 移除链表元素

  • 通过设置虚节点的方式统一头节点和非头节点的操作
  • cur从dummyHead开始(创建、next指向head、cur设为dummyHead)
  • 通过判断下一个节点是否为空指针,判断是否到达链表尾部
class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) {
        ListNode* dummyHead = new ListNode(0); //设置虚拟头节点
        dummyHead->next = head;
        ListNode* cur = dummyHead;
        while (cur->next != NULL) {
            if (cur->next->val == val) {
                ListNode* temp = cur->next;
                cur->next = cur->next->next;
                delete temp;
            } else {
                cur = cur->next;
            }
        }
        head = dummyHead->next;
        delete dummyHead;
        return head;
    }
};

2.2 设计链表

LeetCode | 模板

  • get是找到需要操作的节点,addAtIndex与deleteAtIndex都是找到需要操作的前一个节点
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
class MyLinkedList {
public:
    // 定义链表节点结构体
    // struct ListNode {
    //     int val;
    //     ListNode *next;
    //     ListNode(int val) : val(val), next(NULL) {};
    // }

    MyLinkedList() {
        _dummyHead = new ListNode(0);
        _size = 0; // 链表中节点个数,非下标,下标-1
    }
    
    int get(int index) {
        if (index < 0 || index > _size - 1) {
            return -1;
        }
        ListNode* cur = _dummyHead->next;
        while (index--) {
            cur = cur->next;
        }
        return cur->val;
    }
    
    void addAtHead(int val) {
        ListNode* newNode = new ListNode(val);
        newNode->next = _dummyHead->next;
        _dummyHead->next = newNode;
        _size++;
    }
    
    void addAtTail(int val) {
        ListNode* newNode = new ListNode(val);
        ListNode* cur = _dummyHead;
        while (cur->next != NULL) {
            cur = cur->next;
        }
        cur->next = newNode;
        _size++;
    }
    
    void addAtIndex(int index, int val) {
        if (index > _size) return; // 如果index大于链表的长度,则返回空;等于链表长度时,添加在尾部
        if (index < 0) index = 0; // 如果index小于0,则在头部插入节点
        ListNode* newNode = new ListNode(val);
        ListNode* cur = _dummyHead;
        while(index--) {
            cur = cur->next;
        }
        newNode->next = cur->next;
        cur->next = newNode;
        _size++;
    }
    
    void deleteAtIndex(int index) {
        if (index > _size-1 || index < 0) {
            return; // 节点下标最大为_size-1
        }
        ListNode* cur = _dummyHead;
        while(index--) {
            cur = cur->next;
        }
        ListNode* temp = cur->next;
        cur->next = cur->next->next;
        delete temp;
        temp = nullptr;
        _size--;
    }

private:
    int _size;
    ListNode* _dummyHead;
};

2.3 反转链表

LeetCode206 | 反转链表

双指针法:

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* temp; //保存cur的下一个节点,因为接下来要改变cur->next
        ListNode* cur = head;
        ListNode* pre = NULL;
        while(cur) {
            temp = cur->next;
            cur->next = pre;
            // 更新
            pre = cur;
            cur = temp;
        }
        return pre;
    }
};

2.4 两两操作链表

  • cur设置为要操作的链表节点前一个节点(操作1、2,cur=dummyHead;操作3、4,cur=新的1)
  • 节点修改指向后,原指向的节点索引不到,因此需要先创建一个临时节点指向此节点(1、3)
    在这里插入图片描述

LeetCode24 | 两两交换链表中的节点

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* cur = dummyHead;
        while(cur->next != nullptr && cur->next->next != nullptr) {
            ListNode* temp = cur->next; // 1
            ListNode* temp1 = cur->next->next->next; // 3

            cur->next = cur->next->next; // cur->2
            cur->next->next = temp; // 2->1
            cur->next->next->next = temp1; // 1->3

            cur = cur->next->next; // cur=1
        }
        ListNode* result = dummyHead->next;
        delete dummyHead;
        return result;
    }
};

2.5 操作链表中倒数第n个节点

  • 通过快慢指针间隔n+1进行移动,直至快指针移动到队尾

LeetCode19 | 删除链表的倒数第N个节点

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* slow = dummyHead;
        ListNode* fast = dummyHead;
        while(n-- && fast != nullptr) {
            fast = fast->next;
        }
        fast = fast->next; //fast多走一步,使最后slow指向要删除节点的前一个节点
        while(fast != nullptr) {
            fast = fast->next;
            slow = slow->next;
        }
        ListNode* temp = slow->next;
        slow->next = slow->next->next;
        delete temp;
        return dummyHead->next;
    }
};

2.6 找链表中相交的节点

LeetCode 面试题0207| 链表相交

在这里插入图片描述

  • 交点不是数值相等,而是指针相等。求两个链表交点节点的指针
  • 求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置
  • 比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA = headA;
        ListNode* curB = headB;
        int lenA = 0, lenB = 0;
        while (curA != NULL) {
            lenA++;
            curA = curA->next;
        }
        while (curB != NULL) {
            lenB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;
        if (lenB > lenA) {
            swap(lenA, lenB);
            swap(curA, curB);
        }
        int gap = lenA - lenB;
        while(gap--) {
            curA = curA->next;
        }
        while (curA != NULL) {
            if (curA == curB) {
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};

2.7 环形列表

LeetCode142 | 环形链表II

  • 判断是否有环:快慢指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
  • 寻找环的入口:从头结点出发一个指针,从相遇节点也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是环形入口的节点。
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
            if (slow == fast) {
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;
            }
        }
        return NULL;
    }
};

三、哈希表

  • 快速判断一个元素是否出现集合里的时候,就要考虑哈希法

LeetCode242 | 有效的字母异位词

  • 数组即为哈希表
class Solution {
public:
    bool isAnagram(string s, string t) {
        int record[26] = {0};
        for (int i = 0; i < s.size(); i++) {
            record[s[i]-'a']++;
        }
        for (int i = 0; i < t.size(); i++) {
            record[t[i]-'a']--;
        }
        for (int i = 0; i < 26; i++) {
            if (record[i] != 0) {
                return false;
            }
        }
        return true;
    }
};

LeetCode349 | 两个数组的交集

  • unordered_set.find() 结果不为unordered_set.end(),即为找到对应值
  • 去重,查找,vector、unordered_set相互转换
class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> res;
        unordered_set<int> nums1_set(nums1.begin(), nums1.end());
        for (int num : nums2) {
            if (nums1_set.find(num) != nums1_set.end()) {
                res.insert(num);
            }
        }
        return vector<int>(res.begin(), res.end());
    }
};

LeetCode202 | 快乐数

  • 使用unordered_set判断结果是否进入循环
  • 分离整数的各位数
class Solution {
public:
    bool isHappy(int n) {
        unordered_set<int> set;
        while (1) {
            int sum = 0;
            while (n) {
                sum += pow(n%10, 2);
                n /= 10;
            }
            if (sum == 1) {
                return true;
            }
            // 使用unordered_set判断是否循环
            if (set.find(sum) != set.end()) {
                return false;
            } else {
                set.insert(sum);
            }
            n = sum;
        }
    }
};

LeetCode1 | 两数之和

  • 查找给出元素(target-nums[i])对应的下标i
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> map;
        for (int i = 0; i < nums.size(); i++) {
            auto iter = map.find(target-nums[i]);
            if (iter != map.end()) {
                return {i, iter->second};
            }
            map[nums[i]] = i;
        }
        return {};
    }
};

LeetCode454 | 四数之和

  • 两两分组,使用unordered_map记录第一组数求和出现次数,对第二组求和依次查找是否存在满足要求的数,并对出现的次数求和输出
class Solution {
public:
    int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
        unordered_map<int, int> map; // key: a+b  value:出现的次数
        for (int a : nums1) {
            for (int b : nums2) {
                map[a + b]++;
            }
        }
        int count = 0;
        for (int c : nums3) {
            for (int d : nums4) {
                if (map.find(-(c+d)) != map.end()) {
                    count += map[-(c+d)];
                }
            }
        }
        return count;
    }
};

LeetCode383 | 赎金信

  • 与242.有效的字母异位词思路一致
class Solution {
public:
    bool canConstruct(string ransomNote, string magazine) {
        int record[26] = {0};
        for (int i = 0; i < magazine.size(); i++) {
            record[magazine[i]-'a']++;
        }
        for (int i = 0; i < ransomNote.size(); i++) {
            record[ransomNote[i]-'a']--;
            if (record[ransomNote[i]-'a'] < 0) {
                return false;
            }
        }
        return true;
    }
};

LeetCode15 | 三数之和

  • for循环遍历第一数,重复则跳过
  • 通过排序后数组头尾双指针收缩进行剩下两数的选择
  • 找到三元组后,需进行后两数的去重
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> res;
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i++){
            if (nums[i] > 0) return res;
            // 第一个数去重
            if (i > 0 && nums[i] == nums[i-1]) continue;
            // 错误去重a方法,将会漏掉-1,-1,2 这种情况
            /*
            if (nums[i] == nums[i + 1]) {
                continue;
            }
            */
            
            int left = i+1, right = nums.size()-1;
            while (left < right) {
                if (nums[left] + nums[right] > -nums[i]) right--;
                else if (nums[left] + nums[right] < -nums[i]) left++;
                else {
                    res.push_back(vector<int>({nums[i], nums[left], nums[right]}));
                    
                    // 找到一个三元组后,进行去重收缩
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;

                    // 找到答案时,双指针同时收缩
                    right--;
                    left++;
                }
            }
        } 
        return res; 
    }
};

LeetCode18 | 四数之和

  • 在C++中,不同的数据类型表示不同范围的整数值。以下是各种整数数据类型的位数和范围:

    • int: 通常为32位,表示带符号的整数,范围约为 -2,147,483,648 到 2,147,483,647。

    • short: 通常为16位,表示带符号的短整数,范围约为 -32,768 到 32,767。

    • long: 通常为32位,表示带符号的长整数,范围约为 -2,147,483,648 到 2,147,483,647。

    • long long: 通常为64位,表示带符号的长长整数,范围约为 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。

  • 与三数之和类似,通过两层for循环遍历前两个数,后两个数通过双指针收缩获得

class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
        sort(nums.begin(), nums.end());
        for (int i = 0; i < nums.size(); i++) {
            // 去重
            if (i > 0 && nums[i] == nums[i-1]) continue;

            for (int j = i+1; j < nums.size(); j++) {
                // 去重
                if (j > i+1 && nums[j] == nums[j-1]) continue;

                int left = j + 1, right = nums.size() - 1;
                while (left < right) {
                    if ((long)nums[left] + nums[right] < (long)target - nums[i] - nums[j]) left++;
                    else if ((long)nums[left] + nums[right] > (long)target - nums[i] - nums[j]) right--;
                    else {
                        res.push_back({nums[i], nums[j], nums[left], nums[right]});
                        // 去重
                        while(left < right && nums[right] == nums[right-1]) right--;
                        while(left < right && nums[left] == nums[left+1]) left++;
                        // 找到答案时收缩
                        right--;
                        left++;
                    }
                }
            }
        }
        return res;
    }
};

四、字符串

  • s.insert(startIdx, s)
  • s.replace(startIdx, s)
  • reverse(s.begin(), s.end())
  • s.erase(iterator pos)
  • s.erase(iterator fisrt, iterator last)

LeetCode151 | 反转字符串中的单词

stringstream解法:

class Solution {
public:
    string reverseWords(string s) {
        stringstream ss(s);
        string word;
        vector<string> words;
        while (ss >> word) {
            words.push_back(word);
        }
        reverse(words.begin(), words.end());

        ostringstream oss;
        for (int i = 0; i < words.size(); i++) {
            if (i != 0) oss << ' ';
            oss << words[i];
        }
        return oss.str();
    }
};

LeetCode239 | 滑动窗口最大值

class Solution {
    //维护有可能成为窗口里最大值的元素,同时保证队列里的元素数值是由大到小的。
    // pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
    // push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止
    class MyQueue {
    public:
        deque<int> que;
        void pop(int value) {
            if (!que.empty() && value == que.front()) {
                que.pop_front();
            }
        }
        void push(int value) {
            while (!que.emptya() && value > que.back()) {
                que.pop_back();
            }
            que.push_back(value);
        }
        int front() {
            return que.front();
        }
    };
    
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        MyQueue que;
        vector<int> res;
        for (int i = 0; i < k; i++) {
            que.push(nums[i]);
        }
        res.push_back(que.front());
        for (int i = k; i < nums.size(); i++) {
            que.pop(nums[i-k]);
            que.push(nums[i]);
            res.push_back(que.front());
        }
        return res;
    }
};

LeetCode347 | 前 K 个高频元素

  • https://en.cppreference.com/w/cpp/container/priority_queue
  • 命名结构体实现仿函数比较,用于实现定制化priority_queque小顶堆
class Solution {
public:
    struct mycomparison{
        bool operator()(const pair<int, int> &lhs, const pair<int, int> &rhs) {
            return lhs.second > rhs.second;
        }
    };
    
    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> mp;
        for (int i = 0; i < nums.size(); i++) {
            mp[nums[i]]++;
        }

        // 小顶堆
        priority_queue<pair<int,int>, vector<pair<int,int>>, mycomparison> pri_que;

        for (unordered_map<int, int>::iterator it = mp.begin(); it != mp.end(); it++) {
            pri_que.push(*it);
            if (pri_que.size() > k){
                pri_que.pop();
            }
        }

        vector<int> res(k);
        for(int i = k-1; i >= 0; i--) {
            res[i] = pri_que.top().first;
            pri_que.pop();
        }

        return res;
    }
};

LeetCode | 模板

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值