剑指 Offer 53 - II. 0~n-1中缺失的数字 利用二分法找到第一个与当前位置不符合的位置就行,前面都是有序的如果缺一个肯定会造成无序(注意,因为全有序说明少了最后一个,此时left也指向了最后一个)
75. 颜色分类 这道题要求扫描一遍直接排序完,这里就需要用到多指针的办法。下面的代码用到了三个指针分别代表着指向第0个分区的尾指针,第1个分区的尾指针和第2个分区的尾指针。(注意这里的尾巴指针并没有相应的值)遍历时候根据当前的值进行判断,如果当前的值为0说明需要往0分区添加一位0,这时候第1分区和第2分区就得腾位置出来,因为这个数组是连着的,如果新出现了0的话其他两个分区往后挪,0分区就会占用1分区的一个值,这时候1分区就需要多添加一个1,2分区也会因为1分区的挪动被占用一个值,也需要多填一个2。
155. 最小栈 这道题用到了两个栈来维护,第一个栈就正常的使用,第二个栈用来维护最小值,当新来的值大于第二个栈的top值的话就没必要加进来,因为之后每次取最小值的时候都会返回这个top值,直到pop的时候将这个值弹出。那么这个栈top为最小 top-1的值就是当top不在时候整个栈的最小值以此类推。当pop出的值是top值的时候说明这个栈的最小值不再是这个top值了。
207. 课程表 首先要完全理解题意,这道题的[a,b]并不是b满足了a就可以真正的学习a这门课了,因为a还有可能需要其他选修课的条件。类似下图。这题的思路在于使用合适的数据结构来存储,这里用hash表来存储如果1这门课可以修了之后,可以让哪些课所需条件–。定义一个每个课所需要的条件的数组。关键就在于当条件都满足也就是所需条件为0的时候,应该让他发挥他的作用。
994. 腐烂的橘子 这道题的思路在于使用一个队列,存储每一个腐烂的橘子的位置,每一分钟都把队列中已有的烂橘子同时开始污染周围的好橘子,把好橘子给存入队列,如果周围都已经是烂橘子了就不用继续花时间来感染了,本题类似层序遍历的写法。
138. 复制带随机指针的链表 这题的主要思路在于利用hash先来。第二次遍历为new节点的next字段和random字段赋值,old的next和random会在这个hash表中存在并有和其映射的new节点,此时就可以为new节点赋值完,最后返回头结点。(不用担心next和random不存在的问题,如果不存在map默认返回nil刚好对应要赋的值)
73. 矩阵置零 这题要求在原地修改,空间复杂度为常数级。这题的主要思路在于用第一行第一列来保存整行整列是否为0的状态。用两个bool变量储存第一行和第一列后续是否需要变动。你可能会想如果改变第一行第一列的值但是第一行第一列本来不需要变,那怎么找回原有的值?这个不用担心,因为只要中间有了0,这个第一行或者第一列的标识迟早也会变成0,只不过早变晚变的原因。这里需要注意的一点为[0][0]的位置,如果你在给每行每列赋值的时候从这个位置开始的话,会影响第一行第一列这个用来暂存的全部数据,导致全部变成0。
560. 和为 K 的子数组 本题的主要思路为创建一个哈希表记录,在遍历这个数组的时候查询有没有sum-k的值在哈希表中,如果有,说明有个位置到当前位置的和为k。有可能不止一个,哈希表负责记录有几个sum-k,将和记录下来。这里把每个以i结尾的结果都算上了,所以不用担心不全等问题。
3. 无重复字符的最长子串 这题做法为滑动窗口(为什么用滑动窗口?如果暴力以每个字符开头开始暴力的话时间太久了,滑动窗口左边直接去掉的是完全没必要作为开头的所以利用滑动窗口一齐排除,省去了大部分的步骤),需要判断新加入窗口的右边有没有在这个出现过,如果没有出现过说明没有重复那就继续扩大窗口的右边,如果有重复的值,那就要注意一下了。这个无重复字符子串不可能再包括前面那个与它重复的字符了,因为是子串那么重复前面的也不能要了但是前面那个重复的后面那些字符串肯定和这个新出现的不相等还可以继续保留。这里需要注意一下,出现重复的。
128. 最长连续序列 这题代码的要求为o(n)复杂度,那就只能空间来换时间了,O(n)常常与哈希表相关。本题的思路也在于哈希表的解法。遍历每个元素,如果元素不存在于哈希表的话,也就是新数加入就为其创建一个键值对。这个键值对的val代表的意思为加入了这个字符之后,。如果是旧数加入的话,因为肯定用过它,如果没用过它,它也撼动不了最长的地位,这个序列的某个位置加入才会使它变长,也就是说这旧数的加入没有意义,就没有必要加进来。那么这个新加入的数字怎么样知道自己的出现会不会导致所在的序列变为最长呢?
42. 接雨水 这题关键在于每个地方接的水为左边最大和右边最大的最小值减去这个地方的高度。还需要注意的一个地方在于两边作为边界的地方是不会接水的。这里我用动态规划的方式记录当前位置左边最大和右边最大的值。这里需要注意的一个点是如果左边没有比当前位置大的当前位置应该赋值为多少?这里直接赋值为当前高度值,因为最后会取两边大值的最小值减去当前值。(如果填了本身之后,右边也会出现两种情况,如果右边有比这个大的值,那就不影响最终值,如果右边没有比这个大的值的话,也会为其赋值为本身的高度,不影响最后结果的计算)
583. 两个字符串的删除操作 本题也是动态规划的解法,dp[i][j]代表的意思就是word1中0~i的字符串和word2中0 ~ j的字符串相等需要的步数,也是通过判断尾部字符是否相等的方式,如果相等的话也就不需要改动了取dp[i-1][j-1]的步数就行,如果不相等的话需要找个最小值,有可能是i结尾字符串和j-1结尾字符串重合度高步数少,也有可能是j结尾字符串和i-1结尾字符串重合度高步数少,两者取步数最小的情况,还要加上肯定变动的步数1。
115. 不同的子序列 这题dp[i][j]的含义为s中0~i的字符串中有多少个子串为t的0 ~ j这个字符串,dp[i][j]的推导关键在于判断s[i]和t[j]是否相等的时候,如果相等的话可以选择是否需要i位置的字符参与,如果不需要的话有s[i-1][j]种,如果需要i位置的字符参与说明肯定要用这个字符,那这个字符是定死了和t字符串第j个相等的,那么s[i-1][j-1]有多少种这种就有多少种。所以s[i]和t[j]相等的时候为这两种情况的和。
122. 买卖股票的最佳时机 II 和 买卖股票的最佳时机含手续费 两题的本质是一样的,只不过含手续费多了一个手续费,手续费可以在买的时候一并扣掉就行。这两题的关键在于到理解dp数组创建的意义,这两题dp数组创建的意义为,dp[i]就可以根据dp[i-1]也推出最优情况,只需要考虑前面的最优和新的一天的变故就能推出到今天为止的最优情况。dp[0]代表未持有状态 dp[1]代表持有状态。当天dp[0]的结果=之前最好的或者今天卖掉股票后目前的利润。
139. 单词拆分 i循环和j循环的顺序很重要,如果i循环在外面的话有时候需要其他字符前面的支持才能判断以当前位置结尾的是true还是false,这时候如果没有前面的支持直接判错,后面用其他元素来填充的时候也没机会了,永远都是false了。
279. 完全平方数 这题也是完全背包问题,看看这个容量能被多少个装满而且要最少。一开始思路可能是直接给个最接近的完全平方数,然后对剩下的差值进行组装,因为动态规划的原因可以直接知道这个差值用的最少数量。问题就出在这个差值上面,差值有多有少,可能差值太多导致这个n组成要用掉很多完全平方数。既然肯定要组装,那就把比它小的完全平方数都遍历一遍然后看看和差值组成数量的和哪个最小(这样就包含了全部情况但也没包含全部情况)。
474. 一和零 dp数组创建的含义:当m,n最大为i,j的时候最多可以放几个子集。a,b代表这个字符串有多少个0多少个1注意点:如果你直接把一个元素进行遍历的话,dp[i][j]前面的值有些是加过这个新元素进行过计算的。就会干扰这个值。所以这个就创建一个三维数组来保留原有二维数组dp[][]的值,利用原有的进行计算。最后就得到了所有元素都试过的最大值。