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 }

 

posted on 2019-08-13 15:41  ERFishing  阅读(175)  评论(0编辑  收藏  举报