Leetcode array Top Interview Questions 32道题总结

本篇文章是对leetcode array和Top Interview Questions标签下32道array类型题目的总结

leetcode 283. Move Zeroes
此题属于数组交换型,交换数组中元素的位置以满足某种条件
解:交换数组位置,一般使用两个指针的办法,已到达O(1) space 的效果
并且此题有个特殊之处,题目中将数组中的元素分为0项和非0项,也可分为数组分类型,数组分类型的问题,假如有n个类,往往不必全部考虑这n个类,只要将n-1个类处理完,那最后一类自然被处理完。如本题中,只要处理了非0类,那么0类的数自然排到了末尾。

leetcode 169. Majority Element
此题属于数组计数问题,要找到数组中出现次数最多的那个数
解:

  1. 首先观察本题,发现此题可以从大问题分解为小问题,因此非常适合使用分治法
  2. 多数投票算法(Boyer-Moore Voting Algorithm)
    论文翻译:假设有一群投票的人,每个人都会投票个某个候选人。为了选择最终赢的选取的候选人,可以采用这样的选举方式:每个投票人找到其他的投票人,并且这个投票人支持的候选不同于自己的支持的候选人,PK过后,这一对投票人同时出局。经过全部的PK之后,那么还没有出局的投票人支持的候选人,就有可能是最终的选举胜利者(获得半数以上的选票)。最后,选举主席,需要检查这位可能赢得选举的候选人的票数,来确认他是否赢得了选举。
    https://www.cnblogs.com/javanerd/p/6262249.html
    因此,数组计数问题可以考虑摩尔投票算法
  3. 数字数量上的问题,最后会反应到32位上的每一位,因此可以从位操作的角度出发

leetcode 268. Missing Number
此题的特点在于限制了数组中出现数的范围,寻找缺失的那一个
解:

  1. 根据数组的和与每个数的关系,可以得到缺失数
  2. 数组位置i及其存储的值nums[i] 之间的关系,因为一个数异或它本身为0,而数组位置0~n-1,数组存储的值包含 0 ~ n,又因为异或具有交换律,所以将数组位置 i 与nums[i]异或,最后的结果便是缺失的数

leetcode 1. Two Sum
寻找数组中是否有特定的数,可以通过hash来减少查找时间
解:

  1. 使用hash减少查找时间,因为用hash可以达到O(1)的查找时间

leetcode 53. Maximum Subarray
寻找数组中的连续最大和子序列,属于寻找数组区间(不像上一道题是寻找某个数)
解:

  1. 观察本题,发现此题仍然可以从大问题分解为小问题,因此使用分治法
  2. 动态规划,区间的问题非常适合动态规划。而且发现在数组中,i 位置的情况是和 i-1情况相关的,因此使用dp
  3. 贪心, 发现 i 位置只需要依赖 i-1 位置的情况(在 i-1 情况明确时), 可以将动态规划简化为贪心

leetcode 26. Remove Duplicates from Sorted Array
此题特点:数组元素出现重复
解:

  1. 使用两根指针,一根指非重复数字,一根指向重复数字,并通过while循环跳过重复数组
  2. 也可看做数组分类问题

Leectode 88. Merge Sorted Array
此题特点:合并问题
解:

  1. 使用两根指针,合并时从后往前合并,这样就不需要考虑从前往后排时,要将后面的数字统一往后移动一格的问题。正向遍历困难时,通常可以考虑反向遍历

Leetcode 189. Rotate Array
此题特点:数组反转
解:

  1. 使用循环替代
  2. 先反转整个数组,然后再翻转前k个元素,再翻转后n-k个元素

leetcode 238. Product of Array Except Self
此题特点:数组区间
解:

  1. 使用两个动态数组。leftDp[i] 表示i左边的数的乘积,rightDp[i] 表示i右边的数的乘积,那么res[i] = leftDp[i] * leftDp[i]
  2. 发现 i 只与 i-1/i+1有关,因此可以将动态数组简化为1个变量

