从外向里以顺时针的顺序打印矩阵-题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
样例
输入:
[
[1, 2, 3, 4],
[5, 6, 7, 8],
[9,10,11,12]
]
输出:[1,2,3,4,8,12,11,10,9,5,6,7]
设计一个支持push,pop,top等操作并且可以在O(1)时间内检索出最小元素的堆栈。
push(x)–将元素x插入栈中
pop()–移除栈顶元素
top()–得到栈顶元素
getMin()–得到栈中最小元素
样例
MinStack minStack = new MinStack();
minStack.push(-1);
minStack.push(3);
minStack.push(-4);
minStack.getMin(); —> Returns -4.
minStack.pop();
minStack.top(); —> Returns 3.
minStack.getMin(); —> Returns -1.
空间换时间,用另外一个stack去储存最小值。
另外,如果要求额外存储空间控制在$O(1)$呢?
思路:
如果新来的元素NewCome,比之前的最小值oldMin更小;oldMin存的就是当前元素了; 现在的问题变成“当当前元素被pop出之后,如何计算出新的最小值)?” 新存入的diff = newMin - oldMin; oldMin = newMin - diff ; 因为diff小于0,所以oldMin更大;
1 | class MinStack { |
请实现两个函数,分别用来序列化和反序列化二叉树。
您需要确保二叉树可以序列化为字符串,并且可以将此字符串反序列化为原始树结构。
样例
你可以序列化如下的二叉树
8
/ \
12 2
/ \
6 4
为:”[8, 12, 2, null, null, 6, 4, null, null, null, null]”
注意:
以上的格式是AcWing序列化二叉树的方式,你不必一定按照此格式,所以可以设计出一些新的构造方式
重点考虑反序列化,我们需要考虑一种很容易找到root节点的遍历方式。
如果我无法找到root节点,是无法通过递归来完成反序列化的。
在前中后三种遍历中,我们可以选择先序遍历。 先序遍历,最容易找到root节点
1 | /** |
给定一个数字,我们按照如下规则把它翻译为字符串:
0翻译成”a”,1翻译成”b”,……,11翻译成”l”,……,25翻译成”z”。
一个数字可能有多个翻译。例如12258有5种不同的翻译,它们分别是”bccfi”、”bwfi”、”bczi”、”mcfi”和”mzi”。
请编程实现一个函数用来计算一个数字有多少种不同的翻译方法。
样例
输入:”12258”
输出:5
首先最容易想到的解法是递归,但是我们注意到有很多重复计算,所以更优的做法应该是DP
dp[i]表示0到s[i]这个字符串能够包含的翻译方法。
启发式思考:对于字符串类的DP问题,dp[i]一般是表示以i为结尾的字符串要计算的值。
d[i] = dp[i - 1] + dp[i - 2] when as a ALHPA OR dp[i - 1] when other
1 | class Solution { |
请实现一个函数可以复制一个复杂链表。
在复杂链表中,每个结点除了有一个指针指向下一个结点外,还有一个额外的指针指向链表中的任意结点或者null。
注意:
函数结束后原链表要与输入时保持一致。
有两种思路:
第一种思路,比较容易想到
(1)遍历一遍,复制出新的node, 并维护好node之间的next关系,同时使用HashMap存储新旧node的映射关系
(2)再遍历一遍,借助hashMap找到新节点对应的random指向过去。
时间复杂度:$O(2n)$, 空间复杂度:$O(n)$
那么我们是否可以不使用额外的空间吗?答案是可以的(此处需要启发式思考,实在想不出我觉得也问题不大!)
(1)遍历一遍,复制新的节点,作为原节点的下一个节点插入到链表中
(2)遍历一遍,如果有random指针,则借助新插入的节点找到新的random指针目标节点
(3)再遍历一遍,访问链表,去掉老的链表节点
时间复杂度:$O(3n)$, 空间复杂度:$O(1)$
1 | /** |
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。
假设压入栈的所有数字均不相等。
例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。
注意:若两个序列长度不等则视为并不是一个栈的压入、弹出序列。若两个序列都为空,则视为是一个栈的压入、弹出序列。
样例
输入:[1,2,3,4,5]
[4,5,3,2,1]
输出:true
模拟一遍;
对于每一个pushed序列元素,
对于栈有两个操作:
(1)当前元素入栈
(2)弹出栈。
如果弹出序列的当前元素与栈顶元素相同,则弹出;否则继续入栈。
首先压入第一个元素;
然后紧接着此时栈有两个操作? 弹出当前元素 或者 继续压入新的元素。
如何区分呢?
要看top与弹出序列的当前是否一致;如果一致,说明弹出了元素; 如果不一致,说明压入了新的元素。
接着再继续比较…
1 | class Solution { |
如果我们把二叉树视为一个图,父子节点之间的连线视为双向的,我们姑且定义为“举例”为两节点之间边的个数。写一个程序求一颗二叉树中相距最远的两个节点之间的距离(《编程之美》3.8)
该题目的技巧在于 全局变量的 应用。
需要克服的惯性思维:更多的题目的计算结果会作为递归函数的返回变量。
这个函数的返回值其实是root节点的高度。
在计算高度的过程中,顺便完成全局变量的更新。
1 | int GetMaxDistance(TreeNode* root) { |
输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。
从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
样例
给出二叉树如下所示,并给出num=22。
5
/ \
4 6
/ / \
12 13 6
/ \ / \
9 1 5 1
输出:[[5,4,12,1],[5,6,6,5]]
这里,我在下面的代码中写了很多种写法,我们需要好好体会以下几个点:
(1)我的return条件是什么?(是遇到空节点return还是遇到叶子结点return)
(2)我应该如何处理当前访问到的节点
(2)我应该如何还原现场(是pop一次还是pop两次)
(3)return条件与还原现场的关系是什么?
1 | /** |
给定一棵二叉树的其中一个节点,请找出中序遍历序列的下一个节点。
注意:
如果给定的节点是中序遍历序列的最后一个,则返回空节点;
二叉树一定不为空,且给定的节点一定不是空节点;
样例
假定二叉树是:[2, 1, 3, null, null, null, null], 给出的是值等于2的节点。
则应返回值等于3的节点。
解释:该二叉树的结构如下,2的后继节点是3。
2
/ \
1 3
需要我们明确中序遍历的定义
模拟题
其实模拟题就是分情况讨论。
1 | /** |
从上往下打印出二叉树的每个结点,同一层的结点按照从左到右的顺序打印。
样例
输入如下图所示二叉树[8, 12, 2, null, null, 6, null, 4, null, null, null]
8
/ \
12 2
/
6
/
4
输出:[8, 12, 2, 6, 4]
借助queue来实现层次遍历
1 | /** |
从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印到一行。
样例
输入如下图所示二叉树[8, 12, 2, null, null, 6, null, 4, null, null, null]
8
/ \
12 2
/
6
/
4
输出:[[8], [12, 2], [6], [4]]
相对上题,难点在于如何识别分行,一般而言我们有以下几种办法:
(1)使用nullptr作为分隔符,每次遇到nullptr进行分行处理,并压入新的nullptr。nullptr是当前层的终止符。
(2)借助size, 每次遍历size个元素,size为上一次压入的元素个数。通过size来代表该层有多少元素。
1 | /** |
请实现一个函数按照之字形顺序从上向下打印二叉树。
即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
样例
输入如下图所示二叉树[8, 12, 2, null, null, 6, 4, null, null, null, null]
8
/ \
12 2
/ \
6 4
输出:[[8], [2, 12], [6, 4]]
基于上题,在res加入元素之前,判定res是否奇数,如果是奇数的话,就reverse一下子level数组
1 | /** |
解答思路:
1 | typedef std::pair<int, TreeNode*> PIT; |