Leetcode热题100

LeetCode热题100

一.哈希表专题

题目1:两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。
  • 暴力解
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int len=nums.size();

        for(int i=0;i<len-1;i++)
        {
            for(int j=i+1;j<len;j++)
            {
                if(nums[i]+nums[j]==target)
                {
                    return {i,j};
                }
            }
        }
        return {};
    }
};
  • 哈希表
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 it=map.find(target-nums[i]);
            if(it!=map.end())
            {
                return {i,it->second};
            }
            //存储数据
            map[nums[i]]=i;
        }
        return {};
    }
};
  • 知识点补充:unordered_map用法
要使用 unordered_map 容器,需要包含头文件 <unordered_map>,然后可以按照以下步骤进行操作:

声明和初始化 unordered_map:
#include <unordered_map>
unordered_map<int, int> hash; // 声明一个空的 unordered_map,键值为 int 类型

插入元素:
hash[1] = 10; // 插入 key 为 1,值为 10 的元素
hash[2] = 20; // 插入 key 为 2,值为 20 的元素

访问元素:
int value = hash[1]; // 访问 key 为 1 的元素的值

查找元素:
auto it = hash.find(2); // 查找 key 为 2 的元素,返回指向该元素的迭代器
if (it != hash.end()) {
    int value = it->second; // 获取 key 为 2 的元素的值
}

删除元素:
hash.erase(1); // 删除 key 为 1 的元素

遍历 unordered_map:
for (auto& kv : hash) {
    int key = kv.first; // 获取遍历到的键
    int value = kv.second; // 获取遍历到的值
    cout << "Key: " << key << ", Value: " << value << endl;
}
通过以上操作,你可以使用 unordered_map 实现哈希表的功能,快速地查找、插入和删除键值对。unordered_map 是 C++ 标准库中常用的容器之一,可以在解决各种问题时发挥重要作用。

题目2:字母异位词分组

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。

字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
  • 哈希表
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string,vector<string>>map;

        for(int i=0;i<strs.size();i++)
        {
            string str=strs[i];
            //将每个字符排序
            sort(str.begin(),str.end());

            //向vector的末尾插入元素
            map[str].emplace_back(strs[i]);
        }

        vector<vector<string>>ans;

        for(auto it =map.begin();it!=map.end();it++)
        {
            ans.emplace_back(it->second);
        }

        return ans;
    }
};
  • 补充知识:

emplace_back是在向vector中添加元素时的一种简便方法,它通过构造新元素并将其插入到vector的末尾来实现。在给定的代码中,mp[key]是一个vector,emplace_back用于在mp[key]中添加当前字符串str。

通过emplace_back将字符串str添加到对应的vector中,可以将具有相同键的字符串分组在一起。这样可以方便地将同一组异位词放在同一个vector中,并最终将这些vector放入另一个vector中以表示所有分组的异位词。

题目3:最长连续序列

给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
  • 暴力解
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        if(nums.size()==0)
        {
            return 0;
        }
        if(nums.size()==1)
        {
            return 1;
        }
        int max_size=1;
        int count=1;
        //排序
        sort(nums.begin(),nums.end());
        //想要时间复杂度是O(n),就必须只是遍历一次
        for(int i=1;i<nums.size();i++)
        {
            
            if(nums[i]-nums[i-1]==1)
            {
                //连续
                
                count++;
                if(max_size<count)
                {
                    max_size=count;
                }
            }else if(nums[i]-nums[i-1]==0)
            {
                //不计入连续长度
                continue;
            }else
            {
                
                //不连续
                count=1;
            }

        }
        
        return max_size;
    }
};
  • 哈希表
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> Set;
        for (const int& num : nums) {
            Set.insert(num);
        }

        int longestStreak = 0;

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

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

                longestStreak = max(longestStreak, currentStreak);
            }
        }

        return longestStreak;           
    }
};

  • 知识点补充 :unordered_set的用法
C++中的unordered_set是一种无序的关联容器,它存储唯一的元素,元素被存储在一个散列表中,因此元素的存储顺序是不确定的。以下是unordered_set的用法示例:

#include <iostream>
#include <unordered_set>

