Index LeetCode
简单罗列下c++ stl里数据结构:vector,hash(unordered_set\map),queue,deque,priority_queue,stack,list
主要会用以上数据结构的成员函数有:empty(),count(),insert(),push(),push_back(),front(),top(),back(),push_front(),pop(),pop_back(),pop_front(),erase(),iterator
C++ STL
在刷题时,我们几乎一定会用到各种数据结构来辅助我们解决问题,因此我们必须熟悉各种数据结构的特点。C++ STL提供的数据结构包括(实际底层细节可能因编译器而异):
Sequence Containers:维持顺序的容器。
- vector:动态数组,是我们最常使用的数据结构之一,用于O(1)的随机读取。因为大部分算法的时间复杂度都会大于O(n),因此我们经常新建vector来存储各种数据或中间变量。因为在尾部增删的复杂度是O(1),我们也可以把它当作stack来用。
- list:双向链表,也可以当作stack和queue来使用,由于LeetCode的题目多用Node表示链表,且链表不支持快速随机读取,因此用这个数据结构。一个例外是经典的LRU问题,我们需要利用链表的特性来解决,我们在后文会遇到这个问题。
- deque:双端队列,这是一个非常强大的数据结构,既支持O(1)随机读取,又支持O(1)时间的头部增删和尾部增删,不过有一定的额外开销,
- array:固定大小的数组,一般在刷题时我们不使用。
- forward_list:单向链表,一般在刷题时我们不使用。
Container Adaptors:基于其他容器实现的数据结构。
- stack:先入后出(LIFO)的数据结构,默认基于deque实现。stack常用于深度优先搜索、一些字符串匹配问题以及单调栈问题。
- queue:先入先出(FIFO)的数据结构,默认基于deque实现。queue常用于广度优先搜索。
- priority_queue:默认基于vector实现堆结构。它可以在O(nlogn)的时间排序数组,O(logn)的时间删除最大值。priority_queue常用于维护数据结构并快速获取最大或最小值。
- 大顶堆:最大值先出的数据结构,priority_queue<int> min;
- 小顶堆:最小值先出的数据结构,priority_queue<int, vector<int>, greater<int>> max;
Associative Containers:实现了排好序的数据结构。
- set:有序集合,元素不可重复,底层实现默认为红黑树,即一种特殊的二叉查找树(BST)。它可以在O(nlogn)的时间排序数组,O(logn)的时间插入、删除、查找任意值,O(logn)的时间获得最小或最大值。这里注意,set和priority_queue都可以用于维护数据结构并快速获取最小或最大值。但是它们的时间复杂度和功能略有不同。
- multiset:支持重复元素的set。
- map:有序映射或有序表,在set的基础上加上了映射关系,可以对每个元素key存一个值value。
- multimap:支持重复元素的map。
Unordered Associative Containers:对每个Associative Containers实现了哈希版本。
- unorded_set:哈希集合,可以在O(1)的时间快速插入、查找、删除元素,常用于快速查询一个元素是否在这个容器内。
- unordered_multiset:支持重复元素的unordered_set。
- unordered_map:哈希映射或哈希表,在unordered_set的基础上加上映射关系,可以对每一个元素key存一个值value。在某些情况下,如果key的范围一致且较小,我们也可以用vector代替unordered_map,用位置表示key,用每个位置的值表示value。
- unordered_multimap:支持重复元素的unordered_map。
BM50.两数之和(1.两数之和)[easy]
输入:nums=[2,7,11,15],target=9
输出:[0,1]
hash
vector<int> twoSum(vector<int>& nums, int target){
unordered_map<int, int> hash;
for(int i=0; i<nums.size(); i++){
if(hash.count(nums[i]))
return {i, hash[nums[i]]};
else
hash[target-nums[i]]=i;
}
return {-1, -1};
}
BM51.数组中出现次数超过一半的数字(剑指offer39 数组中出现次数超过一半的数字)[easy]
输入:[1,2,3,2,2,2,5,4,2]
输出:2
hash
int majorityElement(vector<int>& nums){
unordered_map<int, int> hash;
for(int i=0; i<nums.size(); i++){
hash[nums[i]]++;
if(hash[nums[i]] > nums.size()/2)
return nums[i];
}
return -1;
}
第二种思路:快排
因为有个值超过半数,排序后,中位数就是你要的那个值,时间复杂度 $O(nlog(n))不稳定, $ 空间复杂度 O(n)
BM52.数组中只出现一次的两个数字()[easy]
说明:一个长度为n的数组,除了两个数字只出现一次,其余均出现2次
hash
vector<int> findNumsAppearonce(vector<int>& nums){
vector<int> res;
unordered_map<int, int> hash;
for(int i=0; i<nums.size();i++)
hash[nums[i]]++;
unordered_map<int, int>::iterator iter=hash.begin();
while(iter != hash.end()){
if(iter->second == 1) res.push_back(iter->first);
iter++;
}
if(res[0] > res[1]) return {res[1], res[0]}; //升序输出
return res;
}
BM53.缺失的第一个正整数(41.缺失的第一个正整数)[hard]
输入:nums=[3,4,-1,1], nums=[7,8,9,11,12]
输出:2, 1
说明:给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。
int firstMissingPositive(vector<int>& nums){
unordered_set<int> hash;
for(int n:nums) hash.insert(n);
for(int i=1; i<=nums.size(); i++){
if(!hash.count(i)) return i;
}
return nums.size()+1;
}
BM54.三数之和(15.三数之和)[medium]
输入:nums=[-1,0,1,2,-1,-4] 找出三个数a,b,c;使得a+b+c=0
输出:[[-1,-1,2],[-1,0,1]]
排序+ 双指针 +去重
vector<vector<int>> threeSum(vector<int>& nums){
vector<vector<int>> res;
if(nums.size() < 3) return res;
sort(nums.begin(), nums.end());
for(int i=0; i<nums.size()-2; i++){
if(nums[i]+nums[i+1]+nums[i+2] > 0) return res;
if(i>0 && nums[i]==nums[i-1]) continue; //去重
int left=i+1,right=nums.size()-1;
while(left < right){
if(num[left]+nums[right] == -nums[i]){
res.push_back({nums[i], nums[left], nums[right]});
while(left<right && nums[left]==nums[left+1]) left++;
while(left<right && nums[right]==nums[right-1]) right--;
left++;
right--;
}else if(nums[left]+nums[right] > -nums[i]){
right--;
}else{
left++;
}
}
}
return res;
}