必须干掉这10道,面试100%遇到!(三)
0X05二叉树层序(锯齿)遍历
二叉树的遍历,对应力扣102,107,103.
详细分析:一次面试,被二叉树层序遍历打爆了
如果普通二叉树层序遍历,也不是什么困难的问题,但是它会有个分层返回结果的操作,就需要你详细考虑了。
很多人会用两个容器(队列)进行分层的操作,这里其实可以直接使用一个队列,我们首先记录枚举前队列大小len,然后根据这个大小len去枚举遍历就可以得到完整的该层数据了。
还有一个难点就是二叉树的锯齿层序(也叫之字形打印),第一趟是从左往右,第二趟是从右往左,只需要记录一个奇偶层数进行对应的操作就可以了。
这里就拿力扣103二叉树的锯齿形层序遍历作为题板给大家分享一下代码:
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> value=new ArrayList<>();//存储到的最终结果
if(root==null)
return value;
int index=0;//判断
Queue<TreeNode>queue=new ArrayDeque<>();
queue.add(root);
while (!queue.isEmpty()){
List<Integer>va=new ArrayList<>();//临时 用于存储到value中
int len=queue.size();//当前层节点的数量
for(int i=0;i<len;i++){
TreeNode node=queue.poll();
if(index%2==0)//根据奇偶 选择添加策略
va.add(node.val);
else
va.add(0,node.val);
if(node.left!=null)
queue.add(node.left);
if(node.right!=null)
queue.add(node.right);
}
value.add(va);
index++;
}
return value;
}
0X06 二叉树中后序遍历(非递归)
二叉树的非递归遍历也是考察的重点,对于中序后序遍历递归实现很简单,非递归实现起来还是要点技巧的哦。
详细分析:二叉树的各种遍历(递归、非递归)
对于二叉树的中序遍历,其实就是正常情况第二次访问该节点的时候才抛出输出(第一次数前序),这样我们枚举每个节点第一次不能删除,需要先将它存到栈中,当左子节点处理完成的时候在抛出访问该节点。
核心也就两步,叶子节点左右都为null,也可满足下列条件:
- 枚举当前节点(不存储输出)并用栈存储,节点指向左节点,直到左孩子为null。
- 抛出栈顶访问。如果有右节点,访问其右节点重复步骤1,如有没右节点,继续重复步骤2抛出。
实现代码为:
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer>value=new ArrayList<Integer>();
Stack<TreeNode> q1 = new Stack();
while(!q1.isEmpty()||root!=null)
{
while (root!=null) {
q1.push(root);
root=root.left;
}
root=q1.pop();//抛出
value.add(root.val);
root=root.right;//准备访问其右节点
}
return value;
}
}
而后序遍历按照递归的思路其实一般是第三次访问该节点是从右子节点回来才抛出输出,这个实现起来确实有难度。但是具体的实现,我们使用一个pre节点记录上一次被抛出访问的点,如果当前被抛出的右孩子是pre或者当前节点右为null,那么就将这个点抛出,否则说明它的右侧还未被访问需要将它"回炉重造",后面再用!如果不理解可以看前面的详细介绍。
具体实现的代码为:
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
TreeNode temp=root;//枚举的临时节点
List<Integer>value=new ArrayList<>();
TreeNode pre=null;//前置节点
Stack<TreeNode>stack=new Stack<>();
while (!stack.isEmpty()||temp!=null){
while(temp!=null){
stack.push(temp);
temp=temp.left;
}
temp=stack.pop();
if(temp.right==pre||temp.right==null)//需要弹出
{
value.add(temp.val);
pre=temp;
temp=null;//需要重新从栈中抛出
}else{
stack.push(temp);
temp=temp.right;
}
}
return value;
}
}
当然,后序遍历也有用前序(根右左)的前序遍历结果最后翻转一下的,但面试官更想考察的还是上面提到的方法。
0X07 跳台阶(斐波那契、爬楼梯)
爬楼梯、跳台阶是一个经典问题,对应剑指offer10和力扣70题,题目的要求为:
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?注意:给定 n 是一个正整数。
分析:
这个问题入门级别dp,分析当前第k阶的结果,每个人可以爬1个或者2个台阶,那么说明它可能是由k-1或者k-2来的,所以就是两个子情况的叠加(需要特殊考虑一下初始情况),这个思路有人会想到递归,没错用递归确实可以解决但是用递归效率较低(因为这个是个发散的递归一个拆成两个),使用记忆化搜索会稍微好一些。
但是dp是比较好的方法,核心状态转移方程为:dp[i]=dp[i-1]+dp[i-2],有些空间优化的那就更好了,因为只用到前两个值,所以完全可以用三个值重复使用节省空间。
class Solution {
public int climbStairs(int n) {
if(n<3)return n;
int dp[]=new int[n+1];
dp[1]=1;
dp[2]=2;
for(int i=3;i<n+1;i++)
{
dp[i]=dp[i-1]+dp[i-2];
}
return dp[n];
}
public int climbStairs(int n) {
int a = 0, b = 0, c = 1;
for (int i = 1; i <= n; i++) {
a = b;
b = c;
c = a + b;
}
return c;
}
}
当然,有的数据很大求余的跳台阶,可以用矩阵快速幂解决,但是这里就不介绍啦,有兴趣可以详细看看。
文章转自公众号:bigsai