int main() {
    // 创建一个unordered_set
    std::unordered_set<int> mySet;

    // 插入元素
    mySet.insert(1);
    mySet.insert(2);
    mySet.insert(3);

    // 遍历元素
    for (auto it = mySet.begin(); it != mySet.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    // 检查元素是否存在
    if (mySet.find(2) != mySet.end()) {
        std::cout << "Element 2 exists in the set" << std::endl;
    }

    // 删除元素
    mySet.erase(2);
	
	//查询键值元素的数量
	mySet.count(2);//查找key=2出现的次数

    // 再次遍历元素
    for (auto it = mySet.begin(); it != mySet.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    return 0;
}
在上面的示例中,我们创建了一个unordered_set存储整数,插入了几个元素并遍历了它们。我们还展示了如何检查元素是否存在并删除元素。请注意,unordered_set中的元素是唯一的,因此不会重复存储相同的元素。

二.双指针专题

题目1:移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。
  • 暴力解
class Solution {
public:
    void moveZeroes(vector<int>& nums) {
        //双指针
        int left=0;
        int right=0;

        while(right<nums.size())
        {
            if(nums[right])
            {
                //找到不为零的数,放再前面
                //swap(nums[left],nums[right]);
                int temp=nums[right];
                nums[right]=0;
                nums[left]=temp;
                left++;
            }
            right++;
        }
    }
};

题目2:盛最多水的容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。
  • 双指针
class Solution {
public:
    int maxArea(vector<int>& height) {
       int left=0;
       int right=height.size()-1;
       int maxarea=0;
       while(left<right)
       {
            int wide=right-left;
            int high=min(height[left],height[right]);
            int area=wide*high;
            maxarea=max(maxarea,area);

            if(height[left]<height[right])
            {
                left++;
            }else
            {
                right--;
            }

       }
       return maxarea;
    }
};
  • 算法详解

这段代码的作用是根据当前左右指针指向的高度大小,来决定移动哪一个指针。具体来说,如果左指针所指的高度小于右指针所指的高度,那么左指针向右移动一步;如果右指针所指的高度小于等于左指针所指的高度,那么右指针向左移动一步。通过这样的移动方式,我们可以不断地调整容器的宽度和高度,从而找到最大的容器面积。

这种移动方式的核心思想是:在每一步中,我们总是移动高度较小的指针,这样可以保留有可能产生更大面积的候选容器。最终在遍历完所有的可能情况后,我们就可以得到最大的容器面积。

这段代码块是双指针法解决容器盛水最多问题中的关键步骤之一,通过动态调整左右指针位置,不断地确认当前状态下的最优解

题目3:三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。
  • 官方题解(较为复杂,不好想)
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        vector<vector<int>> ans;
        // 枚举 a
        for (int first = 0; first < n; ++first) {
            // 需要和上一次枚举的数不相同
            if (first > 0 && nums[first] == nums[first - 1]) {
                continue;
            }
            // c 对应的指针初始指向数组的最右端
            int third = n - 1;
            int target = -nums[first];
            // 枚举 b
            for (int second = first + 1; second < n; ++second) {
                // 需要和上一次枚举的数不相同
                if (second > first + 1 && nums[second] == nums[second - 1]) {
                    continue;
                }
                // 需要保证 b 的指针在 c 的指针的左侧
                while (second < third && nums[second] + nums[third] > target) {
                    --third;
                }
                // 如果指针重合,随着 b 后续的增加
                // 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
                if (second == third) {
                    break;
                }
                if (nums[second] + nums[third] == target) {
                    ans.push_back({nums[first], nums[second], nums[third]});
                }
            }
        }
        return ans;
    }
};

三.滑动窗口

题目1:无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。
  • 官方题解(待研究)
class Solution {
public:
   int lengthOfLongestSubstring(string s) {
       // 哈希集合,记录每个字符是否出现过
       unordered_set<char> occ;
       int n = s.size();
       // 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
       int rk = -1, ans = 0;
       // 枚举左指针的位置,初始值隐性地表示为 -1
       for (int i = 0; i < n; ++i) {
           if (i != 0) {
               // 左指针向右移动一格,移除一个字符
               occ.erase(s[i - 1]);
           }
           while (rk + 1 < n && !occ.count(s[rk + 1])) {
               // 不断地移动右指针
               occ.insert(s[rk + 1]);
               ++rk;
           }
           // 第 i 到 rk 个字符是一个极长的无重复字符子串
           ans = max(ans, rk - i + 1);
       }
       return ans;
   }
};
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值