C++算法之数据结构一

妙用数据结构

主要介绍如下:
在这里插入图片描述

1.C++ STL

  1. Sequence Containers:维持顺序的容器。
    (a). vector:动态数组,是我们最常使用的数据结构之一,用于 O(1) 的随机读取。因为大部分算法的时间复杂度都会大于 O(n),因此我们经常新建 vector 来存储各种数据或中间变量。因为在尾部增删的复杂度是 O(1),我们也可以把它当作 stack 来用。
    (b). list:双向链表,也可以当作 stack 和 queue 来使用。很少用到这个数据结构。一个例外是经典的 LRU 问题,我们需要利用链表的特性来解决,我们在后文会遇到这个问题。
    ©. deque:双端队列,这是一个非常强大的数据结构,既支持 O(1) 随机读取,又支持 O(1)时间的头部增删和尾部增删,不过有一定的额外开销。
    (d). array:固定大小的数组,一般在刷题时我们不使用。
    (e). forward_list:单向链表,一般在刷题时我们不使用。

  2. Container Adaptors:基于其它容器实现的数据结构。
    (a). stack:后入先出(LIFO)的数据结构,默认基于 deque 实现。stack 常用于深度优先搜索、一些字符串匹配问题以及单调栈问题。
    (b). queue:先入先出(FIFO)的数据结构,默认基于 deque 实现。queue 常用于广度优先搜索。
    ©. priority_queue:最大值先出的数据结构,默认基于vector实现堆结构。它可以在O(n log n)的时间排序数组,O(log n) 的时间插入任意值,O(1) 的时间获得最大值,O(log n) 的时间删除最大值。priority_queue 常用于维护数据结构并快速获取最大或最小值。

  3. Associative Containers:实现了排好序的数据结构。
    (a). set:有序集合,元素不可重复,底层实现默认为红黑树,即一种特殊的二叉查找树(BST)。它可以在 O(n log n) 的时间排序数组,O(log n) 的时间插入、删除、查找任意值,O(log n) 的时间获得最小或最大值。这里注意,set 和 priority_queue 都可以用于维护数据结构并快速获取最大最小值,但是它们的时间复杂度和功能略有区别,如priority_queue 默认不支持删除任意值,而 set 获得最大或最小值的时间复杂度略高,具体使用哪个根据需求而定。
    (b). multiset:支持重复元素的 set。
    ©. map:有序映射或有序表,在 set 的基础上加上映射关系,可以对每个元素 key 存一个值 value。
    (d). multimap:支持重复元素的 map。

  4. Unordered Associative Containers:对每个 Associative Containers 实现了哈希版本。
    (a). unordered_set:哈希集合,可以在 O(1) 的时间快速插入、查找、删除元素,常用于快速的查询一个元素是否在这个容器内。
    (b). unordered_multiset:支持重复元素的 unordered_set。
    ©. unordered_map:哈希映射或哈希表,在 unordered_set 的基础上加上映射关系,可以对每一个元素 key 存一个值 value。在某些情况下,如果 key 的范围已知且较小,我们也可以用 vector 代替 unordered_map,用位置表示 key,用每个位置的值表示 value。
    (d). unordered_multimap:支持重复元素的 unordered_map。因为这并不是一本讲解 C++ 原理的书,更多的 STL 细节请读者自行搜索。只有理解了这些数据结构的原理和使用方法,才能够更加游刃有余地解决算法和数据结构问题。

11.2 数组

题目一:给定一个长度为 n 的数组,其中包含范围为 1 到 n 的整数,有些整数重复了多次,有些整数没有出现,求 1 到 n 中没有出现过的整数。
在这里插入图片描述
题解:利用数组这种数据结构建立 n 个桶,把所有重复出现的位置进行标记,然后再遍历一遍数组,即可找到没有出现过的数字。进一步地,我们可以直接对原数组进行标记:把重复出现的数字在原数组出现的位置设为负数,最后仍然为正数的位置即为没有出现过的数。

vector<int>findDisappearedNumbers(vector<int>&nums)
{
    vector<int>ans;
    for(const int & num:nums)
    {
        int pos = abs(num)-1;
        if(nums[pos]>0){
            nums[pos] = -nums[pos];
        }
    }
    for(int i=0;i<num.size();i++)
    {
        if(nums[i]>0){
            ans.push_back(i+1);
        }
    }
    return ans;
}

题目二:给定一个 n × n 的矩阵,求它顺时针旋转 90 度的结果,且必须在原矩阵上修改(in-place)。怎样能够尽量不创建额外储存空间呢?
在这里插入图片描述
每次只考虑四个间隔 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=0;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]=matrix[n-i][n-j];
            matrix[n-i][n-j]=temp;
        }
    }
}   

题目三:给定一个二维矩阵,已知每行和每列都是增序的,尝试设计一个快速搜索一个数字是否在矩阵中存在的算法。
在这里插入图片描述
可以从右上角开始查找,若当前值大于待搜索值,我们向左移动一位;若当前值小于待搜索值,我们向下移动一位。如果最终移动到左下角时仍不等于待搜 索值,则说明待搜索值不存在于矩阵中。若要从其他位置开始搜索,需要改变移动方向。。

bool searchMatrix(vector<vector<int>>&matrix,int target)
{
    int m=matrix.size();
    if(m==0){
        return false;
    }
    int n matrix[0].size();
    int i=0,j=n-1;
    while(i < m && j  >=0)
    {
        if(matrix[i][j] == target){
            return true;
        }
        else if(matrix[i][j] > target){
            --j;
        }
        else{
            ++i;
        }
    }
    return false;
}  

题目四:给定一个含有 0 到 n 整数的数组,每个整数只出现一次,求这个数组最多可以分割成多少个子数组,使得对每个子数组进行增序排序后,原数组也是增序的。
在这里插入图片描述
从左往右遍历,同时记录当前的最大值,每当当前最大值等于数组位置时,我们可以多一次分割。
为什么可以通过这个算法解决问题呢?如果当前最大值大于数组位置,则说明右边一定有小于数组位置的数字,需要把它也加入待排序的子数组;又因为数组只包含不重复的 0 到 n,所以当前最大值一定不会小于数组位置。所以每当当前最大值等于数组位置时,假设为 p,我们可以成功完成一次分割,并且其与上一次分割位置 q 之间的值一定是 q + 1 到 p 的所有数字。

int maxChunksToSorted(vector<int>&arr)
{
    int chunks=0;
    int cur_max=0;
    for(int i=0;i<arr.size();++i)
    {
        curr_max=max(curr_max,arr[i]);
        if(curr_max==i)
        {
            ++chunks;
        }
    }
    return chunks;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明月醉窗台

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值