LeetCode热题 100

两数之和

暴力枚举

cpp 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        for(int i=0; i<nums.size()-1; i++){
            for(int j=i+1; j<nums.size(); j++){
                if(target == nums[i] + nums[j]){
                    return {i,j};
                }
            }
        }
        return {};
    }
};

哈希表

方法一的时间复杂度较高的原因是寻找target-x的时间复杂度过高。

因此,我们需要一种更优秀的方法,能够快速寻找数组中是否存在目标元素。如果存在,我们需要找到它的索引。

使用哈希表,可以将寻找target-x的时间复杂度从O(N)降到O(1)。

cpp 复制代码
class Solution{
public:
	vector<int> twoSum(vector<int>& nums, int target){
		unordered_map<int, int> hashtable;
		for(int i=0; i<nums.size(); i++){
			auto it = hashtable.find(target-nums[i]);
			if(it != hashtable.end()){
				return {it->second,i};
			}
			hashtable[nums[i]] = i;
		}
		return {];
	}
};

字母异位词分组

给一个字符串数组,请你将字母异位词组合在一起。

排序

由于互为字母异位词的两个字符串包含的字母相同。

cpp 复制代码
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> strMap;
        for(string& str:strs){
        	string key = str;
        	sort(key.begin(),key.end());
        	mp[key].emplace_back(str);
        }
        
        vector<vector<string>> ans;
        for(auto it=mp.begin(); it!=mp.end();it++){
        	ans.empalce_back(it->second);
        }
        return ans;
    }
};

最长连续序列

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

请设计并实现时间复杂度为O(n)的算法解决此问题。

哈希表

我们考虑枚举数组中的每个数x,考虑以其为起点,不断尝试匹配x+1,x+2,...是否存在,假设最长匹配到了x+y,那么以x为起点的最长连续序列即为x,x+1,...,x+y,其长度为y+1,我们不断枚举并更新答案即可。

对于匹配的过程,暴力的方法是O(n)遍历数组去看是否存在这个数,但其实更高效的方法是使用一个哈希表存储数组中的数,这样查看一个数是否存在即能优化至O(1)的时间复杂度。

仅仅是这样,我们的算法时间复杂度最坏情况下还是会达到O(n²)(外存需要枚举O(n)个数,内存需要暴力匹配O(n)次),无法满足题目的要求。

仔细分析这个过程,会发现其中执行了很多不必要的枚举,如果已知有一个从x开始的连续序列,而我们却从x+1开始,那么得到的结果肯定不会优于枚举x为起点的答案,因此我们在外层循环的时候碰到这种情况跳过即可。

cpp 复制代码
class Solution {
public:
    int longestConsecutive(vector<int>& nums) {
        unordered_set<int> hashTable;
        for(const int&num:nums){
        	hashTable.insert(num);
        }
		
		int maxLength = 0;
		for(const int&num:hashTable){
			if(!hashTable.count(num-1)){
				int currentNum = num;
				int currentLen = 1;
				
				while(hashTable.count(currentNum+1)){
					currentNum++;
					currentLen++;
				}
				maxLength = max(maxLength, currentLen);
			}
		}
		return maxLength;
    }
};

移动零

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

双指针

使用双指针,左指针指向当前已处理好的序列的尾部,右指针指向待处理序列的头部。

右指针不断向右移动,每次右指针指向非零数,则将左右指针对应的数交换,同时左指针右移。

无重复字符的最长子串

cpp 复制代码
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
    	int n = s.size();
    	if(n <= 1){
    		return n;
    	}

		int l = 0;
		int r = 1;
		unordered_set<char> charMap;
		charMap.insert(s[l]);
		int maxLen = 1;

		while(r < n){
			if(charMap.count(s[r])){
				maxLen = max(maxLen, r-l);
				charMap.remove(s[l]);
				l++;
			}else{
				charMap.insert(s[r]);
				r++;
			}
		}
		maxLen = max(maxLen,r-l);
		return maxLen;
    }
};

和为k的子数组

枚举

