刷题手帐之剑指offer-1

为了应对面试(字节跳动问的一些基础题都没做出来,第二天给拒绝了说来都是眼泪),以及接下来的笔试,我觉得有必要把剑指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( ̄▽ ̄)ブ

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值