zoukankan      html  css  js  c++  java
  • 7.数据结构-stl

    总结:stl里数据结构,如hash(unordered_setmap),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;
    }
     



     类型
    1. 贪心算法
    2. 双指针 
    3. 二分查找    
    4. 排序算法    
    5. 二维数组的搜索
    6. 动态规划 
    7. 数据结构-stl 
    8. 字符串比较、匹配 
    9. 链表
    10. 二叉树
     
  • 相关阅读:
    L1-050 倒数第N个字符串 (15分)
    Oracle存储过程的疑难问题
    Linux的细节
    Linux字符设备和块设备的区别
    Shell变量
    游标的常用属性
    Oracle中Execute Immediate用法
    Oracle中的sqlerrm和sqlcode
    Oracle把一个表的数据复制到另一个表中
    Oracle的差异增量和累积增量
  • 原文地址:https://www.cnblogs.com/go-ahead-wsg/p/14594466.html
Copyright © 2011-2022 走看看