数组 链表 树存储方式分析
为什么需要树这种数据结构
-
数组存储方式分析
优点:通过下标方式访问元素,速度快。对于有序数组,还可以使用二分查找提高检索速度;
缺点:如果要检所具体某个值,或者插入值(按一定顺序)会整体移动,效率低;
拓展:集合的底层其实也是维护了一个数组,它插入值同样使用数组扩容来实现;
-
链式存储方式分析
优点:在一定程度上对数组存储方式有优化(如:插入一个数值节点,只需要将插入节点,链接到链表即可,删除效率也很好);
缺点:检索效率仍然较低;
-
树存储方式分析
能提高数据存储,读取的效率,比如利用二叉排序树(Binary Sort Tree),既可以保证数据的检索速度,同时也可以保证数据的插入,删除,修改的速度;
案例:[7,3,10,1,5,9,12];
二叉排序树存储数据效率:
- 查找12,就拿12和7比较,结果12>7,就往7的右边查找;然后12和10比较,结果12>10,继续往10的右边查找;最后找到了12;
- 添加13,先通过上方的步骤找到12的位置,然后12和13比较,13>12,就将13连接到12的右侧;
- 删除和修改的速度也同样很快;
二叉树的概念和常用术语
树示意图
树常用术语
- 节点
- 根节点
- 父节点
- 子节点
- 叶子节点(没有子节点的节点)
- 节点的权(节点值)
- 路径(从root节点到该节点的路线)
- 层
- 子树
- 树的高度(最大层数)
- 森林(多颗子树构成森林)
二叉树的概念
-
树的类型很多,每个节点最多只能有两个子节点的一种形式称为二叉树;
-
二叉树的子节点分为左节点和右节点;
-
如果该二叉树的所有叶子节点都在最后一层,并且节点总数=2n-1,n为层数,增我们称为满二叉树;
-
如果该二叉树的所有叶子节点都在最后一层或者倒数第二层,而且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续,我们称为完全二叉树;
前序中序后序遍历二叉树图解
使用前序,中序和后序对下面的二叉树进行遍历;
-
前序遍历:
先输出当前节点,如果当前节点左子节点不为空,就递归前序遍历;如果当前节点右子节点不为空,就递归前序遍历。
-
中序遍历:
如果当前节点的左子节点不为空,就递归中序遍历;然后输出当前节点;若当前节点右子节点不为空,就递归中序遍历。
-
后序遍历:
如果当前节点的左子节点不为空,就递归后序遍历;若当前节点右子节点不为空,就递归后序遍历;最后输出当前节点。
前序中序后序遍历二叉树代码实现
代码实现
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
| package com.jokerdig.tree;
public class BinaryTreeDemo { public static void main(String[] args) { BinaryTree binaryTree = new BinaryTree(); HeroNode root = new HeroNode(1, "宋江"); HeroNode node2 = new HeroNode(2, "吴用"); HeroNode node3 = new HeroNode(3, "卢俊义"); HeroNode node4 = new HeroNode(4, "武松"); root.setLeft(node2); root.setRight(node3); node3.setRight(node4); binaryTree.setRoot(root); System.out.println("前序遍历"); binaryTree.preOrder(); System.out.println("中序遍历"); binaryTree.infixOrder(); System.out.println("后序遍历"); binaryTree.postOrder(); } }
class BinaryTree{ private HeroNode root;
public void setRoot(HeroNode root) { this.root = root; }
public void preOrder(){ if(this.root!=null){ this.root.preOrder(); }else{ System.out.println("二叉树为空,无法遍历"); } }
public void infixOrder(){ if(this.root!=null){ this.root.infixOrder(); }else{ System.out.println("二叉树为空,无法遍历"); } } public void postOrder(){ if(this.root!=null){ this.root.postOrder(); }else{ System.out.println("二叉树为空,无法遍历"); } } }
class HeroNode{ private int no; private String name; private HeroNode left; private HeroNode right;
public HeroNode(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 HeroNode getLeft() { return left; }
public void setLeft(HeroNode left) { this.left = left; }
public HeroNode getRight() { return right; }
public void setRight(HeroNode right) { this.right = right; }
@Override public String toString() { return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + '}'; } public void preOrder(){ System.out.println(this); if(this.left!=null){ this.left.preOrder(); } if(this.right !=null){ this.right.preOrder(); } } public void infixOrder(){ if(this.left!=null){ this.left.infixOrder(); } System.out.println(this); if(this.right!=null){ this.right.infixOrder(); } } public void postOrder(){ if(this.left!=null){ this.left.postOrder(); }
if(this.right!=null){ this.right.postOrder(); } System.out.println(this); } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 前序遍历 HeroNode{no=1, name='宋江'} HeroNode{no=2, name='吴用'} HeroNode{no=3, name='卢俊义'} HeroNode{no=4, name='武松'} 中序遍历 HeroNode{no=2, name='吴用'} HeroNode{no=1, name='宋江'} HeroNode{no=3, name='卢俊义'} HeroNode{no=4, name='武松'} 后序遍历 HeroNode{no=2, name='吴用'} HeroNode{no=4, name='武松'} HeroNode{no=3, name='卢俊义'} HeroNode{no=1, name='宋江'}
Process finished with exit code 0
|
拓展
- 在上图的"卢俊义"节点处,增加一个左子节点"关胜";
- 使用前序,中序,后序遍历,并查看输出结果;
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
| package com.jokerdig.tree;
public class BinaryTreeDemo { public static void main(String[] args) { BinaryTree binaryTree = new BinaryTree(); HeroNode root = new HeroNode(1, "宋江"); HeroNode node2 = new HeroNode(2, "吴用"); HeroNode node3 = new HeroNode(3, "卢俊义"); HeroNode node4 = new HeroNode(4, "武松"); HeroNode node5 = new HeroNode(5, "关胜"); root.setLeft(node2); root.setRight(node3); node3.setLeft(node5); node3.setRight(node4); binaryTree.setRoot(root); System.out.println("前序遍历"); binaryTree.preOrder(); System.out.println("中序遍历"); binaryTree.infixOrder(); System.out.println("后序遍历"); binaryTree.postOrder(); } }
class BinaryTree{ private HeroNode root;
public void setRoot(HeroNode root) { this.root = root; }
public void preOrder(){ if(this.root!=null){ this.root.preOrder(); }else{ System.out.println("二叉树为空,无法遍历"); } }
public void infixOrder(){ if(this.root!=null){ this.root.infixOrder(); }else{ System.out.println("二叉树为空,无法遍历"); } } public void postOrder(){ if(this.root!=null){ this.root.postOrder(); }else{ System.out.println("二叉树为空,无法遍历"); } } }
class HeroNode{ private int no; private String name; private HeroNode left; private HeroNode right;
public HeroNode(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 HeroNode getLeft() { return left; }
public void setLeft(HeroNode left) { this.left = left; }
public HeroNode getRight() { return right; }
public void setRight(HeroNode right) { this.right = right; }
@Override public String toString() { return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + '}'; } public void preOrder(){ System.out.println(this); if(this.left!=null){ this.left.preOrder(); } if(this.right !=null){ this.right.preOrder(); } } public void infixOrder(){ if(this.left!=null){ this.left.infixOrder(); } System.out.println(this); if(this.right!=null){ this.right.infixOrder(); } } public void postOrder(){ if(this.left!=null){ this.left.postOrder(); }
if(this.right!=null){ this.right.postOrder(); } System.out.println(this); } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| 前序遍历 HeroNode{no=1, name='宋江'} HeroNode{no=2, name='吴用'} HeroNode{no=3, name='卢俊义'} HeroNode{no=5, name='关胜'} HeroNode{no=4, name='武松'} 中序遍历 HeroNode{no=2, name='吴用'} HeroNode{no=1, name='宋江'} HeroNode{no=5, name='关胜'} HeroNode{no=3, name='卢俊义'} HeroNode{no=4, name='武松'} 后序遍历 HeroNode{no=2, name='吴用'} HeroNode{no=5, name='关胜'} HeroNode{no=4, name='武松'} HeroNode{no=3, name='卢俊义'} HeroNode{no=1, name='宋江'}
Process finished with exit code 0
|
前序中序后序查找思路图解
二叉树查找指定节点
要求:
- 请编写前序,中序,后序查找的方法;
- 并分别使用三种查找方式,查找heroNo=5的节点;
- 并分析各种查找方式,分别比较了多少次;
查找步骤分析
前序查找
中序查找
-
先判断当前节点的左子节点是否为空,若不为空,则左递归中序查找,找到节点就返回;
-
如果没有找到,就判断当前节点是否等于要查找的节点,如果相等就返回当前节点;
-
如果不相等,则继续判断当前节点的右子节点是否为空,若不为空,则继续右递归中序查找,最后返回节点(不管是否为空都返回);
后序查找
-
先判断当前节点的左子节点是否为空,若不为空,则左递归后序查找,找到节点就返回;
-
然后继续判断当前节点的右子节点是否为空,若不为空,则继续右递归后序查找,找到节点就返回;
-
否则,就判断当前节点是否等于要查找的节点,如果相等就返回当前节点,否则返回null
;
前序中序后序查找代码实现
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
| package com.jokerdig.tree;
public class BinaryTreeDemo { public static void main(String[] args) { BinaryTree binaryTree = new BinaryTree(); HeroNode root = new HeroNode(1, "宋江"); HeroNode node2 = new HeroNode(2, "吴用"); HeroNode node3 = new HeroNode(3, "卢俊义"); HeroNode node4 = new HeroNode(4, "武松"); HeroNode node5 = new HeroNode(5, "关胜"); root.setLeft(node2); root.setRight(node3); node3.setLeft(node5); node3.setRight(node4); binaryTree.setRoot(root); System.out.println("=======前序遍历查找======="); HeroNode heroNode = binaryTree.preOrderSearch(5); if(heroNode!=null){ System.out.printf("找到:no=%d name=%s\n",heroNode.getNo(),heroNode.getName()); }else{ System.out.printf("没有 no=%d 的节点\n",5); } System.out.println("=======中序遍历查找======="); HeroNode heroNode2 = binaryTree.infixOrderSearch(5); if(heroNode2!=null){ System.out.printf("找到:no=%d name=%s\n",heroNode2.getNo(),heroNode2.getName()); }else{ System.out.printf("没有 no=%d 的节点\n",5); } System.out.println("=======后序遍历查找======="); HeroNode heroNode3 = binaryTree.postOrderSearch(5); if(heroNode3!=null){ System.out.printf("找到:no=%d name=%s\n",heroNode3.getNo(),heroNode3.getName()); }else{ System.out.printf("没有 no=%d 的节点\n",5); } } }
class BinaryTree{ private HeroNode root;
public void setRoot(HeroNode root) { this.root = root; } public HeroNode preOrderSearch(int no){ if(root!=null){ return root.preOrderSearch(no); }else{ return null; } } public HeroNode infixOrderSearch(int no){ if(root!=null){ return root.infixOrderSearch(no); }else{ return null; } } public HeroNode postOrderSearch(int no){ if(root!=null){ return root.postOrderSearch(no); }else{ return null; } } }
class HeroNode{ private int no; private String name; private HeroNode left; private HeroNode right;
public HeroNode(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 HeroNode getLeft() { return left; }
public void setLeft(HeroNode left) { this.left = left; }
public HeroNode getRight() { return right; }
public void setRight(HeroNode right) { this.right = right; }
@Override public String toString() { return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + '}'; } private HeroNode node = null;
public HeroNode preOrderSearch(int no){ System.out.println("进入前序遍历查找"); if(this.no == no){ return this; } if(this.left!=null){ node = this.left.preOrderSearch(no); } if(node!=null){ return node; } if(this.right!=null){ node = this.right.preOrderSearch(no); } return node; } public HeroNode infixOrderSearch(int no){ if(this.left!=null){ node = this.left.infixOrderSearch(no); } if(node!=null){ return node; } System.out.println("进入中序遍历查找"); if(this.no == no){ return this; } if(this.right!=null){ node = this.right.infixOrderSearch(no); } return node; } public HeroNode postOrderSearch(int no){ if(this.left!=null){ node = this.left.postOrderSearch(no); } if(node!=null){ return node; } if(this.right!=null){ node = this.right.postOrderSearch(no); } if(node!=null){ return node; } System.out.println("进入后序遍历查找"); if(this.no == no){ return this; } return null; } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| =======前序遍历查找======= 进入前序遍历查找 进入前序遍历查找 进入前序遍历查找 进入前序遍历查找 找到:no=5 name=关胜 =======中序遍历查找======= 进入中序遍历查找 进入中序遍历查找 进入中序遍历查找 找到:no=5 name=关胜 =======后序遍历查找======= 进入后序遍历查找 进入后序遍历查找 找到:no=5 name=关胜
Process finished with exit code 0
|
二叉树删除节点思路图解
二叉树删除节点
要求:
- 如果删除的节点是叶子节点,则删除该节点;
- 如果删除的节点是非叶子节点,则删除该子树;
- 测试删除5号叶子节点和3号树;
步骤:
-
如果只有一个root节点,则等价于将二叉树为空;
-
因为我们的二叉树是单向的,所以通常判断当前节点的子节点是否为待删除节点,而不是去判断当前节点是否待删除;
-
如果当前节点的左子节点不为空,并且左子节点就是待删除的节点,就将this.left=null
,并返回(结束递归删除);
-
如果当前节点的右子节点不为空,并且右子节点就是待删除的节点,就将this.right=null
,并返回(结束递归删除);
-
如果第2,3步骤没有删除节点,那么我们就需要向左子树进行递归删除;
-
如果第4步骤也没有删除节点,那么我们就需要向右子树进行递归删除;
二叉树删除节点代码实现
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
| package com.jokerdig.tree;
public class BinaryTreeDemo { public static void main(String[] args) { BinaryTree binaryTree = new BinaryTree(); HeroNode root = new HeroNode(1, "宋江"); HeroNode node2 = new HeroNode(2, "吴用"); HeroNode node3 = new HeroNode(3, "卢俊义"); HeroNode node4 = new HeroNode(4, "武松"); HeroNode node5 = new HeroNode(5, "关胜"); root.setLeft(node2); root.setRight(node3); node3.setLeft(node5); node3.setRight(node4); binaryTree.setRoot(root); System.out.println("==========删除5号叶子节点=========="); System.out.println("删除前,前序遍历"); binaryTree.preOrder(); binaryTree.delNode(5); System.out.println("删除后,前序遍历"); binaryTree.preOrder();
System.out.println("==========删除3号树=========="); System.out.println("删除前,前序遍历"); binaryTree.preOrder(); binaryTree.delNode(3); System.out.println("删除后,前序遍历"); binaryTree.preOrder(); } }
class BinaryTree{ private HeroNode root;
public void setRoot(HeroNode root) { this.root = root; }
public void preOrder(){ if(this.root!=null){ this.root.preOrder(); }else{ System.out.println("二叉树为空,无法遍历"); } } public void delNode(int no){ if(root!=null){ if(root.getNo()==no){ root = null; }else{ root.delNode(no); } }else{ System.out.println("二叉树为空"); } } }
class HeroNode{ private int no; private String name; private HeroNode left; private HeroNode right;
public HeroNode(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 HeroNode getLeft() { return left; }
public void setLeft(HeroNode left) { this.left = left; }
public HeroNode getRight() { return right; }
public void setRight(HeroNode right) { this.right = right; }
@Override public String toString() { return "HeroNode{" + "no=" + no + ", name='" + name + '\'' + '}'; } public void preOrder(){ System.out.println(this); if(this.left!=null){ this.left.preOrder(); } if(this.right !=null){ this.right.preOrder(); } } 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); } } }
|
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ==========删除5号叶子节点========== 删除前,前序遍历 HeroNode{no=1, name='宋江'} HeroNode{no=2, name='吴用'} HeroNode{no=3, name='卢俊义'} HeroNode{no=5, name='关胜'} HeroNode{no=4, name='武松'} 删除后,前序遍历 HeroNode{no=1, name='宋江'} HeroNode{no=2, name='吴用'} HeroNode{no=3, name='卢俊义'} HeroNode{no=4, name='武松'} ==========删除3号树========== 删除前,前序遍历 HeroNode{no=1, name='宋江'} HeroNode{no=2, name='吴用'} HeroNode{no=3, name='卢俊义'} HeroNode{no=4, name='武松'} 删除后,前序遍历 HeroNode{no=1, name='宋江'} HeroNode{no=2, name='吴用'}
Process finished with exit code 0
|
顺序存储二叉树思路图解
顺序存储二叉树基本介绍
从数据存储来看,数据存储方式和树的存储方式可以相互转换,即数组可以转换成树,树也可以转换成数组;
要求:
- 上图的二叉树的节点,要求以数组的方式来存放;
- 遍历数组的时候,要求仍然以前序,中序和后序的遍历方式来遍历数据;
顺序存储二叉树的特点
- 顺序存储二叉树通常只考虑完全二叉树;
- 第n个元素的左子节点为
2*n+1
;
- 第n个元素的右子节点为
2*n+2
;
- 第n个元素的父节点为
(n-1)/2
;
- n:表示二叉树中第几个元素(从0开始编号)
顺序存储二叉树代码实现
题目要求
给你一个数组{1,2,3,4,5,6,7},要求以二叉树前序,中序和后序遍历的方式进行遍历;
代码实现
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
| package com.jokerdig.tree;
public class ArrBinaryTreeDemo { public static void main(String[] args) { int[] arr = {1,2,3,4,5,6,7}; ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr); System.out.println("前序遍历:"); arrBinaryTree.preOrder(); System.out.println("\n中序遍历:"); arrBinaryTree.infixOrder(); System.out.println("\n后序遍历:"); arrBinaryTree.postOrder(); } }
class ArrBinaryTree{ private int [] arr; public ArrBinaryTree(int[] arr) { this.arr = arr; }
public void preOrder(){ this.preOrder(0); } public void infixOrder(){ this.infixOrder(0); } public void postOrder(){ this.postOrder(0); }
public void preOrder(int index){ if(arr == null || arr.length==0){ System.out.println("数组为空"); return; } System.out.print(arr[index]+" "); if((index*2+1)<arr.length){ preOrder(index*2+1); } if((index*2+2)<arr.length){ preOrder(index*2+2); } }
public void infixOrder(int index){ if(arr == null || arr.length == 0){ System.out.println("数组为空"); return; } if((index*2+1)<arr.length){ infixOrder(index*2+1); } System.out.print(arr[index]+" "); if((index*2+2)<arr.length){ infixOrder(index*2+2); } }
public void postOrder(int index){ if(arr == null || arr.length == 0){ System.out.println("数组为空"); return; } if((index*2+1)<arr.length){ postOrder(index*2+1); } if((index*2+2)<arr.length){ postOrder(index*2+2); } System.out.print(arr[index]+" "); } }
|
运行结果
1 2 3 4 5 6 7
| 前序遍历: 1 2 4 5 3 6 7 中序遍历: 4 2 5 1 6 3 7 后序遍历: 4 5 2 6 7 3 1 Process finished with exit code 0
|
八大排序算法中的堆排序,就会使用到顺序存储二叉树;
线索化二叉树的介绍
案例
将数列{1,3,6,8,10,14}构建成一颗二叉树;
问题分析:
- 当我们对上面的二叉树进行中序遍历时,数列为{8,3,10,1,14,6};
- 但是8,10,14这几个节点的左右指针和6的右指针都并没有完全利用上;
- 如果我们希望充分利用各个节点的左右指针,让各个节点可以指向自己的前后节点;
- 使用线索二叉树解决;
线索二叉树基本介绍
- n个节点的二叉链表中含有
n+1
【公式推导:2n-(n-1)=n+1
】个空指针域,利用二叉链表中的空指针域,存放指向该节点在某种遍历次序下的前驱节点和后继节点指针;(这种附加的指针称为线索)
- 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可以分为前序线索二叉树,中序线索二叉树和后序线索二叉树;
- 一个节点的前一个节点,称为前驱节点;
- 一个节点的后一个节点,称为后继节点;
线索化二叉树的思路图解
应用案例
将下面的二叉树,变为中序线索二叉树;
思路分析
结果应该为:{8,3,10,1,14,6}
说明:
当线索化二叉树后,Node节点的属性left和right,有如下情况;
- left指向的是左子树,也可能指向的是前驱节点,比如1节点left指向的左子树,而10节点的left指向的就是前驱节点;
- right指向的是右子树,也可能是指向后继节点,比如1节点right指向的是右子树,而10节点的right指向的是后继节点;
线索化二叉树的代码实现
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
| package com.jokerdig.tree;
public class ThreadedBinaryTreeDemo { public static void main(String[] args) { HeroNode2 root = new HeroNode2(1, "tom"); HeroNode2 node2 = new HeroNode2(3, "jack"); HeroNode2 node3 = new HeroNode2(6, "join"); HeroNode2 node4 = new HeroNode2(8, "black"); HeroNode2 node5 = new HeroNode2(10, "tom1"); HeroNode2 node6 = new HeroNode2(14, "tom2");
root.setLeft(node2); root.setRight(node3); node2.setLeft(node4); node2.setRight(node5); node3.setLeft(node6); BinaryTree2 binaryTree2 = new BinaryTree2(); binaryTree2.setRoot(root); binaryTree2.threadedNodes(); HeroNode2 leftNode = node5.getLeft(); HeroNode2 rightNode = node5.getRight(); System.out.println("10号节点的前驱节点为:"+leftNode); System.out.println("10号节点的后继节点为:"+rightNode); }
}
class BinaryTree2{ private HeroNode2 root; private HeroNode2 pre = null;
public void setRoot(HeroNode2 root) { this.root = root; }
public void threadedNodes(){ this.threadedNodes(root); }
public void threadedNodes(HeroNode2 node){ if(node==null){ return; } threadedNodes(node.getLeft()); if(node.getLeft()==null){ node.setLeft(pre); node.setLeftType(1); } if(pre!=null && pre.getRight()==null){ pre.setRight(node); pre.setRightType(1); } pre = node; threadedNodes(node.getRight()); } }
class HeroNode2 { private int no; private String name; private HeroNode2 left; private HeroNode2 right; private int leftType; private int rightType;
public HeroNode2(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 HeroNode2 getLeft() { return left; }
public void setLeft(HeroNode2 left) { this.left = left; }
public HeroNode2 getRight() { return right; }
public void setRight(HeroNode2 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 "HeroNode2{" + "no=" + no + ", name='" + name + '}'; } }
|
运行结果
1 2 3 4
| 10号节点的前驱节点为:HeroNode2{no=3, name='jack} 10号节点的后继节点为:HeroNode2{no=1, name='tom}
Process finished with exit code 0
|
遍历线索化二叉树实现
需求分析
说明:对前面的中序线索化二叉树进行遍历;
分析:因为线索化后,各个节点可以通过线索方式遍历,因此无需使用递归方式,这样也提高了遍历的效率。遍历的次序应当和中序遍历保持一致;
代码实现
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
| package com.jokerdig.tree;
public class ThreadedBinaryTreeDemo { public static void main(String[] args) { HeroNode2 root = new HeroNode2(1, "tom"); HeroNode2 node2 = new HeroNode2(3, "jack"); HeroNode2 node3 = new HeroNode2(6, "join"); HeroNode2 node4 = new HeroNode2(8, "black"); HeroNode2 node5 = new HeroNode2(10, "tom1"); HeroNode2 node6 = new HeroNode2(14, "tom2"); root.setLeft(node2); root.setRight(node3); node2.setLeft(node4); node2.setRight(node5); node3.setLeft(node6); BinaryTree2 binaryTree2 = new BinaryTree2(); binaryTree2.setRoot(root); binaryTree2.threadedNodes(); System.out.println("使用线索化的方式遍历线索化二叉树"); binaryTree2.threadedList(); } }
class BinaryTree2{ private HeroNode2 root; private HeroNode2 pre = null;
public void setRoot(HeroNode2 root) { this.root = root; }
public void threadedNodes(){ this.threadedNodes(root); }
public void threadedList(){ HeroNode2 node = root; while(node!=null){ while(node.getLeftType()==0){ node = node.getLeft(); } System.out.println(node); while(node.getRightType()==1){ node = node.getRight(); System.out.println(node); } node = node.getRight(); } }
public void threadedNodes(HeroNode2 node){ if(node==null){ return; } threadedNodes(node.getLeft()); if(node.getLeft()==null){ node.setLeft(pre); node.setLeftType(1); } if(pre!=null && pre.getRight()==null){ pre.setRight(node); pre.setRightType(1); } pre = node; threadedNodes(node.getRight()); } }
class HeroNode2 { private int no; private String name; private HeroNode2 left; private HeroNode2 right; private int leftType; private int rightType;
public HeroNode2(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 HeroNode2 getLeft() { return left; }
public void setLeft(HeroNode2 left) { this.left = left; }
public HeroNode2 getRight() { return right; }
public void setRight(HeroNode2 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 "HeroNode2{" + "no=" + no + ", name='" + name + '}'; } }
|
运行结果
1 2 3 4 5 6 7 8 9
| 使用线索化的方式遍历线索化二叉树 HeroNode2{no=8, name='black} HeroNode2{no=3, name='jack} HeroNode2{no=10, name='tom1} HeroNode2{no=1, name='tom} HeroNode2{no=14, name='tom2} HeroNode2{no=6, name='join}
Process finished with exit code 0
|