[ 数据结构 ] 二叉树详解--------前序、中序、后序、存储、线索化

news2024/11/19 7:42:08

0 前言

  1. why?为什么需要树结构?

    数组虽然查找快(通过下标访问),但无法动态扩容(拷贝到新的数组),而链表相反,树结构刚好结合两者优点

  2. 浅谈树?

    树的存储和读取效率都很高,比如二叉排序树,理解树的抽象模型有助于理解递归的原理,树的模型接近于单向链表,父节点指向左右子树,而链表相比二叉树可以看成单叉

1 初识二叉树

  1. 遍历:分为前、中、后序,区分前中后简单说就是对当前节点的处理操作(打印)是在左右子节点的递归调用的前面、中间、还是后面,比如前序:先打印当前节点,再分别左右子节点递归调用
  2. 查找:同样有前、中、后序,取决于判断操作是在两个递归调用的前面、中间、后面,当然,如果到方法的最后(搜索到叶子节点)都没能找到目标,需要返回null(结果需要在判断之后立即返回,不可以先收集再在末尾返回)
  3. 删除:先找再删,这里的找不同于2中的查找,判断操作是在当前节点判断子节点是否要删除,毕竟只能通过父节点的指针删除,而不能自己删除自己

image-20230108112820521.png

//二叉树的前序、中序、后序遍历,前中后序查找,删除三类方法
//主类+树类+节点类
public class Tree01_BinaryTreeDemo {
    public static void main(String[] args) {

        //手动创建二叉树
        BinaryTree tree = new BinaryTree();
        Hero root  = new Hero(1, "宋江");
        Hero node2 = new Hero(2, "吴用");
        Hero node3 = new Hero(3, "卢俊义");
        Hero node4 = new Hero(4, "林冲");
        Hero node5 = new Hero(5, "关胜");
        Hero node8 = new Hero(8, "晁盖");
        Hero node7 = new Hero(7, "武松");

        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        node2.setLeft(node8);
        node2.setRight(node7);
        tree.setRoot(root);

        //遍历
        tree.preOrder();//12354
//        tree.infixOrder();//21534
//        tree.postOrder();//25431

        //查找
        tree.postOrderSearch(4);

        //删除
//        tree.delNode(3);
//        System.out.println("删除后:");
//        tree.preOrder();
    }
}

//二叉树
class BinaryTree {
    private Hero root;

    public void setRoot(Hero root) {
        this.root = root;
    }

    //三种方式遍历树,从root起
    public void preOrder() {
        if (root != null) {
            root.preOrder();
        } else {
            System.out.println("二叉树为空");
        }
    }
    public void infixOrder() {
        if (root != null) {
            root.infixOrder();
        } else {
            System.out.println("二叉树为空");
        }
    }
    public void postOrder() {
        if (root != null) {
            root.postOrder();
        } else {
            System.out.println("二叉树为空");
        }
    }

    //三种方式查找树,从root起
    public void preOrderSearch(int no) {
        if (root != null) {
            if (root.preOrderSearch(no) != null) {
                System.out.println("查找结果为" + root.preOrderSearch(no));
            } else {
                System.out.println("没找到!!!");
            }
        } else {
            System.out.println("二叉树为空");
        }
    }
    public void infixOrderSearch(int no) {
        if (root != null) {
            if (root.infixOrderSearch(no) != null) {
                System.out.println("查找结果为" + root.infixOrderSearch(no));
            } else {
                System.out.println("没找到!!!");
            }
        } else {
            System.out.println("二叉树为空");
        }
    }
    public void postOrderSearch(int no) {
        if (root != null) {
            Hero result= root.postOrderSearch(no);
            if (result != null) {
                System.out.println("查找结果为" + result);
            } else {
                System.out.println("没找到!!!");
            }
        } else {
            System.out.println("二叉树为空");
        }
    }

    //删除节点
    public void delNode(int no) {
        if (root == null) {
            System.out.println("二叉树空,无法删除");
        } else {
            //删除的节点就是root,直接置空,否则遍历删除
            if (root.getNo() == no) {
                root = null;
            } else {
                root.delNode(no);
            }
        }
    }
}

//树节点
//编写三种遍历方式
class Hero {
    private int no;
    private String name;
    private Hero left;
    private Hero right;