leetcode 78. Subsets
得到数组中所有的子集,属于排列组合型
解:

  1. 回溯法,find里面的循环是寻找第n层递归的起点,依次从前往后添加元素,以避免重复,第n层递归时,添加长度为n的子集。要注意,回溯为了消除前一次的影响,在回溯完之后,要移除回溯前添加的数

leetcode 287. Find the Duplicate Number
此题特点:数组重复
解:

  1. 检测重复问题,可以使用环检测算法,两根指针
    https://blog.csdn.net/xyzxiaoxiong/article/details/78761940
  2. 二分查找
    使用鸽笼原理,当数组内的数<= mid的个数多余mid时,将search范围放在mid~high,
    当个数小于等于mid时,将search范围放在low~mid,最后返回low/high(指的是索引)
    巧妙的运用二分法
    如n=10,mid=5,如果数组内小于等于5的个数多余5个,那么根据鸽笼原理,重复的数必在1~5内

leetcode 48. Rotate Image
特点:属于数组交换问题,交换位置
解:

  1. 先根据斜对称轴对折,再根据中线y轴对折

leetcode 62. Unique Paths
特点:没有特点
解:

  1. 很明显的动态规划,数组 位置 i 上的情况依赖于附近位置的情况

Leetcode 289. Game of Life
特点: 根据某个规则,修个整个数组
解:

  1. 使用辅助数组,记录修改之前的情况
  2. 不适用辅助数组,在原位置做标记,标记该位置做了修改或者需要修改,要注意做的标记不会影响到逻辑判断

leetcode 11. Container With Most Water
特点:寻找符合要求的数组区间
解:

  1. 使用两根指针,思考逻辑链:算面积->需要知道长宽-> 宽的话取决于两边短的一边-> 两边-> 两个指针法
    每次移动短的那一段,因为如果移动大的,那么在另一端不变的情况下,水量可能会变小,首先宽变小一位,而高度始终是以小的算的。相对来说移动小的,在宽度变小的情况下,更容易获得更大的面积

leetcode 75. Sort Colors
特点: 数组交换,数组分类问题
解:

  1. 将数组分为三类0类、1类、2类,对0和1两类进行交换,当交换完成时,2类也自动完成
    需要在数组上交换而不引入额外空间-> 两根指针

leetcode 105. Construct Binary Tree from Preorder and Inorder Traversal
特点:并无什么特点

leetcode 79. Word Search
解:

  1. 回溯法,记录访问轨迹,注意在这个分支回溯完成后,需要把访问数组中该位置置回原来的值
  2. 也可以不适用辅助数组记录访问,可以访问时将本位置的数标记,置为不会影响逻辑的其他数字,回溯完成时重新置回原来的数,这里有一个问题,难道不记录原来的数是什么吗?使用异或的性质,一个数异或本身为0, 设原来的数为A,回溯前A^256, 回溯后A ^ 256 就变回原来的A了

leetcode 73. Set Matrix Zeroes
特点:根据某项规则,修改整个数组
解:

  1. 使用辅助数组
  2. 进行标记,但要注意,这里标记如果原位置标记的话,会对后面的判断造成影响,所以对行首/列首进行标记。这样只需要对行首/列首单独处理即可

leetcode 162. Find Peak Element
寻找数组中是否有特定的数,使用二分查找
解:

  1. 线性扫描,遍历每一个数,比较是否左边的数要低一点,右边的数要高一点。当然,如果发现右边的数要低一点,就没有必要再看右边的数是否符合,直接看右边的数的下一个数
  2. 这样的题使用二分查找,一开始还是很惊奇的,因为印象中都是对有序数组才使用二分法,但这是对二分法的误解,要理解二分法的精髓在于在每一步都减少待搜索的区间
    考虑本题,一个数mid根据和其右边的数进行比较,可以得到该数是在下降的斜坡上还是在上升的斜坡,如果右边的数更小,可以认为是在下降的斜坡上,那么峰值一定是在mid的左边(包含其本身);如果右边的数更大,可以认为是在上升的斜坡上,那么峰值一定是在mid的右边,所以搜索范围可以限制在mid及其右边的子数组
    当然,比如右边的数比mid更大,是在上升的斜坡中,将舍弃mid左边的子数组中也可能存在peak元素,但本题不是求最大的数,本题只要求找到一个peak即可,甚至边界值(i=0,i=n-1)也算peak,所以才可以这样使用二分法
    使用二分法减少搜索空间

