LeetCode 热题100 刷题笔记

发布于:2024-03-01 ⋅ 阅读:(60) ⋅ 点赞:(0)

一:哈希表

一般哈希表都是用来快速判断一个元素是否出现集合里。

直白来讲其实数组就是一张哈希表,哈希表中关键码就是数组的索引下标,然后通过下标直接访问数组中的元素。

1.两数之和

题目链接:. - 力扣(LeetCode)

// 哈希表
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        std::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 {iter->second, i};
            }
            else{
                map.insert(pair<int, int>(nums[i], i));
            }
        }
        return {};
    }
};

本题其实有四个重点:

  • 为什么会想到用哈希表

本题呢,我就需要一个集合来存放我们遍历过的元素,然后在遍历数组的时候去询问这个集合,某元素是否遍历过,也就是 是否出现在这个集合。那么我们就应该想到使用哈希法了。

  • 哈希表为什么用map

这道题目中并不需要key有序,选择std::unordered_map 效率更高!

  • 本题map是用来存什么的

map目的用来存放我们访问过的元素,因为遍历数组的时候,需要记录我们之前遍历过哪些元素和对应的下标,这样才能找到与当前元素相匹配的(也就是相加等于target)

  • map中的key和value用来存什么的

这道题 我们需要 给出一个元素,判断这个元素是否出现过,如果出现过,返回这个元素的下标。

那么判断元素是否出现,这个元素就要作为key,所以数组中的元素作为key,有key对应的就是value,value用来存下标。

所以 map中的存储结构为 {key:数据元素,value:数组元素对应的下标}。

 2. 字母异位词分组

class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        map<string, int> myhash;
        vector<vector<string>> myAns;
        int cint =0;
        for(int i = 0; i<strs.size(); i++){
            string temString = strs[i];
            sort(temString.begin(), temString.end());
            auto iter = myhash.find(temString);
            if(iter == myhash.end()){
                myAns.push_back(vector<string>());
                myAns.back().push_back(strs[i]);
                myhash[temString]=cint;
                cint ++;
            }
            else{
                int index = myhash[temString];
                myAns[index].push_back(strs[i]);
            }
        }
        return myAns;
    }
};

 3.最长连续序列

class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> myset;
        for(const int& num : nums){
            myset.insert(num);
        }

        int longestStreak = 0;

        for(const int& num : myset){
            if(myset.count(num-1) == 0){
                int currentNum = num;
                int currentStreak = 1;

                while(myset.count(currentNum +1)){
                    currentNum = currentNum + 1;
                    currentStreak = currentStreak + 1; 
                }

                longestStreak = max(longestStreak, currentStreak);
            }
        }

        return longestStreak;
    }
};

 二:双指针

1.移动零

// 使用双指针,左指针指向当前已经处理好的序列的尾部,右指针指向待处理序列的头部。
// 右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        int n = nums.size(), left = 0, right = 0;
        while (right < n) {
            if (nums[right]) {
                swap(nums[left], nums[right]);
                left++;
            }
            right++;
        }
    }
};

思路:通过右指针来遍历数组,左指针始终指向处理好的序列的尾部。

 2.盛最多水的容器

/* 算法流程:
1.初始化: 双指针 i , j 分列水槽左右两端;
2.循环收窄: 直至双指针相遇时跳出;
    a.更新面积最大值 res;
    b.选定两板高度中的短板,向中间收窄一格;
3.返回值: 返回面积最大值 res 即可; */

class Solution {
public:
    int maxArea(vector<int>& height) {
        int i = 0, j = height.size() - 1, res = 0;
        while(i < j) {
            res = height[i] < height[j] ? 
                max(res, (j - i) * height[i++]): 
                max(res, (j - i) * height[j--]); 
        }
        return res;
    }
};

3.三数之和

class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        vector<vector<int>> result;
        sort(nums.begin(), nums.end());
        // 找出a + b + c = 0
        // a = nums[i], b = nums[left], c = nums[right]
        for (int i = 0; i < nums.size(); i++) {
            // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了
            if (nums[i] > 0) {
                return result;
            }
            // 错误去重a方法,将会漏掉-1,-1,2 这种情况
            /*
            if (nums[i] == nums[i + 1]) {
                continue;
            }
            */
            // 正确去重a方法
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            int left = i + 1;
            int right = nums.size() - 1;
            while (right > left) {
                // 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
                /*
                while (right > left && nums[right] == nums[right - 1]) right--;
                while (right > left && nums[left] == nums[left + 1]) left++;
                */
                if (nums[i] + nums[left] + nums[right] > 0) right--;
                else if (nums[i] + nums[left] + nums[right] < 0) left++;
                else {
                    result.push_back(vector<int>{nums[i], nums[left], nums[right]});
                    // 去重逻辑应该放在找到一个三元组之后,对b 和 c去重
                    while (right > left && nums[right] == nums[right - 1]) right--;
                    while (right > left && nums[left] == nums[left + 1]) left++;

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

        }
        return result;
    }
};

 思路:

首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。

依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i],b = nums[left],c = nums[right]。

接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。

如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。

4.接雨水 

class Solution {
public:
    int trap(vector<int>& height) {
        int ans = 0;
        int left = 0, right = height.size() - 1;
        int leftMax = 0, rightMax = 0;
        while (left < right) {
            leftMax = max(leftMax, height[left]);
            rightMax = max(rightMax, height[right]);
            if (height[left] < height[right]) {
                ans += leftMax - height[left];
                ++left;
            } else {
                ans += rightMax - height[right];
                --right;
            }
        }
        return ans;
    }
};
本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到