zoukankan      html  css  js  c++  java
  • D10-树[Java数据结构和算法]

    1.数组、链表和树存储方式分析

      1.1数组的存储方式

      (1)优点:通过下表当时访问元素,速度快。对于有序数组,还可以使用二分查找提高检索速度

      (2)缺点:如果检索具体某个值,或者插入值(按照一定顺序)会整体移动,效率较低

      1.2 链式存储方式的分析

      (1)优点:在一定程度上对数组存储方式有优化(例如:插入一个数值节点,只需要将插入节点,连接到链表中即可,删除效率也很好)

      (2)缺点:在进行检索时,效率仍然比较低,比如(检索某个值,需要从头节点开始遍历)

      1.3 树存储方式的分析

      能够提高数据存储,读取的效率,比如利用二叉排序树(binary Sort Tree),既可以保证数据的检索速度,同时也可以保证数据的插入,删除,修改的速度。

    2.二叉树的概念和常用术语

       2.1 二叉树的概念

      (1)每个节点最多只能有两个子节点的一种形式

      (2)二叉树的子节点分为左节点和右节点

      (3)如果该二叉树的所有叶子节点都在最后一层,并且节点总数=2^n-1,n为层数时,称为满二叉树

      (4)如果该二叉树的所有叶子节点都在最后一层或倒数第二层,而且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续,称为完全二叉树

      2.2 二叉树的遍历

      (1)前序遍历:先输出父节点,再遍历左子树和右子树;

      (2)中序遍历:先遍历左子树,再输出父节点,再遍历右子树;

      (3)后序遍历:先遍历左子树,再遍历右子树,最后输出父节点

      (4)分析二叉树的前序,中序,后序的遍历步骤

        1)创建一颗二叉树

        2) 前序遍历

          2.1)先输出当前节点;(初始的时候事root节点)

          2.2)如果左子节点不为空,则递归继续前序遍历;

          2.3)如果右子节点不为空,则递归继续前序遍历

        3)中序遍历

          3.1)如果当前节点的左子节点不为空,则递归中序遍历;

          3.2)输出当前节点;

          3.3)如果当前节点的右子节点不为空,则递归中序遍历;

        4)后序遍历

          4.1)如果当前节点的左子节点不为空,则递归后序遍历;

          4.2)如果当前节点的右子节点不为空,则递归后序遍历;

          4.3)输出当前节点;

      2.3 二叉树的前序,中序,后序的查找步骤

      (1)前序查找

        1)先判断当前节点的no是否等于要查找的;

        2)如果相等,返回当前节点;

        3)如果不相等,则判断当前节点的左子节点是否为空,如果不为空,则递归前序查找;

        4)如果左递归前序查找,找到节点则返回,否则继续判断,当前节点的右子节点是否为空,如果不为空,则继续向右递归前序查找;

      (2)中序查找

        1)判断当前节点的左子节点是否为空,如果不为空,则递归中序查找;

        2)如果找到就返回,如果没有找到就和当前节点比较,如果是则返回当前节点,否则继续进行右递归的中序查找;

        3)如果右递归中序查找,找到就返回,否则就返回null;

      (3)后序查找

        1)判断当前节点的左子节点是否为空,如果不为空就继续向左后序查找;

        2)如果找到就返回,没有找到就判断当前节点的右子节点是否为空,如果不为空就右递归进行后序查找,如果找到就返回;

        3)就和当前节点进行比较,如果是则返回,否则返回null;

      2.4 二叉树删除节点

      (1)要求

        1)如果删除的节点是叶子节点,则删除该节点;

        2)如果删除的节点是非叶子节点,则删除该子树;

        3)测试,删除掉5号叶子节点和3号子树;

      (2)删除思路分析

        1)因为二叉树是单向的,因此判断的是当前节点的子节点是否需要删除,而不是判断当前节点;

        2)如果当前节点的左子节点不为空,并且左子节点就是要删除的节点,将this.left置空,并且返回,结束递归;

        3)如果当前节点的右子节点不为空,并且右子节点就是要删除的节点,将this.right置空,并且返回,结束递归;

        4)如果第2,3步都没有删除节点,那么向左子树递归删除;

        5)如果第4步也没有删除节点,则应当向右子树递归删除;

        6)考虑如果树是空树root,如果只有一个root节点,则等价于将二叉树置空。(第一步)

      2.5 源代码

      1 package cn.atguigu.Tree;
      2 
      3 public class BinaryTreeDemo {
      4 
      5     public static void main(String[] args) {
      6         // TODO Auto-generated method stub
      7         //先创建一个二叉树
      8         BinaryTree binaryTree=new BinaryTree();
      9         //创建需要的节点
     10         HeroNode root=new HeroNode(1, "宋江");
     11         HeroNode hero2=new HeroNode(2, "吴用");
     12         HeroNode hero3=new HeroNode(3, "卢俊义");
     13         HeroNode hero4=new HeroNode(4, "林冲");
     14         HeroNode hero5=new HeroNode(5, "关胜");
     15         
     16         //先手动创建二叉树
     17         root.setLeft(hero2);
     18         root.setRight(hero3);
     19         hero3.setRight(hero4);
     20         hero3.setLeft(hero5);
     21         binaryTree.setRoot(root);
     22         
     23         //遍历测试
     24         /*
     25          * System.out.println("前序遍历"); binaryTree.preOrder();
     26          * System.out.println("中序遍历"); binaryTree.infixOrder();
     27          * System.out.println("后序遍历"); binaryTree.postOrder();
     28          */
     29         
     30         //查找测试
     31         /*
     32          * System.out.println("前序查找~~~~");//查找了4次 HeroNode
     33          * res=binaryTree.preOrderSearch(5); if(res!=null) {
     34          * System.out.printf("找到了,信息为no=%d,name=%s",res.getNo(),res.getName()); }else {
     35          * System.out.printf("没有找到no=%d 的英雄",5); } System.out.println();
     36          * System.out.println("中序查找~~~~");//查找了3次 res=binaryTree.infixOrderSearch(5);
     37          * if(res!=null) {
     38          * System.out.printf("找到了,信息为no=%d,name=%s",res.getNo(),res.getName()); }else {
     39          * System.out.printf("没有找到no=%d 的英雄",5); } System.out.println();
     40          * System.out.println("后序查找~~~~");//查找了2次 res=binaryTree.postOrderSearch(5);
     41          * if(res!=null) {
     42          * System.out.printf("找到了,信息为no=%d,name=%s",res.getNo(),res.getName()); }else {
     43          * System.out.printf("没有找到no=%d 的英雄",5); }
     44          */
     45         
     46         //删除测试
     47         System.out.println("删除前前序遍历~~~~");
     48         binaryTree.preOrder();//1,2,3,5,4
     49 //        binaryTree.delNode(5);
     50         binaryTree.delNode(3);
     51         System.out.println("删除后前序遍历~~~~");
     52         binaryTree.preOrder();//1,2,3,4
     53     }
     54 
     55 }
     56 //定义BinaryTree 二叉树
     57 class BinaryTree{
     58     private HeroNode root;
     59 
     60     public void setRoot(HeroNode root) {
     61         this.root = root;
     62     }
     63     //删除节点
     64     public void delNode(int no) {
     65         if(root!=null) {
     66             //单独对root节点进行判断
     67             if(root.getNo()==no) {
     68                 root=null;
     69             }else {
     70                 root.delNode(no);
     71             }
     72         }else {
     73             System.out.println("空树,不能删除");
     74         }
     75     }
     76     
     77     //前序遍历
     78     public void preOrder() {
     79         if(this.root!=null) {
     80             this.root.preOrder();
     81         }else {
     82             System.out.println("当前二叉树为空,无法遍历");
     83         }
     84     }
     85     
     86     //中序遍历
     87     public void infixOrder() {
     88         if(this.root!=null) {
     89             this.root.infixOrder();
     90         }else {
     91             System.out.println("当前二叉树为空,无法遍历");
     92         }    
     93     }
     94     //后序遍历
     95     public void postOrder() {
     96         if(this.root!=null) {
     97             this.root.postOrder();
     98         }else {
     99             System.out.println("当前二叉树为空,无法遍历");
    100         }    
    101     }
    102     
    103     //前序查找
    104     public HeroNode preOrderSearch(int no) {
    105         if(root!=null) {
    106             return root.preOrderSearch(no);
    107         }else {
    108             return null;
    109         }
    110     }
    111     
    112     //中序查找
    113     public HeroNode infixOrderSearch(int no) {
    114         if(root!=null) {
    115             return root.infixOrderSearch(no);
    116         }else {
    117             return null;
    118         }
    119     }
    120     //后序查找
    121     public HeroNode postOrderSearch(int no) {
    122         if(root!=null) {
    123             return root.postOrderSearch(no);
    124         }else {
    125             return null;
    126         }
    127     }
    128 }
    129 
    130 
    131 //先创建HeroNode节点
    132 class HeroNode{
    133     private int no;
    134     private String name;
    135     private HeroNode left;//默认null
    136     private HeroNode right;//默认null
    137     public HeroNode(int no, String name) {
    138         this.no = no;
    139         this.name = name;
    140     }
    141     public int getNo() {
    142         return no;
    143     }
    144     public void setNo(int no) {
    145         this.no = no;
    146     }
    147     public String getName() {
    148         return name;
    149     }
    150     public void setName(String name) {
    151         this.name = name;
    152     }
    153     public HeroNode getLeft() {
    154         return left;
    155     }
    156     public void setLeft(HeroNode left) {
    157         this.left = left;
    158     }
    159     public HeroNode getRight() {
    160         return right;
    161     }
    162     public void setRight(HeroNode right) {
    163         this.right = right;
    164     }
    165     @Override
    166     public String toString() {
    167         return "HeroNode [no=" + no + ", name=" + name + "]";
    168     }
    169     //递归删除节点
    170     public void delNode(int no) {
    171         if(this.left!=null&&this.left.no==no) {//如果当前节点的左子节点不为空,并且左子节点就是要删除的节点,将this.left置空,并且返回,结束递归;
    172             this.left=null;
    173             return;
    174         }
    175         if(this.right!=null&&this.right.no==no) {//如果当前节点的右子节点不为空,并且右子节点就是要删除的节点,将this.right置空,并且返回,结束递归;
    176             this.right=null;
    177             return;
    178         }
    179         if(this.left!=null) {//向左子树递归删除
    180             this.left.delNode(no);            
    181         }
    182         if(this.right!=null) {//向右子树递归删除
    183             this.right.delNode(no);
    184         }
    185     }
    186     
    187     
    188     //编写前序遍历的方法
    189     public void preOrder() {
    190         System.out.println(this);//先输出父节点
    191         //递归向左子树遍历
    192         if(this.left!=null) {
    193             this.left.preOrder();
    194         }
    195         //递归向右子树遍历
    196         if(this.right!=null) {
    197             this.right.preOrder();
    198         }
    199     }
    200     //中序遍历
    201     public void infixOrder() {
    202         //向左子树遍历
    203         if(this.left!=null) {
    204             this.left.infixOrder();
    205         }
    206         //输出当前节点
    207         System.out.println(this);
    208         //向右子树遍历
    209         if(this.right!=null) {
    210             this.right.infixOrder();
    211         }
    212     }
    213     //后序遍历
    214     public void postOrder() {
    215         //向左子树遍历
    216         if(this.left!=null) {
    217             this.left.postOrder();
    218         }
    219         //向右子树遍历
    220         if(this.right!=null) {
    221             this.right.postOrder();
    222         }
    223         //输出当前节点
    224         System.out.println(this);
    225     }
    226     
    227     //前序查找
    228     /**
    229      * 
    230      * @param no 查找no
    231      * @return 如果找到就返回该Node,否则返回null
    232      */
    233     public HeroNode preOrderSearch(int no) {
    234         System.out.println("进入前序查找");
    235         //比较当前节点是否是
    236         if(this.no==no) {
    237             return this;
    238         }
    239         //向左判断
    240         HeroNode resNode=null;
    241         if(this.left!=null) {
    242             resNode=this.left.preOrderSearch(no);
    243         }
    244         if(resNode!=null) {
    245             return resNode;
    246         }
    247         //向右判断
    248         if(this.right!=null) {
    249             resNode=this.right.preOrderSearch(no);
    250         }
    251         return resNode;
    252     }
    253     
    254     //中序遍历查找
    255     public HeroNode infixOrderSearch(int no) {
    256         //向左判断
    257         HeroNode resNode=null;
    258         if(this.left!=null) {
    259             resNode=this.left.infixOrderSearch(no);
    260         }
    261         if(resNode!=null) {
    262             return resNode;
    263         }
    264         System.out.println("进入中序查找");
    265         //判断当前节点
    266         if(this.no==no) {
    267             return this;
    268         }
    269         //向右判断
    270         if(this.right!=null) {
    271             resNode=this.right.infixOrderSearch(no);
    272         }
    273         return resNode;
    274     }
    275     
    276     //后序遍历查找
    277     public HeroNode postOrderSearch(int no) {
    278         //向左判断
    279         HeroNode resNode=null;
    280         if(this.left!=null) {
    281             resNode=this.left.postOrderSearch(no);
    282         }
    283         if(resNode!=null) {
    284             return resNode;
    285         }
    286         //向右判断
    287         if(this.right!=null) {
    288             resNode=this.right.postOrderSearch(no);
    289         }
    290         if(resNode!=null) {
    291             return resNode;
    292         }
    293         //判断当前节点
    294         System.out.println("进入后序查找");
    295         if(this.no==no) {
    296             return this;
    297         }else {
    298             return null;
    299         }
    300     }
    301 }

    3.顺序存储二叉树

      3.1顺序存储二叉树的特点

      (1)顺序二叉树通常只考虑完全二叉树

      (2)第n个元素的左节点为2*n+1

      (3)第n个元素的右节点为2*n+2

      (4)第n个元素的父节点为(n-1)/2

      (5)n表示二叉树中第几个元素(从0开始编号)

      3.2 需求:数组{1,2,3,4,5,6,7},要求以二叉树前序遍历的方式进行遍历,结果应当为{1,2,4,5,3,6,7}

     1 package cn.atguigu.Tree;
     2 
     3 public class ArrBinaryTreeDemo {
     4 
     5     public static void main(String[] args) {
     6         // TODO Auto-generated method stub
     7         int[] arr= {1,2,3,4,5,6,7};
     8         ArrBinaryTree arrbinaryTree=new ArrBinaryTree(arr);
     9         arrbinaryTree.preOrder();
    10     }
    11 
    12 }
    13 //编写ArrBinaryTree,实现顺序存储二叉树遍历
    14 class ArrBinaryTree{
    15     private int[] arr;//存储数据节点的数组
    16 
    17     public ArrBinaryTree(int[] arr) {
    18         this.arr = arr;
    19     }
    20     //重载
    21     public void preOrder() {
    22         this.preOrder(0);
    23     }
    24     
    25     //编写一个方法,完成顺序存储二叉树的前序遍历
    26     /**
    27      * 
    28      * @param index 表示数组的下标 
    29      */
    30     public void preOrder(int index) {
    31         //如果数组为空,或者arr.length=0
    32         if(arr==null||arr.length==0) {
    33             System.out.println("数组为空,不能按照二叉树的前序遍历");
    34         }
    35         //输出当前这个元素
    36         System.out.println(arr[index]);
    37         //向左
    38         if((index*2+1)<arr.length) {
    39             preOrder(2*index+1);
    40         }
    41         //向右
    42         if((index*2+2)<arr.length) {
    43             preOrder(2*index+2);
    44         }
    45     }
    46 }

       3.3 顺序二叉树的实际应用场景是堆排序

    4. 线索二叉树

      4.1 基本介绍

      (1)n个节点的二叉链表中含有n+1个空指针域,利用二叉链表中的空指针域,存放指向该节点在某种遍历次序下的前驱和后继节点的指针

      (2)这种添加了线索的二叉链表称为线索链表,相应的二叉树为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树,中序线索二叉树,后序线索二叉树三种;

      (3)一个节点的前一个节点,称为前驱节点;

      (4)一个节点的后一个节点,称为后继节点;

      4.2 线索二叉树应用案例

      (1)当线索化二叉树后,Node节点的属性left和right,有以下两种情况:left指向的是左子树,也可能指向的是前驱节点;right指向的是右子树,也可能指向后继节点;

        中序遍历的结果:{B,F,D,A,C,G,E,H}

      (2)源代码

      1 package cn.atguigu.Tree;
      2 
      3 public class ThreadedBinaryTreeDemo {
      4 
      5     public static void main(String[] args) {
      6         // TODO Auto-generated method stub
      7         //中序线索二叉树测试
      8         ThreadedHeroNode root=new ThreadedHeroNode(1,"tom");
      9         ThreadedHeroNode node2=new ThreadedHeroNode(3,"jack");
     10         ThreadedHeroNode node3=new ThreadedHeroNode(6,"smith");
     11         ThreadedHeroNode node4=new ThreadedHeroNode(8,"mary");
     12         ThreadedHeroNode node5=new ThreadedHeroNode(10,"king");
     13         ThreadedHeroNode node6=new ThreadedHeroNode(14,"dim");
     14         
     15         //手动创建二叉树
     16         root.setLeft(node2);
     17         root.setRight(node3);
     18         node2.setLeft(node4);
     19         node2.setRight(node5);
     20         node3.setLeft(node6);
     21         
     22         //测试线索化
     23         ThreadedBinaryTree tree=new ThreadedBinaryTree();
     24         tree.setRoot(root);
     25         tree.threadedNodes();//线索化
     26         
     27         //测试,以10节点测试
     28         ThreadedHeroNode left=node5.getLeft();
     29         System.out.println("10号节点的前驱节点是:"+left);
     30         
     31         //使用线索化遍历的方式
     32         System.out.println("使用线索化的方式遍历,线索化二叉树");
     33         tree.threadedList();
     34     }
     35 
     36 }
     37 //定义ThreadedBinaryTree 二叉树 实现了线索化功能的二叉树
     38 class ThreadedBinaryTree{
     39     private ThreadedHeroNode root;
     40     //为了实现线索化,需要创建要给指向当前节点的新节点的指针
     41     private ThreadedHeroNode pre=null;
     42     //在递归进行线索化时,pre总是保留前一个节点
     43     
     44     public void setRoot(ThreadedHeroNode root) {
     45         this.root = root;
     46     }
     47     
     48     //重载
     49     public void threadedNodes() {
     50         this.threadedNodes(root);
     51     }
     52     //遍历线索化二叉树的方法
     53     public void threadedList() {
     54         //定义一个遍历,存储当前遍历的节点,从root开始
     55         ThreadedHeroNode node=root;
     56         while(node!=null) {
     57             //循环的找到leftType=1的节点
     58             //后面随着遍历而变化,因为当leftType=1时,说明该节点是按照线索化
     59             //处理后的有效节点
     60             while(node.getLeftType()==0) {
     61                 node=node.getLeft();
     62             }
     63             //输出当前节点
     64             System.out.println(node);
     65             //如果当前节点的右指针指向的是后继节点,就一直输出
     66             while(node.getRightType()==1) {
     67                 node=node.getRight();
     68                 System.out.println(node);
     69             }
     70             //替换遍历节点
     71             node=node.getRight();
     72         }
     73     }
     74     
     75     //编写对二叉树进行中序线索化的方法
     76     /**
     77      * 
     78      * @param node 当前需要线索化的节点
     79      */
     80     public void threadedNodes(ThreadedHeroNode node) {
     81         //如果node==null,不能线索化
     82         if(node==null) {
     83             return;
     84         }
     85         //先线索化左子树
     86         threadedNodes(node.getLeft());
     87         //线索化当前节点
     88         //处理当前节点的前驱节点
     89         if(node.getLeft()==null) {
     90             //让当前节点的做指针指向当前节点
     91             node.setLeft(pre);
     92             node.setLeftType(1);//修改当前节点的指针类型,当前节点指向前驱节点
     93         }
     94         //处理后继节点
     95         if(pre!=null&&pre.getRight()==null) {
     96             pre.setRight(node);//让前驱节点的右指针指向当前指针
     97             pre.setRightType(1);//修改前驱节点的指针类型
     98         }
     99         //每处理一个节点后,让当前节点是下一个节点的前驱节点 
    100         pre=node;
    101         
    102         //后线索化右子树
    103         threadedNodes(node.getRight());
    104     }
    105 }
    106 
    107 
    108 
    109 //先创建ThreadedHeroNode节点
    110 class ThreadedHeroNode{
    111     private int no;
    112     private String name;
    113     private ThreadedHeroNode left;//默认null
    114     private ThreadedHeroNode right;//默认null
    115     //说明
    116     //1.如果leftType==0表明指向左子树,1表示指向前驱节点
    117     //2如果righttype==0表明指向右子树,1表示指向后继节点
    118     private int leftType;
    119     private int rightType;
    120     
    121     public int getLeftType() {
    122         return leftType;
    123     }
    124     public void setLeftType(int leftType) {
    125         this.leftType = leftType;
    126     }
    127     public int getRightType() {
    128         return rightType;
    129     }
    130     public void setRightType(int rightType) {
    131         this.rightType = rightType;
    132     }
    133     public ThreadedHeroNode(int no, String name) {
    134         this.no = no;
    135         this.name = name;
    136     }
    137     public int getNo() {
    138         return no;
    139     }
    140     public void setNo(int no) {
    141         this.no = no;
    142     }
    143     public String getName() {
    144         return name;
    145     }
    146     public void setName(String name) {
    147         this.name = name;
    148     }
    149     public ThreadedHeroNode getLeft() {
    150         return left;
    151     }
    152     public void setLeft(ThreadedHeroNode left) {
    153         this.left = left;
    154     }
    155     public ThreadedHeroNode getRight() {
    156         return right;
    157     }
    158     public void setRight(ThreadedHeroNode right) {
    159         this.right = right;
    160     }
    161     @Override
    162     public String toString() {
    163         return "ThreadedHeroNode [no=" + no + ", name=" + name + "]";
    164     }
    165 }
  • 相关阅读:
    直接拿来用!最火的前端开源项目(一)
    前端开发框架三剑客
    javascript获取ckeditor编辑器的值(实现代码)
    FireFox不支持InnerText的解决方法
    makefile:4: *** missing separator. Stop.
    javascript跨域解决方案
    wap网站获取访问者手机号PHP类文件
    CentOS安装libpcap
    运用百度开放平台接口根据ip地址获取位置
    azure 云上MySQL最新版本 MySQL5.7.11 批量自动化一键式安装 (转)
  • 原文地址:https://www.cnblogs.com/ERFishing/p/11335812.html
Copyright © 2011-2022 走看看