leetcode 56. Merge Intervals
特点:数组区间问题,排序处理
解:将每个区间进行排序,按照start进行排序,start小的排在前面,遍历排序后的intervals,如果第n个interval的end < 第n+1个的interval的start,那么就将第n个interval当作一个新的interval加入结果中。如果第n个interval的end >= 第n+1个的interval的start,那么就进行合并,end取两个区间中最大那一个

leetcode 34. Find First and Last Position of Element in Sorted Array
特定: 寻找数组中特定的数,使用二分查找
解:

  1. 遍历数组找到最大最小
  2. 使用二分查找,减小搜索空间。
    按照正常binary search首先在mid处找到一个target,因为数组已经排序好了,如果还有其他的target,那一定是在target的左边还有右边,如果有,那就分别以mid-1作为最右边界/mid+1作为最左边界继续查找,得到position更小/更大的数

leetcode 33.Search in Rotated Sorted Array
特点:寻找数组中特定的数,使用二分查找
解:

  1. 观察题目发现,因为数组被旋转了,所以可以确定,左边被旋转的那一部分的第一个数比右边被旋转的部分都大,右边被旋转的最后一个数也是比左边被旋转的所有数都小,所以每次二分查找时,判断mid的左边和右边是否正常,比较target和nums[start]的大小关系,如果nums[start] 比 target大,说明target应该位于mid的右边

leetcode 55. Jump Game
特点:
解:

  1. 自顶向下动态规划,从method 1中我们可以看出,有些位置会重复遍历,因此我们引入记忆化搜索,还会回溯,但每到一个位置i时,先查看该位置是否是能够到达终点的位置(GOOD), 还是不能到达终点的位置(BAD),如果是不知道的位置(UNKNOWN),则遍历这个位置
    通过使用dp降低了复杂度,存储了index i是否能到达终点的结果,当再经过这些index的时候不再需要计算!
  2. 从method 3 中再次得到启发,如果在位置i,且i~nums[i]+i这之中有一个GOOD INDEX,那么代表位置i也是个GOOD INDEX
    遍历完如果lastPos = 0 则表示起点也是good Index,那么从起点也可以跳到终点
  3. 当方法超时的时候)发现是不是有些步骤被重复计算,如果有重复计算,那么就引入动态规划
  4. 反向思考,问的是从起点到终点,如果能从终点到起点,结果也是等价的

leetcode 54. Spiral Matrix
特点:遍历数组
解:

  1. method1 中 if和else过多过于繁杂,观察题目我们可以发现,遍历的方向是有顺序的,永远是往右,往下,往左,往上。因此,我们可以通过加法+求余来得到下一个方向,因此,也能够知道x,y该加还是该减,还是该不变
    dr 表示row上的操作,dc 表示在column上的操作,当往左或往右时,行数不变,因此dr[0]/dr[2]都为0
    如果遍历的方向是有规律的,是有循环的,那么可以使用加法和求余来控制方向!

leetcode 152. Maximum Product Subarray
特定:数组区间型
解:

  1. 单纯用Maximum Sum Subarray的方法来做本道题是错的,问题就在于dp[1]可能不仅和dp[i-1]有关,甚至和dp[i-2]有关,例如负,正,负的情况,在这样的情况下,dp不仅需要记录以i-1结尾的最大积,也需要记录以i-1结尾的最小积,因为如果异数相乘,就变成了一个负数
  2. 使用一个变量代替数组,当动态规划数组中,dp[i]只与dp[i-1]相关,那么这个时候可以用一个变量代替dp数组
  3. 动态规划中弄清楚,当前位置的数究竟和附近哪几个位置有关

