码迷,mamicode.com
首页 > 其他好文 > 详细

7.数据结构-stl

时间:2021-03-30 13:58:40      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:包含   解决问题   不同   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;
}

 

7.数据结构-stl

标签:包含   解决问题   不同   new   等于   这一   shm   计算   sub   

原文地址:https://www.cnblogs.com/go-ahead-wsg/p/14594466.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有
迷上了代码!