    public Hero(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Hero getLeft() {
        return left;
    }

    public void setLeft(Hero left) {
        this.left = left;
    }

    public Hero getRight() {
        return right;
    }

    public void setRight(Hero right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }

    //前序遍历
    public void preOrder() {
        System.out.println(this);
        if (left != null) {
            left.preOrder();
        }
        if (right != null) {
            right.preOrder();
        }
    }
    //中序遍历
    public void infixOrder() {
        if (left != null) {
            left.infixOrder();
        }
        System.out.println(this);
        if (right != null) {
            right.infixOrder();
        }
    }
    //后序遍历
    public void postOrder() {
        if (left != null) {
            left.postOrder();
        }
        if (right != null) {
            right.postOrder();
        }
        System.out.println(this);
    }

    //前序查找
    //这里当前节点的查找结果毫无疑问若正确可以直接返回
    //子节点的查找结果必须先收集再判断,若正确直接返回,为空则在方法末尾返回
    //  =>如果不立即判断返回则当查找到第一个叶子节点直接必出结果,即路径上遇到结果就返回,到叶子都没遇到就返回null了
    //整体判断逻辑:当前节点能否返回(能否返回:找到正确结果才返回)-->左递归能否返回-->右递归直接返回-->补上方法返回值null
    //总结:以前序为例,无论是遍历还是查找,对于当前节点的操作其实只是左递归前的附带操作,
    //      因此从执行结果来看,整体是先从上往下执行,(中+左)=>(中+左)=>(中+左)=>叶子,然后开始从下层处理到上层
    public Hero preOrderSearch(int no) {

        if (this.no == no) {
            return this;
        }

        if (left != null) {
            if (left.preOrderSearch(no) != null) {
                return left.preOrderSearch(no);
            }
        }

        if (right != null) {
            return right.preOrderSearch(no);
        }

        return null;
    }
    //中序查找
    public Hero infixOrderSearch(int no) {

        if (left != null) {
            if (left.infixOrderSearch(no) != null) {
                return left.infixOrderSearch(no);
            }
        }
        if (this.no == no) {
            return this;
        }
        if (right != null) {
            if (right.infixOrderSearch(no) != null) {
                return right.infixOrderSearch(no);
            }
        }
        return null;
    }
    //后序查找
    public Hero postOrderSearch(int no) {
        Hero result = null;

        if (left != null) {
            result = left.postOrderSearch(no);
            if (result != null) {
                return result;
            }
        }
        if (right != null) {
            result = right.postOrderSearch(no);
            if (result != null) {
                return result;
            }
        }

        //统计比较多少次,以后序查找为例,
        //注意为了得到正确的比较次数,需要将递归结果先收集再做判断或返回,否则次数会翻倍
        System.out.println("一次");
        if (this.no == no) {
            return this;
        }
        return result;
    }

    //删除节点
    //核心:在当前节点判断是否删除子节点(因为单向二叉树只能操作自己的左右指针),注意根节点的判断在树类中
    //若删除了子节点,则直接返回结束方法(递归),否则调用递归删除
    public void delNode(int no) {
        if (this.left != null && this.left.no == no) {
            this.left = null;
            return;
        }
        if (this.right != null && this.right.no == no) {
            this.right = null;
            return;
        }
        if (this.left != null) {
            this.left.delNode(no);
        }
        if (this.right != null) {
            this.right.delNode(no);
        }
    }
}

2 顺序存储二叉树

说明:

  1. 顺序存储二叉树其实就是将数组转换成树,也可以说是将树节点存放在数组中
  2. 根节点表示下标为0的数组元素,那么下标为n的节点,它的左子节点下标(2 * n + 1),右子节点(2 * n - 1),父节点(n-1) / 2.
  3. 按要求,顺序二叉树需满足完全二叉树(叶子节点在最下面两层,最后一层左侧连续)

image-20230108111944477.png

//顺序存储二叉树(数组和树可相互转换,这里用数组存放二叉树节点)
//数组存储元素顺序是按树层序遍历顺序......
public class Tree02_ArrBinaryTreeDemo {
    public static void main(String[] args) {
        int[] arr = { 1, 2, 3, 4, 5, 6, 7 };
        ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);
        arrBinaryTree.preOrder();

    }
}

//树
class ArrBinaryTree {
    private int[] arr;

    public ArrBinaryTree(int[] arr) {
        this.arr = arr;
    }

    //重载
    public void preOrder() {
        preOrder(0);
    }