leetcode 15. 3Sum
特点:寻找数组中特定的数
解:

  1. 3sum问题转换为2sum问题,然后再用2sum的办法解决。这里使用两个指针的方
  2. 在使用两根指针的时候,考虑一下是否需要排序,让指针的移动更具有方向性
  3. 当处理去除重复性问题时,既可以使用set自动去重,也可以进行排序,当数重复的时候便跳过

leetcode 42. Trapping Rain Water
特定: 和leetcode 11一样,着眼于每个位置i能够积累多少水
解:

  1. 先找到数组中bar高度最高中最后一个的索引(因为可能存在多个高度一样的bar)
    还是以每个位置i上能积累多少水量的思想计算,从左往右遍历时,每次遍历到位置i,只要在i+1~maxIndex这片区域中有连续的小于height[i]的bar(j),就证明这个bar可以积累水,并且积水量等于height[i]-height[j]
    从右往左也是相同的
    为了避免重复计算,会跳过已经计算过水的索引

leetcode 128. Longest Consecutive Sequence
特点:寻找数组区间
解:

  1. 滑动窗口 利用set的特性,不断地remove
    使用一个set,对每一个数,检查是否有它的前继和后继,如果有则推进,感觉相当于滑动窗口的变种,使用remove方法可以减少遍历的数据量

leetcode 84. Largest Rectangle in Histogram
特点:数组区间问题
解:

  1. 分治法,RMQ思想,区间最值
    http://dongxicheng.org/structure/lca-rmq/
    https://blog.csdn.net/qq_41311604/article/details/79900893
  2. 等正式复习完补充

leetcode 41. First Missing Positive

leetcode 4. Median of Two Sorted Arrays


问题特点:

  1. 数组交换
    283、48
  2. 数组分类
    26、
  3. 数组计数
    169、
  4. 限制数组中出现数的范围
    268、
  5. 数组区间
    53、238、11、56、152、128、42、84
  6. 数组元素重复
    26、287
  7. 合并问题
    88、
  8. 排列组合
    78、
  9. 根据某个规则,修改整个数组
    289、73

