标签:包含 解决问题 不同 new 等于 这一 shm 计算 sub
/* 总结:stl里数据结构,如hash(unordered_set\map),queue,deque,priority_queue,stack 主要会用以上数据结构的成员函数,empty(),count(),insert(),push(),push_back(),front(),top(),back(),push_front() pop(),pop_back(),pop_front()等 */ //c++ stl简介 /* 在刷题时,我们几乎一定会用到各种数据结构来辅助我们解决问题,因此我们必须熟悉各种数据结构的特点。C++ STL提供的数据结构包括(实际 底层细节可能因编译器而异): 1.Sequence Containers:维持顺序的容器。 a.vector:动态数组,是我们最常使用的数据结构之一,用于O(1)的随机读取。因为大部分算法的时间复杂度都会大于O(n),因此我们经常 新建vector来存储各种数据或中间变量。因为在尾部增删的复杂度是O(1),我们也可以把它当作stack来用。 b.list:双向链表,也可以当作stack和queue来使用,由于LeetCode的题目多用Node表示链表,且链表不支持快速随机读取,因此用这个 数据结构。一个例外是经典的LRU问题,我们需要利用链表的特性来解决,我们在后文会遇到这个问题。 c.deque:双端队列,这是一个非常强大的数据结构,既支持O(1)随机读取,又支持O(1)时间的头部增删和尾部增删,不过有一定的额外开销, d.array:固定大小的数组,一般在刷题时我们不使用。 e.forward_list:单向链表,一般在刷题时我们不使用。 2.Container Adaptors:基于其他容器实现的数据结构。 a.stack:后入先出(LIFO)的数据结构,默认基于deque实现。stack常用于深度优先搜索、一些字符串匹配问题以及单调栈问题。 b.queue:先入先出(FIFO)的数据结构,默认基于deque实现。queue常用于广度优先搜索。 c.priority_queue:最大值先出的数据结构,默认基于vector实现堆结构。它可以在O(nlogn)的时间排序数组,O(logn)的时间删除最大值。 priority_queue常用于维护数据结构并快速获取最大或最小值。 3.Associative Containers:实现了排好序的数据结构。 a.set:有序集合,元素不可重复,底层实现默认为红黑树,即一种特殊的二叉查找树(BST)。它可以在O(nlogn)的时间排序数组,O(logn) 的时间插入、删除、查找任意值,O(logn)的时间获得最小或最大值。这里注意,set和priority_queue都可以用于维护数据结构并快速获取最小或 最大值。但是它们的时间复杂度和功能略有不同。 b.multiset:支持重复元素的set。 c.map:有序映射或有序表,在set的基础上加上了映射关系,可以对每个元素key存一个值value。 d.multimap:支持重复元素的map。 4.Unordered Associative Containers:对每个Associative Containers实现了哈希版本。 a.unorded_set:哈希集合,可以在O(1)的时间快速插入、查找、删除元素,常用于快速查询一个元素是否在这个容器内。 b.unordered_multiset:支持重复元素的unordered_set。 c.unordered_map:哈希映射或哈希表,在unordered_set的基础上加上映射关系,可以对每一个元素key存一个值value。在某些情况下, 如果key的范围一致且较小,我们也可以用vector代替unordered_map,用位置表示key,用每个位置的值表示value。 d.unordered_multimap:支持重复元素的unordered_map。 */ //448.找到所有数组中消失的数字 /* 题目描述:给定一个长度为n的数组,其中包含范围为1到n的整数,有些整数重复了多次,有些整数没有出现,求1到n中没有出现过的整数。 输入输出样例:输入是一个一维整数数组,输出也是一个一维整数数组,表示输入数组内没有出现过的数字。 Input:[4,3,2,7,8,2,3,1] Output:[5,6] 题解:把值转变为下标,通过正负号来标记是否出现过此数字,下标值为负代表出现过。 */ vector<int> findDisappearedNumbers(vector<int>& nums) { vector<int> result; //返回值 for(int num:nums){ //遍历 int pos=abs(num)-1; //通过值转变为下标 if(nums[pos]>0){ //去重 nums[pos] = -nums[pos]; //通过正负号来标记是否出现过 } } for(int i=0; i<nums.size(); i++){ if(nums[i]>0){ //未出现 result.push_back(i+1); } } return result; } //48.旋转图像 /* 题目描述:给定一个长度为n*n的矩阵,求它顺时针旋转90度的结果,且必须在原矩阵上修改(in-place) 。怎样能够尽量的不创建额外存储空间呢? 输入输出样例:输入输出都是一个二维整数矩阵。 Input: [[1,2,3], [4,5,6], [7,8,9]] Output: [[7,4,1], [8,5,2], [9,6,3]] 题解:每次只考虑四个间隔90度的位置,可以进行O(1)额外空间的旋转。 */ void rotate(vector<vector<int>>& matrix){ int temp = 0,n=matrix.size()-1; for(int i=0; i<= n/2; i++){ for(int j=i; j<n-i; j++){ temp=matrix[j][n-i]; matrix[j][n-i]=matrix[i][j]; matrix[i][j]=matrix[n-j][i]; matrix[n-j][i]=n[n-i][n-j]; matrix[n-i][n-j]= temp; } } } //240.搜索二维矩阵 /* 题目描述:给定一个二维矩阵,已知每行和每列都是增序的,尝试设计一个快速搜索一个数字是否在矩阵中存在的算法。 输入输出样例:输入是一个二维矩阵,和一个待搜索的整数。输出是一个布尔值,表示这个整数是否存在于矩阵中。 Input: matirx= [[ 1, 4, 7,11,15], [ 2, 5, 8,12,19], [ 3, 6, 9,16,22], [10,13,14,17,24], [18,21,23,26,30]], target=5 Output:true 题解:这道题有一个简单的技巧:我们可以从右上角(或者左下角)开始查找,若当前值大于待搜索值,我们向左移动一位;若当前值小于待搜索值,我们向下移动一位。 如果最终移动到左下角时仍不等于待搜索值,则说明待搜索值不存在于矩阵中。 */ bool searchMatrix(vector<vector<int>>& matrix, int target){ int m=matirx.size(); if(m <= 0){ return false; } int n=matirx[0].size(); int i=0,j=n-1; while(i<m && j>=0){ if(target == matrix[i][j]){ //选点(右上或左下)、二分查找方法 return true; }else if(target > matirx[i][j]){ j--; }else{ i++; } } return false; } //20.有效的括号 /* 题目描述:给定一个由左右原括号、花括号和方括号组成的字符串,求这个字符串是否合法。合法的定义是每一个类型的左括号都有一个右括号 一一对应,且括号内的字符串也满足此要求。 输入输出样例:输入是一个字符串,输出是一个布尔值,表示字符串是否合法。 Input:"{[]}()" Output:true 题解:括号是典型的使用栈来解决的问题。我们先从左往右遍历,每当遇到左括号便放入栈内,遇到右括号则判断其和栈顶的括号是否统一类型,是则从 栈内取出左括号,否则说明字符串不合法。 */ bool isValid(string s){ stack<char> parsed; for(int i=0; i<s.length(); i++){ if(s[i]==‘{‘ || s[i]==‘[‘ || s[i]==‘(‘){ parsed.push(s[i]); }else{ if(parsed.empty()){ return false; } char c=parsed.top(); if((s[i]==‘[‘ && c==‘]‘) || (s[i]==‘{‘ && c==‘}‘) || (s[i]==‘(‘ && c==‘)‘)){ parsed.pop(); }else{ return false; } } } return parsed.empty(); } //739.每日温度 /* 单调栈:通过维持栈内值的递增(递减)性,在整体O(n)的时间内处理需要大小比较的问题。 题目描述:给定每天的温度,求对于每一天需要等几天才可以等到更暖和的一天。如果该天之后不存在更暖和的天气,则记为0. 输入输出样例:输入是一个一维整数数组,输出是同样长度的整数数组,表示对于每天需要等待多少天。 Input:[73,74,74,71,69,72,76,73] Output:[1,1,4,2,1,1,0,0] 题解:我们可以维持一个单调递减的栈,表示每天的温度;为了方便计算天数差,我们这里存放位置(即日期)而非温度本身。我们从左向右遍历 温度数组,对于每个日期p,如果p的温度比栈顶存储位置q的温度高,则我们取出q,并记录q需要等待的天数为p-q;我们重复这一过程,直到p的温度 小于等于栈顶存储位置的温度(或空栈)时,我们将p插入栈顶,然后考虑下一天。在这个过程中,栈内数组永远保持单调栈递减避免了使用排序进行 比较。最后若栈内剩余一些日期,则说明他们之后都没有出现过更暖和的日期。 */ vector<int> dailyTemperatures(vector<int>& temperatures){ int n=tempperparetures.size(); vector<int> ans(n); stack<int> indices; for(int i=0; i<n; i++){ while(!indices.empty()){ int pre_index = indices.top(); if(temperatures[i] <= temperatures[pre_index]){ break; } indices.pop(); ans[pre_index]=i-pre_index; } indices.push(i); } } //23.合并k个升序链表 /* 题目描述:给定k个增序的链表,试将它们合成一条增序链表。 输入输出样例:输入是一个一维数组,每个位置存储链表的头节点;输出是一条链表。 Input: [1->4->5, 1->3->4, 2->6] Output:1->1->2->3->4->4->5->6 题解:本题可以有很多种解法,比如类似于归并排序进行两两合并。我们这里展示一个速度比较快的方法,即把所有的链表存储在一个优先队列中, 每次提取所有链表头部节点值最小的那个节点,直到所有链表都被提取完为止。注意因为Comp函数默认是对最大堆进行比较并维持递增关系,如果 我们想要获取最小的节点值,则我们需要实现一个最小堆,因此比较函数应该维持递减关系,所以operator()中返回时用大于号而不是小于号进行比较。 */ struct Comp{ bool operator() (ListNode* l1, ListNode* l2){ return l1->val > l2->val; } }; ListNode* mergeKLists(vector<ListNode*>& lists){ if(lists.empty()) return nullptr; priority_queue<ListNode*, vector<ListNode*>, Comp> q; for(ListNode* list:lists){ if(list){ q.push(list); } } ListNode* dummy = new ListNode(0), *cur=dummy; while(!q.empty()){ cur->next = q.top(); q.pop(); cur=cur->next; if(cur->next){ q.push(cur->next); } } return dummy->next; } //239.双端队列 /* 题目描述:给定一个整数数组和一个滑动窗口大小,求在这个窗口的滑动过程中,每个时刻其包含的最大值。 输入输出样例:输入是一个一维整数数组,和一个表示滑动窗口大小的整数;输出是一个一维整数数组,表示每个时刻的窗口内最大值。 Input:nums=[1,3,-1,-3,5,3,6,7], k=3 Output:[3,3,5,5,6,7] 题解:我们可以利用双端队列进行操作:每当向右移动时,把窗口左端的值从队列左端剔除,把队列右边小于窗口右端的值全部剔除。这样 双端队列的最左端永远是窗口内的最大值。另外,这道题也是单调栈的一种延伸:该双端队列利用从左到右递减来维持大小关系。 */ vector<int> maxSlidingWindow(vector<int>& nums, int k){ deque<int> dq; vector<int> ans; for(int i=0; i<nums.size(); i++){ if(!dp.empty() && dp.front() == i-k){ //窗口右移,剔除窗口左端的值 dp.pop_front(); } while(!dp.empty() && nums[dp.back()]<nums[i]){ //把队列右边小于窗口右端的值全部剔除 dp.pop_back(); } dp.push_back(i); if(i >= k-1){ ans.push_back(nums[dp.front()]); } } return ans; } //1.两数之和:哈希表实现 /* 题目描述:给定一个整数数组,已知有且只有两个数的和等于给定值,求这两个数的位置。 输入输出样例:输入一个一维整数数组和一个目标值,输出是一个大小为2的一维数组,表示满足条件的两个数字的位置。 Input: nums = [2,7,11,15], target=9 Output: [0,1] 在这个样例中,第0个位置的值2和第1个位置的值7的和为9。 题解:我们可以利用哈希表存储遍历过的值以及它们的位置,每次遍历到位置i的时候,查找哈希表里是否存在target-num[i],若存在 则说明这两个值的和为target。 */ vector<int> twoSum(vector<int>& nums, int target) { //键是数字,值是该数字在数组的位置 unordered_map<int, int> hash; //定义一个hash的数组 vector<int> ans; //返回值 for(int i=0; i<nums.size(); i++){ int num = nums[i]; auto pos = hash.find(target - num); if (pos == hash.end()){ hash[num] = i; //数组值作为key,下标作为value }else{ ans.push_back(pos->second); ans.push_back(i); break; } } return ans; } //128.最长子序列 /* 给定一个整数数组,求这个数组中的数字可以组成的最长连续序列有多长。 输入输出样例:输入是一个整数数组,输出是一个整数,表示连续序列的长度。 Input:[100,4,200,1,3,2] Output:4 在这个样例中,最长连续序列是[1,2,3,4]. 题解:我们可以把所有的数字放在一个哈希表,然后不断的从哈希表中任意取一个值,并删掉其之前之后的所有连续数字,然后更新目前的最长连续 序列长度。重复这一过程,我们就可以找到所有的连续数字序列。 */ int longestConsecutive(vector<int>& nums) { unordered_set<int> hash; for(const int & num:nums){ hash.insert(num); } int ans=0; while(!hash.empty()){ int cur=*(hash.begin()); hash.erase(cur); int next=cur+1,prev=cur-1; while(hash.count(next)){ hash.erase(next++); } while(hash.count(prev)){ hash.earse(prev--); } ans=max(ans, next-prev-1); } return ans; } //560.和为k的子数组 /* 题目描述:给定一个数组,寻找和为k的连续区间个数。 输入输出样例:输入一个一维整数数组和一个整数值k;输出一个整数,表示满足条件的连续区间个数。 Input: nums=[1,1,1],k=2 Output:2 在这个样例中,我们可以找到两个[1,1]连续区间满足条件。 题解:本题同样是利用前缀和,不同的是这里我们使用一个哈希表hashmap,其键是前缀和,而值是该前缀和出现的次数。在我们 遍历到位置i时,假设当前的前缀和是psum,那么hashmap[psum-k]即为以当前位置结尾、满足条件的区间个数。 */ int subarraySum(vector<int>& nums, int k){ int count=0, psum=0; unordered_map<int, int> hashmap; hashmap[0]=1; //初始化很重要 for(int i:nums){ psum += i; count += hashmap[psum-k]; ++hashmap[psum]; } return count; }
标签:包含 解决问题 不同 new 等于 这一 shm 计算 sub
原文地址:https://www.cnblogs.com/go-ahead-wsg/p/14594466.html