第 11 章 妙用数据结构

本文仅为个人刷题记录

448. 找到所有数组中消失的数字

这题关键是记录一个复杂度o(n)的方法,即在原数组上修改。
对于Nums中每个数据,不仅有数据的值,还有位置。那就遍历出现过的每个数据,并把数据对应成位置进行更改。对应位置的数据取负值。这里对于每个数据,先找到pos = abs(num) - 1;在进行该位置数据判断,是否已经出现,没有出现为正值的话,则改为负值。最后遍历数组,如果该数据为正值,对应位置的数据就是没有出现的。

48. 旋转图像

a[0][0] -> a[0][2]
a[0][1] -> a[1][2]
a[0][2] -> a[2][2]
易得, m[ row ][ col ] = matrix[n - col - 1][row];
此时,m[ row ][ col ]被覆盖,所以用一个tmp值先保留该值。下一步,matrix[n - col - 1][row] = matrix[n - row - 1][n - col - 1];
四次轮回后回到tmp值。
那么循环几次?官方解答很详细
在这里插入图片描述
在这里插入图片描述

240. 搜索二维矩阵 II

貌似是剑指的题,挺熟悉的。
从左下角这个值开始比较。如果 val > target 那么这一行其他值都舍弃。如果val < target 这一列值都舍弃。

769. 最多能完成排序的块

这题真是清奇。题意,这个打乱的数组,分成若干块,要求分好后每一个块内排序,再把这些块组合起来刚好是一个原数组排序后的数组。
切题的做法就是,当当前最大值刚好待在原来从小到大数组的位置时,切块。至于为什么,如果出现了当前最大值在一个较小的位置,比如[2,0,1],2出现在0号位置,意味着,从0 ~ 2号位置必然是比他小的,都要分到一块。也不排除0~2中当前最大值会出现更新,所以要不断判断。

796. 旋转字符串

看错题了一块写了吧。很简单的旋转字符串,记录一下find函数使用

if(dic.find(goal) != dic.npos )
在dic找是否有goal子串,没有返回dic.npos

232. 用栈实现队列

写过但是忘了。。。就是一个出栈一个入栈。每次需要出栈或者判断顶端值的时候,判断出栈是否有数据,为空就把入站数据入站,刚好为倒序。有数据则先用原数据输出。

155. 最小栈

维护两个栈,一个是存放数据,一个用来存放每次遇到的最小值。
关于push():
将数据放入Min_stk的条件一应该是if(min_stk.empty() || val <= min_stk.top())
第一个条件,保证只有一个数据的时候,也是最小值,第二个条件是 <= 。

20. 有效的括号

第一反应就是栈,但是想去维护三个栈乱七八糟的代码。。首先仔细看条件,左括号必须以正确的顺序闭合。也就是,括号内必须是完整的括号。在程序里的判断方法就是,如果当前为右括号,那么top值一定是对对应的左括号。stk里只存入了左括号。
还有几个小细节。一个是哈希Map的声明
unordered_map<char,char> dic = { {')','('} ,{'}', '{'} ,{']','['} };
二是这个代码:if(dic.count(ch)) 这个count函数仅仅是查询是否有key值,也就是查到的都是右括号,这句话是判断当前字符是左还是右括号。

739. 每日温度

单调栈。思路是,每个数据入栈,但是先进行判断顶端值是否小于当前值,也就是找到了顶端值的更大数。那么就将两个值作差放入ans数组。注意,题目要求是天数,所以栈维护的是下标而非数据本身。得到当前值答案就出栈。这个操作是循环进行的。

23. 合并K个升序链表

写了两种方法。
一,两两链表相互比较返回结果。
循环每个链表,两两比较返回一值,该链表下次继续比较。两两比较的代码死记。
二,利用优先队列。将每个链表每个数据放入大根堆,再组合成链表。关于组合链表的方法:

		ListNode* dummy = new ListNode(-1);
        ListNode* head = dummy;
        while(!heap.empty())
        {
            int tmp = heap.top();
            heap.pop();

            head->next = new ListNode(tmp);
            head = head->next;
        }

239. 滑动窗口最大值

优先队列和单调双端队列两种方法。但是本质差不多。
优先队列利用优先队列的已排序特点,先将前 k 个数据以及他们的下标 放入得出最大值。开始循环,每次放入新数据,不用急着将最左值删除,因为滑动窗口最左值不一定是最大值,不影响结果。但如果是最大值,就需要判断该值是否还在滑动窗口内。如何判断呢?当前右值下标为 i ,那么当前最大值的下标 <= i - k 则不在滑动窗口内。i - k 防止出现负数,所以一开始要先把前 K 个数据先处理。
双端队列的方法,是维护一个单调递减的队列,意味着头数据是最大值。每次放入新数据,比q.front() 小的话就进入。但是遇到比某个节点大,就循环把小数据丢出去。因为每次需要得到滑动窗口内的最大值,只有较大值会影响结果。当然头数据不一定在窗口内。判断方法如上。同时也说明了先处理 k 个数据的原因
注意后面的数据开始比较,先弹出后面的数据!!!

1. 两数之和

暴力就不说了。关于哈希表。这里哈希表,key 是数据,data 是 下标。原因是查找数据是否和为target,unordered_map中find函数是查找key的。返回是pos迭代器,所以数据下标是pos -> second。所以用auto 关键字。这里有个小技巧不用先循环放入数据也不用判断是否为同一个数据。那就是一次循环,先不放入nums[i],先判断这个nums[i] 是否有与之匹配的另一个值,有就直接返回,没有再放入该值。这样既不会与自己判断,也不会漏判断,因为后面的值会查找到前面的值。

