为了应对面试(字节跳动问的一些基础题都没做出来,第二天给拒绝了说来都是眼泪),以及接下来的笔试,我觉得有必要把剑指offer里面的题目从头刷个一遍。
4天时间刷了二十多个题。难度的话感觉相比leetcode一些题还是比较简单的。但是在面试的时候可能会更多考虑一个简单题不同考量下的实现,比如省时间省内存这样。这种题我以后可能会归纳一下,这里主要记录一些乍看没思路,看完题解恍然大悟或者看完题解依旧一知半解的题目。
重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
这题考察的是二叉树遍历的一个基本功
(前序遍历)根=>左=>右
(中序遍历) 左=>根=>右
(后序遍历) 右=>左=>根
解决的核心思想是"寻根"
前序遍历和后序遍历都是可以直接找到根节点位置的,前序遍历根节点为第一个,后序遍历根节点在最后一个。
找到根节点以后,要根据根节点在中序遍历中的位置去确定其左右节点位置和左右子树的范围。
同理,其左右节点位置确定以后,进行递归寻找左右节点的左右节点。在寻找过程中要注意边界条件,判断边界条件就是左右子树范围。详细过程就不赘述了,直接撸代码。不清楚遍历的话可以看看B站小黑的二叉树遍历讲解4分钟弄懂。
import java.util.*;
public class Solution {
HashMap<Integer,Integer> hm =new HashMap<>();/
int[] po;
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
po=pre; //储存前序遍历结果
for(int i=0;i<in.length;i++){
hm.put(in[i],i); //哈希表用于储存中序遍历元素的目录
}return recur(0,0,in.length-1);
}
TreeNode recur(int pre, int left,int right)//(到达节点前序遍历位置,到达子树左右边界)
{
if(left>right) return null; //左边界大于有边界即该子树往下为空
int i = hm.get(po[pre]); //读取到达节点对应的中序遍历位置
TreeNode root= new TreeNode(po[pre]);//构建对应节点
root.left=recur(pre+1,left,i-1); //左节点递归
root.right=recur(i+pre-left+1,i+1,right);//右节点递归
return root;
}
}
树的子结构
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
思路:遍历二叉树A,寻找到A中B的根所在位置。
若找到,则判断是否子树。
判断是否子树,AB以给定结点开始以同样顺序遍历。
只要找到一个不同节点或者在A到达底部而B没到达的时候返回错误。
若能正常遍历完B,则B判断为A的子树。
public class Solution {
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
if(root2==null) return false;
if(root1==null&&root2!=null) return false;
boolean tag=false;
if(root1.val==root2.val) tag= IsSubtree(root1,root2);//寻找B的根节点
if(!tag){
tag=HasSubtree(root1.left,root2);//遍历左子树寻找B的根节点
if(!tag){
tag=HasSubtree(root1.right,root2);//遍历右子树寻找B的根节点
}
}
return tag;
}
public boolean IsSubtree(TreeNode root1,TreeNode root2){
if(root2==null) return true;//B树遍历完毕,判断为A的子树
if(root1==null&&root2!=null) return false;//B没遍历到底部而A遍历到了底部,判断B不为子树
if(root1.val==root2.val) //目前节点一致,对左子树右子树进行遍历
return isSubtree(root1.left,root2.left)&&IsSubtree(root1.right,root2.right);
elseB
return false;//该节点不一致,判断B不为子树
}
}
顺时针打印矩阵
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
思路挺简单,就是每次打印一圈,只要找对要打印的圈数,就可以一个循环语句完成。只是开始脑抽了没想到,看到答案感觉怎么能这么简单哈哈哈
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
ArrayList<Integer> result = new ArrayList<>();
int[][] array = matrix;
int n=matrix.length;
int m=matrix[0].length;
int layers = (Math.min(m,n)-1)/2+1; //定义要扫面的圈数
for(int i=0;i<layers;i++){
for(int k =i;k<m-i;k++) result.add(array[i][k]);
for(int j=i+1;j<n-i;j++) result.add(array[j][m-i-1]);
for(int l=m-i-2;l>=i&&(n-i-1)!=i;l--) result.add(array[n-i-1][l]);
for(int p=n-i-2;p>=i+1&&(m-i-1)!=i;p--) result.add(array[p][i]);
}
return result;
}
}
从上往下打印二叉树
从上往下打印出二叉树的每个节点,同层节点从左至右打印
思路:使用一个节点的Arraylist来实现队列功能。
先把根节点加到该队列,通过判断队列是否为空,循环弹出队列第一个,同时向队列加入弹出元素的左右节点(若判断不为空),最后打印出弹出元素的值并进入下一循环。
import java.util.ArrayList;
public class Solution {
//rayList<Integer> al = new ArrayList<>();
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> re = new ArrayList<>();
ArrayList<TreeNode> queue = new ArrayList<>();//创建ArrayList来实现队列
if(root==null)
return re;
queue.add(root);
while(queue.size()!=0)
{
TreeNode tmp=queue.remove(0);//弹出队列第一个
if(tmp.left!=null)
queue.add(tmp.left);
if(tmp.right!=null)
queue.add(tmp.right);
re.add(tmp.val);
}
return re;
}
}
二叉树后序遍历
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
思路:后序遍历输出的结构: 左子树| 右子树| 根节点
右节点在根节点前一个很好找
从最后往前遍历,只要找到有小于根节点值的数,对应则为左节点。
左节点前的数全部都小于根节点,出现不符合的则返回false。
将此以递归形式作用与左右节点已形成递归。
public class Solution {
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence.length==0)
return false;
if(sequence.length==1)
return true;
return check(sequence,0,sequence.length-1); //检查函数
}
public boolean check(int[] s,int start,int root)//数组,节点,以节点为根节点对应子树的边界
{
if(start>root) return true;//边界大于根节点位置,超出返回
int i = root;
while(i>start&&s[i-1]>s[root]) //找到左节点位置
i--;
for(int j =start;j<i-1;j++) //判断左子树是否成立
{
if(s[j]>=s[root])
return false;
}
return check(s,start,i-1)&&check(s,i,root-1);//检查左右子树是否合法
}
}
二叉树和为某值的路径
输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)
创建两个ArrayList:
变脸往下寻找,寻找过程往ArrayList 加入寻访过的节点。
ArrayList<ArrayList<Integer>> Path = new ArrayList<>();
ArrayList<Integer> list = new ArrayList<>();
一个用来存储正在进行的路径,一个储存成功的路径;
public class Solution {
ArrayList<ArrayList<Integer>> Path = new ArrayList<>();
ArrayList<Integer> list = new ArrayList<>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root==null) return Path; //遍历到底部,返回
target-=root.val; //目标数值减去探访节点数值
list.add(root.val); //储存该节点到当前路径
if(target==0&&root.left==null&root.right==null) //若数值减为零,说明路径成立
Path.add(new ArrayList<Integer>(list));//路径储存
FindPath(root.left,target); //查看左右路径
FindPath(root.right,target);
list.remove(list.size()-1);//返回父母节点
return Path;
}
}
题目来自牛客网的在线编程的《剑指offer》题库。
部分代码可能与提交的雷同。
侵匿o( ̄▽ ̄)ブ