cpp 复制代码
class Solution {
public:
    int subarraySum(vector<int>& nums, int k) {
        int count = 0;
        for(int i=0; i<nums.size(); i++){
            int sum = 0;
            for(int j=i; j>=0; j--){
                sum += nums[j];
                if(sum == k){
                    count++;
                }
            }
        }
        return count;
    }
};

前缀和+哈希表优化

方法一的瓶颈在于对每个i,我们需要枚举所有的j来判断是否符合条件。

定义pre[i]为0,...,i的和

cpp 复制代码
pre[i] = pre[i-1] + nums[i]

j,...i\]子数组和为k: ```cpp pre[i] - pre[j-1] == k pre[j-1] = pre[i] - k; ``` ### 最大子数组和 找出一个具有最大和的连续子数组,返回最大和(至少包含一个元素)。 **动态规划** 用f(i)代表以第i个数结尾的连续子数组的最大和。 可以考虑nums\[i\]单独成为一段还是加入f(i-1)对应的一段。 f(i) = max{nums\[i\],f(i-1)+nums\[i\]} 不难给出一个时间复杂度、空间复杂度均为O(n)的实现,即用一个f数组来保存f(i)的值,用一个循环求出所有f(i)。 f(i)只和f(i-1)有关,所以可以用一个变量维护即可,类似于滚动数组。 ```cpp class Solution { public: int maxSubArray(vector& nums) { int n = nums.size(); if(n == 1){ return nums[0]; } int sum = nums[0]; int maxSum = sum; for(int i=1; i> merge(vector>& intervals) { vector> merged; sort(intervals.begin(), intervals.end()); for(int i=0; i& nums,int start,int end){ while(start < end){ swap(nums[start++],nums[end--]); } } void rotate(vector& nums, int k) { int n = nums.size(); k = k%n; reverseNum(nums,0,n-1); reverseNum(nums,0,k-1); reverseNum(nums,k,n-1); } }; ``` **使用额外的数组** 可以使用额外的数组来将每个元素放在正确的位置。 ### 除自身以外数组的乘积 answer\[i\]等于nums中除nums\[i\]之外其余各元素的乘积。 **左右乘积列表** 利用索引左侧所有数字的乘积和右侧所有数字的乘积(即前缀与后缀)相乘得到答案。 1. 初始化两个空数组L和R。对于给定索引i,L\[i\]代表的是i左侧所有数字的乘积,R\[i\]代表的是右侧所有数字的乘积。 2. 我们需要用两个循环来填充L和R数组的值。对于数组L,L\[0\]应该是1。L\[i\] = L\[i-1\]\*nums\[i\]; 3. R\[Length-1\] = 1。R\[i\] = R\[i+1\]\*nums\[i\]。 ```cpp class Solution { public: vector productExceptSelf(vector& nums) { int n = nums.size(); vector L(n,1); vector R(n,1); vector res(n); for(int i=1; i=0; i--){ R[i] = R[i+1]*nums[i+1]; } for(int i=0;i

cpp 复制代码
class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(n,1);
        for(int i=1; i<n; i++){
            res[i] *= res[i-1]*nums[i-1];
        }
        int R = 1;
        for(int i=n-1; i>=0; i--){
            res[i] = res[i]*R;
            R *= nums[i];
        }
        return res;
    }
};
相关推荐
黄昏ivi1 小时前
电力系统最小惯性常数解析
算法
独家回忆3641 小时前
每日算法-250425
算法
烁3471 小时前
每日一题(小白)模拟娱乐篇33
java·开发语言·算法
Demons_kirit2 小时前
LeetCode 2799、2840题解
算法·leetcode·职场和发展
软行2 小时前
LeetCode 每日一题 2845. 统计趣味子数组的数目
数据结构·c++·算法·leetcode
永远在Debug的小殿下2 小时前
查找函数【C++】
数据结构·算法
我想进大厂2 小时前
图论---染色法(判断是否为二分图)
数据结构·c++·算法·深度优先·图论
油泼辣子多加2 小时前
【风控】稳定性指标PSI
人工智能·算法·金融
Yhame.3 小时前
【使用层次序列构建二叉树(数据结构C)】
c语言·开发语言·数据结构
雾月553 小时前
LeetCode 1292 元素和小于等于阈值的正方形的最大边长
java·数据结构·算法·leetcode·职场和发展