128. 最长连续序列

hash表set。将数据放入hash中可以使用count函数判断在不在。
从hash的begin()值,开始一个个往两边找,对于每个找到的值就从hash中erase掉。判断每次的ans 和 next - prev - 1谁大。

149. 直线上最多的点数

比较方式很奇特。对于每个点,不断的对比之后的点,看斜率是否相同,一个点一个斜率就确定了一条线,与这个中心点相同斜率那么都在这条线上。所以对于每个中心点,需要一个哈希表来记录后面每个点与他的斜率与该斜率的点个数。当然用完这个点要清空hash,最大值有两种,一个是在同一横线上也就是y相同,这样保证息率分母为0 单独列出来。另一种就是与中心点重复的点。

332. 重新安排行程

真的好难啊。。。不知道怎么记录。大概就是dfs从JFK开始,循环遍历不断找能够走下去的边,每次找到一条边就把这条路删掉不再走。这个走边的技巧是,使用unordered_map<string,priority_queue<string,vector<string>,greater<string>>>vec;
起点映射多个终点,且终点自动按字典序排序。直到找到死路放入答案中。对于每个点,当边都被一个个删掉不再走的时候,放入的一定是当前情况的死路。最后逆序。

303. Range Sum Query - Immutable (Easy)

用了很普通的加法,这题应该时前缀和

304. 二维区域和检索 - 矩阵不可变

所以二维如何前缀和呢?我们应该求示以位置 (0, 0) 为左上角、位置 (i, j) 为右下角的长方形中所有数字的和。
intergral[i][j] = matrix[i-1][j-1] + integral[i-1][j] + integral[i][j-1] - integral[i-1][j-1]
对于结果
在这里插入图片描述
E = D − B − C + A
integral[row2+1][col2+1] - integral[row2+1][col1] - integral[row1][col2+1] + integral[row1][col1];
注意row + 1 和col + 1,前缀和的第一个数据都是 0 。

560. 和为 K 的子数组

普通方法妥妥的超时。这里前缀和用临时的,利用哈希表,前缀数据对应个数,对于每个psum,需要找到的就是psum - k,求这个数据有几个。但是

count += hashmap[psum-k];

++hashmap[psum];

顺序不可改变,如果当前 i == 0,哈希表先加入的话,count会重复加。因为 hash[0] = 1,前缀和一定要注意这个第一个值是0;

566. 重塑矩阵

我是废废,,这题写不出。。。
每 C 个一行,具体在i % c列,那一个个把原来[i / m][i % m]放进去就好了。。。

225. 用队列实现栈

看了一下思路,自己写出代码了。q1和q2,每次要放入q1的时候,先把q1的数据放入q2,新数据放入q1后再把q2的数据拿回来,保证了倒序。
另外其实使用一个queue就可以,每次放入新数据后,再把前面存在的数据都弹出再放入队列,就实现了倒序。

503. 下一个更大元素 II

双循环了半天没搞出来,看了答案觉得自己是铁憨憨。
分析出了单调栈放下标。
这题如何循环,虽然想到了是不知道怎么比较,原来是求余的方式,循环数组的两倍,超出的部分就是求余i % n。然后按照老样子遇到大大放入结果中。注意结果数组大小。结果数组初始化为 -1 ,这样就不用考虑最大值了

217. 存在重复元素

哈希表的简单题,不过官答更简洁,对于每个数据也不必记录,直接查找dic中是否存在s.find(x) != s.end()存在的话就返回true,反之放入dic,最后返回false.

697. 数组的度

为什么这是简单题。。不会搞。
第一次遇到这种,哈希映射<int ,vector< int >> ,数字映射一个三元数据数组,分别是key的数量,起始位置,终止位置。在遍历填入哈希表的时候,先查找这个数据是否存在,存在的话只需要把dic[nums[i]][0]++,dic[nums[i]][2] = i;
不存在的话放入 新数组{1,i,i};

594. 最长和谐子序列

朴素的哈希。
哈希表存储每个num出现的个数,然后遍历每个num 和num + 1的值找到最大的

287. 寻找重复数

自己用哈希搞出来了但是答案还是和值得学习的。
二分法:
根据题意,数据大小范围是[1 , n - 1],left = 1,right = n - 1,每次取中间,对于一个mid值,整个数组中最多有mid个小于等于mid的数据,比如mid = 3,小于等于3的只有123。如果出现了大于mid的个数,那么说明在left到mid之间一定多了一个数据,重复数据就在这部分,反之在mid + 1到right之间。这个left不是位置,就是数据本身。
Floyd判圈:
建立一个由 i -> nums[i]的边,全部建立后,会出现出现环。那就用环状两边的方法写。慢指针走一步快指针走两步,相同后慢指针归零,快慢指针都只走一步,相等即是重复数据

870. 优势洗牌

田忌赛马的贪心算法。两者都从最大的开始比,要是最大的都比不过,就用最小的去送人头。
关于代码实现
将nums2的数组,用pair对放入新数组,存入数据和位置。将排好序的nums1和nums2p,两个指针从尾部开始遍历,如果nums1的更大,a–,反之将b位置放入一个set,这样不会用重复数据,b–。
最后用迭代器把set中数据都用a剩下的值填入。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值