LeetCode周赛195总结

第一次做LeetCode周赛,总结一下。

听说这次好像服务器出了问题,我做的时候前40分钟巨卡无比,基本交不上。当然我本来也菜,一路踩了很多坑,最后只解决了两道半。

 

题目1:

给你一个字符串 path,其中 path[i] 的值可以是 'N''S''E' 或者 'W',分别表示向北、向南、向东、向西移动一个单位。

机器人从二维平面上的原点 (0, 0) 处开始出发,按 path 所指示的路径行走。

如果路径在任何位置上出现相交的情况,也就是走到之前已经走过的位置,请返回 True ;否则,返回 False 。

第一题是一个hash问题,问题本身很简单(Easy),但是具体做的时候犹豫了一下,主要是考虑如何构造hash,因为我用的是C++,大概想了三个方式:

  • STL的unordered_set
  • STL的set
  • 自己构造数组实现

首先使用了unordered_set去做,因为是一个二维坐标,所以用到了pair,但是unordered_set对于这种特殊的类型需要自定义hash函数,忽然忘记了怎么实现(其实从来也没完全记住过),所以只得换方法。

在这里记录一下unordered_map<pair<int, int>>型自定义hash函数的方法(以二维坐标为例):

1 struct pair_hash {
2     inline size_t operator()(const pair<int,int> & p) const {
3         return p.first * MAP_HEIGHT + p.second;
4     }
5 };
6 unordered_set<pair<int, int>, pair_hash> pHash;

主要是以下几点需要注意:

  • 定义在结构体中
  • 以自定义运算符的形式出现
  • 参数类型,返回类型

实际上,既然已经知道了hash函数的计算方式,完全没有必要再去额外定义hash函数,可以直接使用unordered_set<int>作为hash表,加入的元素是hash函数的运算结果,LeetCode的官方题解就是这样处理的。

 

之后使用了自己构造数组的方法,之所以没有使用第2个方法是因为考虑STL中set的红黑树实现复杂度是对数级别,但是后来发现使用set是最简单的方法...

在自己构造数组中,发现一直有未知错误,原来是又掉坑了,由于数组比较大,分配局部变量的时候数据位于栈空间,栈空间不足会导致运行时出现未知错误,所以如果真的使用这种方法需要注意分配在全局变量里面。

 

最后,终于使用set通过了...但是这样做应该不符合对时间复杂度有严格要求的题目吧?

 

题目2:

给你一个整数数组 arr 和一个整数 k ,其中数组长度是偶数,值为 n 。

现在需要把数组恰好分成 n / 2 对,以使每对数字的和都能够被 k 整除。

如果存在这样的分法,请返回 True ;否则,返回 False 。

首先可以看出:如果数据本身是大于k的,显然减去k对于结果没有影响,就是所有的数字在处理前应该进行取余操作简化。

然后要处理的问题是数组元素配对,满足和为k的构成一对,因此需要一个长度为k的数组进行记录。

需要注意的是负数的处理,为了方便余数统计,需要把负数处理为0-k-1之间的数如下:

xk = (x % k + k) % k

这个题目做的相对顺利一些...

 

题目3.

给你一个整数数组 nums 和一个整数 target 。

请你统计并返回 nums 中能满足其最小元素与最大元素的 和 小于或等于 target 的 非空 子序列的数目。

由于答案可能很大,请将结果对 10^9 + 7 取余后返回。

 

题目中有几个地方需要注意:

  • 子序列、子串、子数组的区别
  • 如何处理int溢出的数据

首先,对于子序列、子串、子数组的定义,这三个名词常出现在dp问题中,子序列是一个数组中不连续元素(连续也可)组成,子串是一个数组中连续元素组成,子数组这个说法不太严谨,一般题目中应当说明是连续子数组,否则只能根据题目用例具体判断。

 

在本题目中,数据的溢出出现在两个方面,一个是输出结果的存储,一个是中间过程pow计算,因此可以使用快速幂递归的方式求解(如果使用,需要取余)。实际操作中,可以开一个数组把各次幂结果保存下来,需要的时候直接取值。具体到本题,由于排序时间复杂度已经O(nlogn),pow的计算可以不使用快速幂,不会影响整体时间复杂度。但是如果题目要求不使用额外空间,则还需要利用快速幂计算。但无论那种方法,对于总体时间复杂度没有影响。

 

在具体做法上,首先,可以看出,子序列是数组中数据的组合,因此与是否排序无关,所以可以使用排序操作简化后续的判断流程。

 

一旦数组完成排序后,可以发现,一个任意子序列都对应一个数组窗口中的一种组合,因此我们只需要遍历数组,考虑满足要求的所有窗口,把窗口中的子序列个数累加起来即可。这个个数就是2^(right-left)个,表示以left开头后面的所有组合情况。

 

总结:

个人感觉LeetCode的选题还是挺好的,简单题直接模拟就可以得到答案,只要熟悉简单的数据结构和STL就可以;中等题一般需要两个知识点左右,一般暴力模拟是不可行的,因此一旦发现是指数复杂度的就应当考虑优化,看一下是否有dp的重叠子问题和hash解决,细节想清楚的话写起来程序不会很长。

 

posted @ 2020-07-01 15:43  haydenhs  阅读(148)  评论(0)    收藏  举报