顺序存储二叉树,线索化二叉树

一、 顺序存储二叉树 

1、概述

  从数据存储来看,数组存储方式和树的存储方式可以相互转换,即数组可以转换成树,树也可以转换成数组

  

2、特点

  • 顺序二叉树通常只考虑完全二叉树
  • 第n个元素的左子节点为  2 * n + 1
  • 第n个元素的右子节点为  2 * n + 2
  • 第n个元素的父节点为  (n-1) / 2
  • n : 表示二叉树中的第几个元素(按0开始编号 如图所示)

3、八大排序算法中的堆排序,就要使用到顺序存储二叉树

4、完整代码

  

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

 

二、线索化二叉树 

1、概述

  1)n个结点的二叉链表中含有n+1  【公式 2n-(n-1)=n+1】 个空指针域。利用二叉链表中的空指针域,存放指向该结点在某种遍历次序下的前驱和后继结点的指针(这种附加的指针称为"线索")

  2)这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)

  根据线索性质的不同,线索二叉树可分为前序线索二叉树中序线索二叉树后序线索二叉树三种

  3)一个结点的前一个结点,称为前驱结点
  4)一个结点的后一个结点,称为后继结点

2、代码  

  

  1 public class ThreadedBT{
  2 
  3     public static void main(String[] args) {
  4         HeroNode root = new HeroNode(1, "亚瑟");
  5         HeroNode node2 = new HeroNode(3, "");
  6         HeroNode node3 = new HeroNode(6, "甄姬");
  7         HeroNode node4 = new HeroNode(8, "虞姬");
  8         HeroNode node5 = new HeroNode(10, "猴子");
  9         HeroNode node6 = new HeroNode(14, "兰陵王");
 10 
 11         //二叉树,后面我们要递归创建, 现在简单处理使用手动创建
 12         root.setLeft(node2);
 13         root.setRight(node3);
 14         node2.setLeft(node4);
 15         node2.setRight(node5);
 16         node3.setLeft(node6);
 17 
 18         //测试中序线索化
 19         ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
 20         threadedBinaryTree.setRoot(root);
 21         threadedBinaryTree.threadedNodes();
 22 
 23         //测试: 以10号节点测试
 24         HeroNode leftNode = node5.getLeft();
 25         HeroNode rightNode = node5.getRight();
 26 
 27         //当线索化二叉树后,能在使用原来的遍历方法
 28         //threadedBinaryTree.infixOrder();
 29         System.out.println("使用线索化的方式遍历 线索化二叉树");
 30         threadedBinaryTree.threadedList();
 31     }
 32 }
 33 
 34 class ThreadedBinaryTree {
 35     private HeroNode root;
 36 
 37     //为了实现线索化,需要创建要给指向当前结点的前驱结点的指针
 38     //在递归进行线索化时,pre 总是保留前一个结点
 39     private HeroNode pre = null;
 40 
 41     public void setRoot(HeroNode root) {
 42         this.root = root;
 43     }
 44 
 45     //重载一把threadedNodes方法
 46     public void threadedNodes() {
 47         this.threadedNodes(root);
 48     }
 49 
 50     //遍历线索化二叉树的方法
 51     public void threadedList() {
 52         //定义一个变量,存储当前遍历的结点,从root开始
 53         HeroNode node = root;
 54         while(node != null) {
 55             //循环的找到leftType == 1的结点,第一个找到就是8结点
 56             //后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化
 57             //处理后的有效结点
 58             while(node.getLeftType() == 0) {
 59                 node = node.getLeft();
 60             }
 61 
 62             //打印当前这个结点
 63             System.out.println(node);
 64             //如果当前结点的右指针指向的是后继结点,就一直输出
 65             while(node.getRightType() == 1) {
 66                 //获取到当前结点的后继结点
 67                 node = node.getRight();
 68                 System.out.println(node);
 69             }
 70             //替换这个遍历的结点
 71             node = node.getRight();
 72 
 73         }
 74     }
 75 
 76     //编写对二叉树进行中序线索化的方法
 77     /**
 78      *
 79      * @param node 就是当前需要线索化的结点
 80      */
 81     public void threadedNodes(HeroNode node) {
 82 
 83         //如果node==null, 不能线索化
 84         if(node == null) {
 85             return;
 86         }
 87 
 88         //(一)先线索化左子树
 89         threadedNodes(node.getLeft());
 90         //(二)线索化当前结点[有难度]
 91 
 92         //处理当前结点的前驱结点
 93         //以8结点来理解
 94         //8结点的.left = null , 8结点的.leftType = 1
 95         if(node.getLeft() == null) {
 96             //让当前结点的左指针指向前驱结点
 97             node.setLeft(pre);
 98             //修改当前结点的左指针的类型,指向前驱结点
 99             node.setLeftType(1);
100         }
101 
102         //处理后继结点
103         if (pre != null && pre.getRight() == null) {
104             //让前驱结点的右指针指向当前结点
105             pre.setRight(node);
106             //修改前驱结点的右指针类型
107             pre.setRightType(1);
108         }
109         //!!! 每处理一个结点后,让当前结点是下一个结点的前驱结点
110         pre = node;
111 
112         //(三)在线索化右子树
113         threadedNodes(node.getRight());
114 
115 
116     }
117     //前序遍历
118     public void preOrder() {
119         if(this.root != null) {
120             this.root.preOrder();
121         }else {
122             System.out.println("二叉树为空,无法遍历");
123         }
124     }
125 
126     //中序遍历
127     public void infixOrder() {
128         if(this.root != null) {
129             this.root.infixOrder();
130         }else {
131             System.out.println("二叉树为空,无法遍历");
132         }
133     }
134     //后序遍历
135     public void postOrder() {
136         if(this.root != null) {
137             this.root.postOrder();
138         }else {
139             System.out.println("二叉树为空,无法遍历");
140         }
141     }
142 
143    
144 }
145 
146 //先创建HeroNode 结点
147 class HeroNode {
148     private int no;
149     private String name;
150     private HeroNode left; //默认null
151     private HeroNode right; //默认null
152     //说明
153     //1. 如果leftType == 0 表示指向的是左子树, 如果 1 则表示指向前驱结点
154     //2. 如果rightType == 0 表示指向是右子树, 如果 1表示指向后继结点
155     private int leftType;
156     private int rightType;
157 
158     public int getLeftType() {
159         return leftType;
160     }
161     public void setLeftType(int leftType) {
162         this.leftType = leftType;
163     }
164     public int getRightType() {
165         return rightType;
166     }
167     public void setRightType(int rightType) {
168         this.rightType = rightType;
169     }
170     public HeroNode(int no, String name) {
171         this.no = no;
172         this.name = name;
173     }
174     public int getNo() {
175         return no;
176     }
177     public void setNo(int no) {
178         this.no = no;
179     }
180     public String getName() {
181         return name;
182     }
183     public void setName(String name) {
184         this.name = name;
185     }
186     public HeroNode getLeft() {
187         return left;
188     }
189     public void setLeft(HeroNode left) {
190         this.left = left;
191     }
192     public HeroNode getRight() {
193         return right;
194     }
195     public void setRight(HeroNode right) {
196         this.right = right;
197     }
198     @Override
199     public String toString() {
200         return "HeroNode [no=" + no + ", name=" + name + "]";
201     }
202 
203     //递归删除结点
204     //1.如果删除的节点是叶子节点,则删除该节点
205     //2.如果删除的节点是非叶子节点,则删除该子树
206     public void delNode(int no) {
207 
208         /*
209          *     1. 因为我们的二叉树是单向的,所以我们是判断当前结点的子结点是否需要删除结点,而不能去判断当前这个结点是不是需要删除结点.
210             2. 如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除)
211             3. 如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除)
212             4. 如果第2和第3步没有删除结点,那么我们就需要向左子树进行递归删除
213             5.  如果第4步也没有删除结点,则应当向右子树进行递归删除.
214 
215          */
216         //2. 如果当前结点的左子结点不为空,并且左子结点 就是要删除结点,就将this.left = null; 并且就返回(结束递归删除)
217         if(this.left != null && this.left.no == no) {
218             this.left = null;
219             return;
220         }
221         //3.如果当前结点的右子结点不为空,并且右子结点 就是要删除结点,就将this.right= null ;并且就返回(结束递归删除)
222         if(this.right != null && this.right.no == no) {
223             this.right = null;
224             return;
225         }
226         //4.我们就需要向左子树进行递归删除
227         if(this.left != null) {
228             this.left.delNode(no);
229         }
230         //5.则应当向右子树进行递归删除
231         if(this.right != null) {
232             this.right.delNode(no);
233         }
234     }
235 
236     //编写前序遍历的方法
237     public void preOrder() {
238         System.out.println(this); //先输出父结点
239         //递归向左子树前序遍历
240         if(this.left != null) {
241             this.left.preOrder();
242         }
243         //递归向右子树前序遍历
244         if(this.right != null) {
245             this.right.preOrder();
246         }
247     }
248     //中序遍历
249     public void infixOrder() {
250 
251         //递归向左子树中序遍历
252         if(this.left != null) {
253             this.left.infixOrder();
254         }
255         //输出父结点
256         System.out.println(this);
257         //递归向右子树中序遍历
258         if(this.right != null) {
259             this.right.infixOrder();
260         }
261     }
262     //后序遍历
263     public void postOrder() {
264         if(this.left != null) {
265             this.left.postOrder();
266         }
267         if(this.right != null) {
268             this.right.postOrder();
269         }
270         System.out.println(this);
271     }
272 
273     //前序遍历查找
274     /**
275      *
276      * @param no 查找no
277      * @return 如果找到就返回该Node ,如果没有找到返回 null
278      */
279     public HeroNode preOrderSearch(int no) {
280         System.out.println("进入前序遍历");
281         //比较当前结点是不是
282         if(this.no == no) {
283             return this;
284         }
285         //1.则判断当前结点的左子节点是否为空,如果不为空,则递归前序查找
286         //2.如果左递归前序查找,找到结点,则返回
287         HeroNode resNode = null;
288         if(this.left != null) {
289             resNode = this.left.preOrderSearch(no);
290         }
291         if(resNode != null) {//说明我们左子树找到
292             return resNode;
293         }
294         //1.左递归前序查找,找到结点,则返回,否继续判断,
295         //2.当前的结点的右子节点是否为空,如果不空,则继续向右递归前序查找
296         if(this.right != null) {
297             resNode = this.right.preOrderSearch(no);
298         }
299         return resNode;
300     }
301 
302     //中序遍历查找
303     public HeroNode infixOrderSearch(int no) {
304         //判断当前结点的左子节点是否为空,如果不为空,则递归中序查找
305         HeroNode resNode = null;
306         if(this.left != null) {
307             resNode = this.left.infixOrderSearch(no);
308         }
309         if(resNode != null) {
310             return resNode;
311         }
312         System.out.println("进入中序查找");
313         //如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前结点
314         if(this.no == no) {
315             return this;
316         }
317         //否则继续进行右递归的中序查找
318         if(this.right != null) {
319             resNode = this.right.infixOrderSearch(no);
320         }
321         return resNode;
322 
323     }
324 
325     //后序遍历查找
326     public HeroNode postOrderSearch(int no) {
327 
328         //判断当前结点的左子节点是否为空,如果不为空,则递归后序查找
329         HeroNode resNode = null;
330         if(this.left != null) {
331             resNode = this.left.postOrderSearch(no);
332         }
333         if(resNode != null) {//说明在左子树找到
334             return resNode;
335         }
336 
337         //如果左子树没有找到,则向右子树递归进行后序遍历查找
338         if(this.right != null) {
339             resNode = this.right.postOrderSearch(no);
340         }
341         if(resNode != null) {
342             return resNode;
343         }
344         System.out.println("进入后序查找");
345         //如果左右子树都没有找到,就比较当前结点是不是
346         if(this.no == no) {
347             return this;
348         }
349         return resNode;
350     }
351 
352 }

 

 

 

posted @ 2019-09-10 14:45  hyunbar  阅读(400)  评论(0编辑  收藏  举报