博客园  :: 首页  :: 新随笔  :: 管理

5.哈希系列

Posted on 2021-03-29 21:43  wsg_blog  阅读(135)  评论(0编辑  收藏  举报

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:维持顺序的容器。
  1. vector:动态数组,是我们最常使用的数据结构之一,用于O(1)的随机读取。因为大部分算法的时间复杂度都会大于O(n),因此我们经常新建vector来存储各种数据或中间变量。因为在尾部增删的复杂度是O(1),我们也可以把它当作stack来用。
  2. list:双向链表,也可以当作stack和queue来使用,由于LeetCode的题目多用Node表示链表,且链表不支持快速随机读取,因此用这个数据结构。一个例外是经典的LRU问题,我们需要利用链表的特性来解决,我们在后文会遇到这个问题。
  3. deque:双端队列,这是一个非常强大的数据结构,既支持O(1)随机读取,又支持O(1)时间的头部增删和尾部增删,不过有一定的额外开销,
  4. array:固定大小的数组,一般在刷题时我们不使用。
  5. forward_list:单向链表,一般在刷题时我们不使用。
Container Adaptors:基于其他容器实现的数据结构。
  1. stack:先入后出(LIFO)的数据结构,默认基于deque实现。stack常用于深度优先搜索、一些字符串匹配问题以及单调栈问题。
  2. queue:先入先出(FIFO)的数据结构,默认基于deque实现。queue常用于广度优先搜索。
  3. priority_queue:默认基于vector实现堆结构。它可以在O(nlogn)的时间排序数组,O(logn)的时间删除最大值。priority_queue常用于维护数据结构并快速获取最大或最小值。
    1. 大顶堆:最大值先出的数据结构,priority_queue<int> min;
    2. 小顶堆:最小值先出的数据结构,priority_queue<int, vector<int>, greater<int>> max;
Associative Containers:实现了排好序的数据结构。
  1. set:有序集合,元素不可重复,底层实现默认为红黑树,即一种特殊的二叉查找树(BST)。它可以在O(nlogn)的时间排序数组,O(logn)的时间插入、删除、查找任意值,O(logn)的时间获得最小或最大值。这里注意,set和priority_queue都可以用于维护数据结构并快速获取最小或最大值。但是它们的时间复杂度和功能略有不同。
  2. multiset:支持重复元素的set。
  3. map:有序映射或有序表,在set的基础上加上了映射关系,可以对每个元素key存一个值value。
  4. multimap:支持重复元素的map。
Unordered Associative Containers:对每个Associative Containers实现了哈希版本。
  1. unorded_set:哈希集合,可以在O(1)的时间快速插入、查找、删除元素,常用于快速查询一个元素是否在这个容器内。
  2. unordered_multiset:支持重复元素的unordered_set。
  3. unordered_map:哈希映射或哈希表,在unordered_set的基础上加上映射关系,可以对每一个元素key存一个值value。在某些情况下,如果key的范围一致且较小,我们也可以用vector代替unordered_map,用位置表示key,用每个位置的值表示value。
  4. 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;
}