启示(array类型问题可以采用的方法):

  1. 两根指针
    主要是应对数组交换、分类等问题,将space cost限制在O(1)
    如果是重复为题,通过while循环跳过重复数组
  2. 如果观察问题,发现问题可以分解为小的问题,采用分治法
  3. 多数投票算法
  4. 使用位操作解决数字计数问题
  5. 寻找数组中是否有特定的数,可以通过hash来减少查找时间
  6. 如果数组位置i及其存储的值nums[i] 有对应关系,可以考虑异或
  7. (数组区间)如果发现数组i位置的情况是和 依赖于附近位置情况,那么可以使用动态规划
  8. 如果动态规划中, 发现 i 位置只需要依赖 i-1 位置的情况(在 i-1 情况明确时), 可以将动态规划简化为贪心
  9. 数组反转
    (1)使用循环代替
    (2)先反转整个数组,然后再翻转前k个元素,再翻转后n-k个元素
  10. 排列组合型,使用回溯法,find里面的循环是寻找第n层递归的起点,依次从前往后添加元素,以避免重复,第n层递归时,添加长度为n的子集。要注意,回溯为了消除前一次的影响,在回溯完之后,要移除回溯前添加的数
  11. 检测重复问题,也可以使用两根指针,使用环检测算法
  12. 二分查找,查找数组中的某个数,可以使用二分查找减少数组的搜索空间,不一定要求数组是有序的,只要能够按照二分查找的思想,每次舍弃一半的无用的查找空间
    (1)leetcode 287
    使用鸽笼原理,当数组内的数<= mid的个数多余mid时,将search范围放在mid~high,
    当个数小于等于mid时,将search范围放在low~mid,最后返回low/high(指的是索引)
    巧妙的运用二分法
    如n=10,mid=5,如果数组内小于等于5的个数多余5个,那么根据鸽笼原理,重复的数必在1~5内
    (2)leetcode 162
    这样的题使用二分查找,一开始还是很惊奇的,因为印象中都是对有序数组才使用二分法,但这是对二分法的误解,要理解二分法的精髓在于在每一步都减少待搜索的区间
    考虑本题,一个数mid根据和其右边的数进行比较,可以得到该数是在下降的斜坡上还是在上升的斜坡,如果右边的数更小,可以认为是在下降的斜坡上,那么峰值一定是在mid的左边(包含其本身);如果右边的数更大,可以认为是在上升的斜坡上,那么峰值一定是在mid的右边,所以搜索范围可以限制在mid及其右边的子数组
    (3)leetcode 34
    按照正常binary search首先在mid处找到一个target,因为数组已经排序好了,如果还有其他的target,那一定是在target的左边还有右边,如果有,那就分别以mid-1作为最右边界/mid+1作为最左边界继续查找,得到position更小/更大的数
    (4)leetcode 33
    观察题目发现,因为数组被旋转了,所以可以确定,左边被旋转的那一部分的第一个数比右边被旋转的部分都大,右边被旋转的最后一个数也是比左边被旋转的所有数都小,所以每次二分查找时,判断mid的左边和右边是否正常,比较target和nums[start]的大小关系,如果nums[start] 比 target大,说明target应该位于mid的右边
  13. 回溯,根据某个规则,修改整个数组
    (1)可以使用辅助数组
    (2)不使用辅助数组,在原位置上做标记,要注意做的标记不会影响到逻辑判断
    (3) 如果是在回溯中做标记,为了不记忆原来的数,使用异或的性质,一个数异或本身为0, 设原来的数为A,回溯前A^256, 回溯后A ^ 256 就变回原来的A了
    (4)进行标记时要注意 这里标记如果原位置标记的话,会对后面的判断造成影响,所以对行首/列首进行标记。这样只需要对行首/列首单独处理即可
  14. 先对数组排序再处理,往往能减少复杂度
  15. 当方法超时的时候)发现是不是有些步骤被重复计算,如果有重复计算,那么就引入动态规划
  16. 反向遍历,反向思考,问的是从起点到终点,如果能从终点到起点,结果也是等价的。
  17. 使用数组来控制遍历的方向
    遍历的方向是有顺序的,永远是往右,往下,往左,往上。因此,我们可以通过加法+求余来得到下一个方向,因此,也能够知道x,y该加还是该减,还是该不变
    dr 表示row上的操作,dc 表示在column上的操作,当往左或往右时,行数不变,因此dr[0]/dr[2]都为0
    如果遍历的方向是有规律的,是有循环的,那么可以使用加法和求余来控制方向!
  18. 动态规划中弄清楚,当前位置的数究竟和附近哪几个位置有关
  19. 在使用两根指针的时候,考虑一下是否需要排序,让指针的移动更具有方向性
  20. 当处理去除重复性问题时,既可以使用set自动去重,也可以进行排序,当数重复的时候便跳过
  21. 对于数组区间问题,可以考虑使用滑动窗口问题,同时通过hash减少查找时间,将查找时间限制在O(1)

简略版:

  1. 两根指针
  2. 二分查找
  3. 分治法
  4. 回溯
  5. 动态规划
  6. 贪心
  7. hash减少查找时间(set/map)
  8. 位操作 异或
  9. 多数投票算法
  10. 环检测算法
  11. 环检测算法
  12. 反向遍历
  13. 排序
  14. 数组控制遍历方向
  15. 滑动窗口

你可能感兴趣的:(leetcode,array)