    //前序遍历
    //核心:父子节点间的索引计算等式,左子节点2n+1,右子节点2n+2,父节点(n-1)/2
    //递归终止条件为索引越界arr.length,当然给递归调用加if判断也可以终止调用
    public void preOrder(int index) {
        if (arr == null || arr.length == 0) {
            System.out.println("数为空,无法遍历!");
            return;
        }
        if (index >= 0 && index < arr.length) {
            System.out.println(arr[index]);
        } else {
            return;
        }

        preOrder(2 * index + 1);
        preOrder(2 * index + 2);

    }
}

3 线索化二叉树

image-20230108105115964.png

  1. 问题:先看上图,数列 {1, 3, 6, 8, 10, 14 } 构建成一颗二叉树,节点数n=6,每个节点两个指针计算,满打满算2n个指针,但是除去根节点只有n-1个指针指向已有节点,算下来就有2n-(n-1)=n+1个空指针,怎么用这些个空指针呢?
  2. 以中序遍历为例,如果遍历时利用这些空指针按规则指向中序遍历结果的相邻节点(前驱节点/后继节点),那么就会将树中序线索化,也就有了后面新的遍历方式(线型遍历)
  3. 线型遍历:利用线索二叉树叶子节点连成的线索,以非递归的方式遍历树(有点像链表)
  4. 线索化代码核心:定义全局变量pre用来存放中序遍历的前驱节点(毕竟是单向的二叉树,无法在当前节点让前驱节点指向自己),通过pre节点和当前节点(两节点形成前驱-后继关系)来改变原来空指针的指向

image-20230108111712367.png

//二叉树的中序线索化及其线型遍历
public class Tree03_ThreadedBinaryTreeDemo {
    public static void main(String[] args) {

        //手动创建二叉树
        Ho root = new Ho(1, "tom");
        Ho node2 = new Ho(3, "jack");
        Ho node3 = new Ho(6, "smith");
        Ho node4 = new Ho(8, "mary");
        Ho node5 = new Ho(10, "king");
        Ho node6 = new Ho(14, "dim");

        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);

        ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
        threadedBinaryTree.setRoot(root);

        //测试
        threadedBinaryTree.threadedNode();
//        System.out.println(node5.getLeft().toString());
//        System.out.println(node5.getRight().toString());

        threadedBinaryTree.threadedList();


    }
}

//二叉树
class ThreadedBinaryTree {
    private Ho root;
    private Ho pre = null;//前驱节点

    public void setRoot(Ho root) {
        this.root = root;
    }

    //三种方式遍历树,从root起
    public void preOrder() {
        if (root != null) {
            root.preOrder();
        } else {
            System.out.println("二叉树为空");
        }
    }
    public void infixOrder() {
        if (root != null) {
            root.infixOrder();
        } else {
            System.out.println("二叉树为空");
        }
    }
    public void postOrder() {
        if (root != null) {
            root.postOrder();
        } else {
            System.out.println("二叉树为空");
        }
    }

    //三种方式查找树,从root起
    public void preOrderSearch(int no) {
        if (root != null) {
            if (root.preOrderSearch(no) != null) {
                System.out.println("查找结果为" + root.preOrderSearch(no));
            } else {
                System.out.println("没找到!!!");
            }
        } else {
            System.out.println("二叉树为空");
        }
    }
    public void infixOrderSearch(int no) {
        if (root != null) {
            if (root.infixOrderSearch(no) != null) {
                System.out.println("查找结果为" + root.infixOrderSearch(no));
            } else {
                System.out.println("没找到!!!");
            }
        } else {
            System.out.println("二叉树为空");
        }
    }
    public void postOrderSearch(int no) {
        if (root != null) {
            Ho result= root.postOrderSearch(no);
            if (result != null) {
                System.out.println("查找结果为" + result);
            } else {
                System.out.println("没找到!!!");
            }
        } else {
            System.out.println("二叉树为空");
        }
    }

    //删除节点
    public void delNode(int no) {
        if (root == null) {
            System.out.println("二叉树空,无法删除");
        } else {
            //删除的节点就是root,直接置空,否则遍历删除
            if (root.getNo() == no) {
                root = null;
            } else {
                root.delNode(no);
            }
        }
    }

