更新中…
本文主要概括总结了一些面试中编程题目的分类,以及常见的一些解决办法。
题目分类主要为两种方式:(1)按照 数据结构 分类;(2)按照 算法思想 进行分类。
首先了解各个数据结构的一些基本特性,然后才能找到适合的场景。而在面试中,主要考察的是你对于不同数据结构的性质的理解。
不使用中间变量,交换两个数值变量的值
随机访问
(1)【随机性】 数组具有随机访问的特性(根据下标,快速定位到数据)。所以可以利用一定的hash法,将数据与其下标形成一定的映射。(比如:年龄为1 到 35,那么可以映射到一个 整型数组A中, A[i]中 i 表示 年龄,此时我们只是应用了下标,而实际存储数据的空间并没有使用,所以我们可以利用其做其他事情,比如说统计人数)
(2)【hash应用信号】当数组中含有有限范围的整型时,那么这类问题很可能可以通过利用数据与下标的hash关系得以解决。
相关题目:
* 【下标与hash】217. Contains Duplicate
数组中求第K大数
问题:
第 k 大的数一定能 找到?(二分查找的过程不会跳过?)
每次排除了无效信息,不断排除,一定可以找到 k 。(排除思维理解二分查找,思想和这里很像)
【数组 + 排列 + 回溯(枚举)】46. Permutations
【数组 + 排列 + 回溯 + 去重 】47. Permutations II
这类问题下标的控制要尤其注意。
(示例2:【栈 + 环形数组】 503. Next Greater Element II)
二维数组通常可以用来刻画地图规划,棋类等问题。在解决这些问题时,都用到了重要的算法思想(后面会介绍)。
地图规划类问题,通常可以通过动态规划,回溯法来解决。(见下面例子)
【数组 + 回溯法 + 字符地图 + 存在类问题】79. Word Search
【数组 + 动态规划 + 无障碍方格地图 + 最值类问题】64. Minimum Path Sum
【数组 + 动态规划 + 路径数量计算问题】62. Unique Paths (之所以说其动态规划,因需考虑多阶段决策)
【数组 + 动态规划 + 有障碍地图 + 路径数量计算】63. Unique Paths II ( 在上面题目上加入有障碍)
感想:
【解决复杂问题】62. Unique Paths 与 63. Unique Paths II 题目非常相似,63题目只是在62的基础上,在地图上加入了障碍信息。其解答的过程告诉我们,当遇到一个很复杂的问题时,尝试去掉一些条件(简化问题),求得解之后,再去增加条件求解。
棋类等问题,通常可以用回溯法来解决。(见下面例子)
1.链表访问 不具有跳跃性。
假如你当前访问的结点为 N2, 那么接下来你 只能访问其相邻的结点 N1或者N3, 而不能不经过N3就直接跳去访问N4。换句话说,其访问的随机性小了(双链表情况下,下一次访问仍有两种选择)。
PS:
(1)随机性访问是数组的长处,你访问了A[2],接下来你可以访问A[3],也可以访问A[11]等,下一次访问的可以是任何数组中的元素。
(2)树的随机访问特性也是没有数组强的,但还是有一定的随机性的。(比如,二叉树中,当前节点为N,那么其下一个可能访问的结点为 N的两个孩子其中之一,每个有 1/2的概率)
2.链表访问具有方向性
(1)单链表只能向后访问。
(2)链表构成队列本质上也是向后访问。(只不过插入时插到队列尾部,所以需要两个指针:头指针,用于出队;尾指针,用于入队。)
(3)链表构成的栈 本质上也是向后访问。(只不过限定每次插入都插入到头部位置,删除时先删除头)
PS:
(1)栈 与 队列都是建立在链表的基础之上的,只不过在链表插入时以及删除时做了一定的限制。
(2)有序性的数组可以使用二分查找,但不适合于有序性的链表。
(3)【有序性】在有序的基础上,可以方便很多操作(比如,二分查找,去重,统计某值出现次数)。
相关的题目:
单个链表 + 更改操作:
【单向性 + 计数后移】19. Remove Nth Node From End of List (也可以使用栈)
【单向性 + 反转】206. Reverse Linked List (可使用几个指针,或者使用栈)
【单向向后 + 限制反转】24. Swap Nodes in Pairs (只允许相邻(每两个)的反转)
单个链表 + 发现特殊结构:
【链表 + 环结构判断】141. Linked List Cycle (链表中是否存在环?相关解答)
【链表 + 环状判断】【启发】求有环单链表中的环长、环起点、链表长【快 、 慢指针】(原来指针还可以有速度)
多个链表:
【单向向后 + 合并链表】21. Merge Two Sorted Lists(无论是数组还是链表都要求按序处理(N1之后是N2),那么在这方面数组与链表没有什么区别)
树或者图的广度优先遍历。
特性
后进先出。
场景:
(1)【次序】数据需要从后面向前匹配。
(2)【依赖】前面的数据的结果依赖于后面的数据。A(i) 结果依赖于 A(j),其中j > i ,且A(i)与其前面内容A(k)关系较少,其中i > k。(示例1:【栈】496. Next Greater Element I )(示例2:【栈 + 环形数组】 503. Next Greater Element II)
(3) 将递归变为迭代。(示例:【树 + 栈 + 用迭代模拟递归】94. Binary Tree Inorder Traversal )(该问题有助于理解递归的计算机实现)
(【启发 + 答案】94. Binary Tree Inorder Traversal(树的非递归中序遍历 + 栈模拟递归) )
【树 + 栈 + 模拟先序遍历】144. Binary Tree Preorder Traversal
【树 + 栈 + 模拟后序遍历】145. Binary Tree Postorder Traversal
其他内容:
【队列 & 栈 + 互相模拟】 232. Implement Queue using Stacks
【队列 & 栈 + 互相模拟】 225. Implement Stack using Queues
【栈 + 括号匹配 + 就近匹配】20. Valid Parentheses(括号的就近【向后】匹配原则,十分适用于通过栈来进行处理(使得出栈的规则变得简单))
树的应用十分广泛,它是一种数组 与链表之间的混合体。
(1)【和链表相似】是因为它的实现和链表的思想是一致的,这使得树具有链表的相似之处。
(2)【和数组相似】是因为在树中某个节点的孩子结点有多种可能(具有了部分数组的随机性),这使得它也可以做数组可做的事情(比如:二分查找)。
注意:
【树的性质】不同的树往往具有不同的性质,而不同的性质导致了其不同的用途。所以有必要在练习的时候结合树的一些性质进行学习。
【遍历】几乎所有树的操作都建立在遍历的基础之上。遍历分为 深度优先遍历 , 广度优先遍历。 — > 需要弄清楚这两种遍历方式适用的场景。
深度优先遍历(应用场景):
先序遍历
(1) 当前结点的结果,后对孩子结点结果造成影响。(比如:【构建字符串 + 先序遍历】606. Construct String from Binary Tree)中序遍历
(1) 搜索树的遍历。(【寻找第k小 + 中序遍历(相当于从小到大访问) + 计数器(访问时计数)】230. Kth Smallest Element in a BST)后序遍历
(1) 当前结点的结果,依赖于其孩子结点的计算结果(比如:【二叉树的直径 + 自底向上(后序遍历)+ 技巧】543. Diameter of Binary Tree)
注意:
如果各结点独立,求解问题也不需要考虑次序,可使用任何一种遍历方式。
针对树的遍历:
———– 非递归实现:
【先序遍历】144. Binary Tree Preorder Traversal【中序遍历】94. Binary Tree Inorder Traversal
【后序遍历】145. Binary Tree Postorder Traversal
———— 广度优先遍历
【广度 + 队列 + 计数(技巧)】637. Average of Levels in Binary Tree
遍历 + 目标:
【遍历 + 根到叶子的路径 +寻找规定的目标和】112. Path Sum
【遍历 + 根到叶子的路径 + 列出所有满足目标的路径】113. Path Sum II
【遍历 + 任一向下路径 + 寻找规定目标和 +计数 +技巧】437. Path Sum III
【平衡树判断 + 不一定是搜索树 + 高度计算 + 技巧】110. Balanced Binary Tree
【二叉树最大深度 + 自底向上(后序遍历) 】104. Maximum Depth of Binary Tree
【构建字符串 + 先序遍历】606. Construct String from Binary Tree
【计算所有左叶子结点的和】404. Sum of Left Leaves
【二叉树的直径 + 自底向上(后序遍历)+ 技巧】543. Diameter of Binary Tree(体会后序遍历的应用场景,当前结点的结果,依赖于其孩子结点的计算结果)
完全二叉树:
【高效计算完全二叉树的数量 + 利用完全二叉树的性质 + 技巧】222. Count Complete Tree Nodes更改树:
【反转二叉树 + 自顶向下(先序遍历) + 左右孩子交换】226. Invert Binary Tree
搜索树:
更改搜索树:
【删除二叉搜索树中的结点】450. Delete Node in a BST【搜索树变为更大的树】538. Convert BST to Greater Tree
判断 & 寻找
【是否是二叉搜索树】98. Validate Binary Search Tree【寻找第k小 + 中序遍历(相当于从小到大访问) + 计数器(访问时计数)】230. Kth Smallest Element in a BST
其他:
【判断一颗树是否对称】101. Symmetric Tree
【判断 2 树是否相同】 100. Same Tree
【按规则合并两棵二叉树】617. Merge Two Binary Trees
【判断一棵树是否是另一棵树的子树】572. Subtree of Another Tree
图类问题,一般需要记录访问状态(visited)。
【百度百科】排列组合
复习几何问题的关键概念,然后尝试用编程思维去解决相关的问题。(做几道题,体会其关键思路)
计算几何基础知识整理
【CSDN】计算几何题集
【这篇文章给出了动态规划更具体的理解】【总结】动态规划概述
动态规划 的核心是 分阶段决策。
动态规划 在实现上,往往能够通过一定的技巧避免函数递归的出现。
个人总结动态规划的特点:
【按一定次序逼近目标】按照某种方式逐渐去接近目标。(逼近的方向应该是明确的,确保可以处理)
【隔离性】动态规划通过划分想要达到的效果是,第 k 个阶段的求解,只与前面有限个阶段有关(如:只和第 k-1,k -2 个阶段有关)。这里也体现了分层次的思想(如,某一层只依赖于下面1层,且只能向上一层提供服务)。 —> 换句话说,你的划分角度应该能到隔离层次的目的。
【举例】上面特点的理解:
求连续子数组的最大和
很多问题都可以归结为连续区间的问题。下面给出一些连续性区间问题的解答思路。
【加工问题,导出问题的子问题】《怎样解题》中提到的一种思想:(原则)将问题转化为类似的问题,以前见过的问题,而新的问题更加容易求解。
示例:
leetcode - 303. Range Sum Query - Immutable 【动态规划 + 间接逼近目标 + 区间计算 +刻度 + 距离计算方式 】
这类问题,一个比较明显的特征是:【独立划分】在试探的过程中,如果某些条件不再符合,那么就采取剪枝的策略(也就是说,重新开始),之前的历史与后面的划分为无关的。可以通过下面一个例子体会:
最终的目标划分成小目标:(如,在求连续子数组最大和中,将原问题归结为求以每个数结尾的最大和)
当远离目标时,及时终止;
当接近目标时,继续使用。求连续子数组的最大和
【启发】leetcode - 121. Best Time to Buy and Sell Stock 【动态规划 + 连续区间问题 + 试探性购买】 (这个问题可以归结成最大连续子数组的问题,细微的区别在于,(1)当远离目标时,终止的规则不同(利润重置为0);(2)其不是直接利用原来的数据,而是利用两个连续数之间的差(比最大连续子数组多了一步计算))
回溯法又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
(1) 针对给定的问题,定义问题的解空间;
(2) 确定易于搜索的解空间结构;
(3) 以深度优先方式搜索解空间,并且在搜索过程中用剪枝函数避免无效搜索。
可结合如下问题进行体会:
UVA 题目难度分级列表