    //中序线索化
    //切入点:线索化无非就是改变左右指向,从null改为指向前驱和后继节点,但是遍历到当前节点时只经过了前驱节点,不可能拿到后继节点,所以...
    //中序遍历的基本思想,处理当前节点时:若需要线索化则改变当前节点的左指向和类型(完成前驱线索化),
    //同时通过改变前驱节点的右指向和类型(完成后继线索化),2步处理完更新前驱节点
    //线索化后再遍历会死龟!!!
    public void threadedNode(Ho node) {
        if (node == null) {
            return;
        }

        threadedNode(node.getLeft());
        if (pre !=  null&&pre.getRight()==null) {
            pre.setRight(node);
            pre.setRightType(1);
        }
        if (node.getLeft() == null) {
            node.setLeft(pre);
            node.setLeftType(1);
        }
        pre = node;

        threadedNode(node.getRight());
    }
    //中序线索化方法重载
    public void threadedNode() {
        this.threadedNode(root);
    }

    //线型方式遍历(非递归),实现线索化二叉树的中序遍历
    //逻辑:左子树上寻找线索化节点并打印,持续输出后继节点,没有后继节点节点就取右子树后找线索化节点-->循环
    //宏观上:就是寻找线索化节点和它的后继节点
    public void threadedList() {
        Ho node = root;
        while (node!=null) {
            //找到线索化节点并打印8,打印8
            //找到线索化节点并打印10,打印10
            //找到线索化节点并打印14,打印14
            while (node.getLeftType()==0) {
                node = node.getLeft();
            }
            System.out.println(node);
            //后继节点打印3
            //后继节点打印1
            //后继节点打印6
            while ( node.getRightType() == 1) {
                node = node.getRight();
                System.out.println(node);
            }
            //右子节点10
            //右子节点6
            //右子节点null,退出
            node = node.getRight();
        }
    }
}

//树节点
//编写三种遍历方式
class Ho {
    private int no;
    private String name;
    private Ho left;
    private Ho right;
    //新增指针类型,0表示子树,1表示前驱/后继节点,问:有啥用?答:遍历防止死龟
    private int leftType;
    private int rightType;

    public Ho(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Ho getLeft() {
        return left;
    }

    public void setLeft(Ho left) {
        this.left = left;
    }

    public Ho getRight() {
        return right;
    }

    public void setRight(Ho right) {
        this.right = right;
    }

    public int getLeftType() {
        return leftType;
    }

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public int getRightType() {
        return rightType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }

    @Override
    public String toString() {
        return "HoNode [no=" + no + ", name=" + name + "]";
    }

    //前序遍历
    public void preOrder() {
        System.out.println(this);
        if (left != null) {
            left.preOrder();
        }
        if (right != null) {
            right.preOrder();
        }
    }
    //中序遍历,线索化后的
//    public void infixOrder() {
//        if (left != null&& leftType==0) {
//            left.infixOrder();
//        }
//        System.out.println(this);
//        if (right != null&& rightType==0) {
//            right.infixOrder();
//        }
//    }
    //中序遍历
    public void infixOrder() {
        if (left != null) {
            left.infixOrder();
        }
        System.out.println(this);
        if (right != null) {
            right.infixOrder();
        }
    }
    //后序遍历
    public void postOrder() {
        if (left != null) {
            left.postOrder();
        }
        if (right != null) {
            right.postOrder();
        }
        System.out.println(this);
    }

    //前序查找
    //这里当前节点的查找结果毫无疑问若正确可以直接返回
    //子节点的查找结果必须先收集再判断,若正确直接返回,为空则在方法末尾返回
    //  =>如果不立即判断返回则当查找到第一个叶子节点直接必出结果,即路径上遇到结果就返回,到叶子都没遇到就返回null了
    //整体判断逻辑:当前节点能否返回(能否返回:找到正确结果才返回)-->左递归能否返回-->右递归直接返回-->补上方法返回值null
    //总结:以前序为例,无论是遍历还是查找,对于当前节点的操作其实只是左递归前的附带操作,
    //      因此从执行结果来看,整体是先从上往下执行,(中+左)=>(中+左)=>(中+左)=>叶子,然后开始从下层处理到上层
    public Ho preOrderSearch(int no) {

        if (this.no == no) {
            return this;
        }

        if (left != null) {
            if (left.preOrderSearch(no) != null) {
                return left.preOrderSearch(no);
            }
        }

        if (right != null) {
            return right.preOrderSearch(no);
        }

        return null;
    }
    //中序查找
    public Ho infixOrderSearch(int no) {

        if (left != null) {
            if (left.infixOrderSearch(no) != null) {
                return left.infixOrderSearch(no);
            }
        }
        if (this.no == no) {
            return this;
        }
        if (right != null) {
            if (right.infixOrderSearch(no) != null) {
                return right.infixOrderSearch(no);
            }
        }
        return null;
    }
    //后序查找
    public Ho postOrderSearch(int no) {
        Ho result = null;

        if (left != null) {
            result = left.postOrderSearch(no);
            if (result != null) {
                return result;
            }
        }
        if (right != null) {
            result = right.postOrderSearch(no);
            if (result != null) {
                return result;
            }
        }

        //统计比较多少次,以后序查找为例,
        //注意为了得到正确的比较次数,需要将递归结果先收集再做判断或返回,否则次数会翻倍
        System.out.println("一次");
        if (this.no == no) {
            return this;
        }
        return result;
    }

    //删除节点
    //核心:在当前节点判断是否删除子节点(因为单向二叉树只能操作自己的左右指针),注意根节点的判断在树类中
    //若删除了子节点,则直接返回结束方法(递归),否则调用递归删除
    public void delNode(int no) {
        if (this.left != null && this.left.no == no) {
            this.left = null;
            return;
        }
        if (this.right != null && this.right.no == no) {
            this.right = null;
            return;
        }
        if (this.left != null) {
            this.left.delNode(no);
        }
        if (this.right != null) {
            this.right.delNode(no);
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/147928.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【因果发现】 针对时序数据的因果图学习

文章目录 一、任务二、数据集说明三、专业名词CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 一、任务 This track focuses on solving a causal structure learning problem in AIOps. AIOps 相关:主要目标是从事件序列数据中挖掘因果图关系,并辅助定位根因。 主要需要解…

php学习笔记-php数组的创建和使用,数组常用函数-day03

php数组的创建和使用&#xff0c;数组常用函数数组的概念一维数组的创建和使用1.直接将变量声明为数组元素2.通过array()函数来创建一维数组3.数组的元素值的访问4.数组元素的赋值方式5.数组的注意事项二维数组的创建和使用1.二维数组的创建2.二维数组的数组元素值访问3.二维元…

ArcGIS基础实验操作100例--实验67设置标注样式

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验67 设置标注样式 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&…

Java+Swing可视化图像处理软件

JavaSwing可视化图像处理软件一、系统介绍二、功能展示1.图片裁剪2.图片缩放3.图片旋转4.图像灰度处理5.图像变形6.图像扭曲7.图像移动三、项目相关3.1 乱码问题3.2 如何将GBK编码系统修改为UTF-8编码的系统&#xff1f;四、其它1.其他系统实现2.获取源码一、系统介绍 该系统实…

Allegro174版本新功能介绍之和172版本兼容设置

Allegro174版本新功能介绍之和172版本兼容设置 Allegro升级到了174版本的时候,如果按照常规操作用174版本保存PCB之后,用172版本是无法打开的。 但是174版本开放了一个和172版本兼容的窗口,即便是174版本保存了PCB,172同样还是能够打开 具体操作如下 选择Setup选择User p…

[Linux]git命令行

&#x1f941;作者&#xff1a; 华丞臧. &#x1f4d5;​​​​专栏&#xff1a;【LINUX】 各位读者老爷如果觉得博主写的不错&#xff0c;请诸位多多支持(点赞收藏关注)。如果有错误的地方&#xff0c;欢迎在评论区指出。 推荐一款刷题网站 &#x1f449; LeetCode刷题网站 文…

前端入门笔记 05 —— js基础

基于ES6 生成html文件&#xff0c;script标签引入js文件 简单语句直接用console面板 <!DOCTYPE html> <html><head><title>js核心语法</title><meta charset"UTF-8" /></head><body><script src "./1.5j…

多线程(4)

文章目录1.单例模式2. 阻塞队列3. 定时器4.线程池前言 :   前面的一些文章&#xff0c;我们已经将有关多线的基础知识了解了&#xff0c; 下面我们来写一些关于多线程的代码案例&#xff0c;来强化对多线程的理解&#xff0c;另外通过这些案例补充一下其他方面的知识。 1.单例…

印度如何在云计算中抓住千载难逢的机会

云对印度的影响大流行和后大流行时期的虚拟和混合世界越来越依赖于云&#xff0c;并随之提高灵活性、敏捷性和创新性。在这场快速的数字化转型中&#xff0c;印度企业也纷纷加入云解决方案以促进业务增长。NASSCOM-EY的调查显示&#xff0c;78%的印度IT公司、53%的医疗保健和BF…

【Javascript】数字和字符串常用方法

数字常用方法&#xff1a; toFixed&#xff1a;保留小数位数&#xff0c;不够自动补0. 注意&#xff1a;返回值是字符串&#xff0c;不能和数字直接相加减&#xff0c;需要把字符串转化为数字&#xff1a;-0即可 取整&#xff1a; // round:四舍五入取整console.log(Math.roun…

SQL IN 操作符

IN 操作符 IN 操作符允许您在 WHERE 子句中规定多个值。 SQL IN 语法 SELECT column1, column2, ... FROM table_name WHERE column IN (value1, value2, ...); 参数说明&#xff1a; column1, column2, ...&#xff1a;要选择的字段名称&#xff0c;可以为多个字段。如果…

STM32——USART串口

文章目录一、通信接口二、串口通信三、硬件电路四、电平标准五、串口参数及时序六、STM32的USART外设简介七、USART框图八、USART基本结构九、数据帧十、起始位侦测和采样位置对齐十一、数据采样十二、波特率发生器十三、数据模式十四、串口发送电路设计关键代码USART_SendData…

基于Java+Swing+mysql眼镜店管理系统

基于JavaSwingmysql眼镜店管理系统一、系统介绍二、功能展示1.管理员登陆2.眼镜信息查询3.眼镜信息添加4.眼镜信息修改5.眼镜信息删除三、项目相关3.1 乱码问题3.2 如何将GBK编码系统修改为UTF-8编码的系统&#xff1f;四、其它1.其他系统实现2.获取源码一、系统介绍 该系统实…

Linux常用命令——find命令

在线Linux命令查询工具 find 在指定目录下查找文件 补充说明 find命令用来在指定目录下查找文件。任何位于参数之前的字符串都将被视为欲查找的目录名。如果使用该命令时&#xff0c;不设置任何参数&#xff0c;则find命令将在当前目录下查找子目录与文件。并且将查找到的子…

js设计模式(二)-创建型模式

创建型设计模式介绍 在软件工程中&#xff0c;创建型模式是处理对象创建的设计模式&#xff0c;试图根据实际情况使用合适的方式创建对象。基本的对象创建方式可能会导致设计上的问题&#xff0c;或增加设计的复杂度。创建型模式通过以某种方式控制对象的创建来解决问题。 创…

GCN-LSTM 预测出租车速度 英文 Taxi Speed Prediction Using GCN-LSTM

GCN-LSTM模型预测出租车速度 GCN&#xff1a;又称GNN&#xff0c;图神经网络 LSTM&#xff1a;长短时记忆网络 可做学习参考 Summary One of the most valuable findings in engineering is the determination of taxi speed. Since the GCN-LSTM program software can r…

BOM(一):window对象常见事件、定时器

BOM&#xff08;一&#xff09;BOM介绍window 对象的常见事件定时器location 对象navigator 对象history 对象BOM介绍 BOM是浏览器对象模型&#xff0c;它提供了独立于内容而与浏览器窗口进行交互的对象&#xff0c;其核心对象是 window。 BOM的构成 window 对象的常见事件 1.…

Python|每日一练|斐波那契数列|优化算法|迭代|动态计算|排列组合|时间复杂度:走楼梯

题目名称&#xff1a;走楼梯 时间限制&#xff1a;1000ms内存限制&#xff1a;256M 题目描述 现在有一截楼梯&#xff0c;根据你的腿长&#xff0c;你一次能走 1 级或 2 级楼梯&#xff0c;已知你要走 n 级楼梯才能走到你的目的楼层&#xff0c;请实现一个方法&#xff0c;计…

进程状态|操作系统|什么是pcb|什么是僵尸进程 |什么是孤儿进程 【超详细的图文解释】【Linux OS】

说在前面 今天给大家带来操作系统中进程状态的详解。 本篇博主将通过从进程状态的广泛概念&#xff0c;深入到Linux操作系统详细的一些进程状态。在解释进程状态的过程中&#xff0c;博主还会穿插一些操作系统一些重要概念&#xff01;本篇干货满满&#xff0c;请大家不要吝啬…

新建的普通用户无法使用sudo的问题

文章目录1.为什么新建用户无法使用sudo指令1. 查看sudoers的权限2. 切换成root用户解决问题1. 切换root用户2.进入 vim编辑器3. 配置成功1.为什么新建用户无法使用sudo指令 [lynVM-8-8-centos ~]$ sudo ls [sudo] password for lyn: lyn is not in the sudoers file. This i…