二叉树
一、为什么需要树这种数据结构
1、数组存储方式的分析
优点:通过下标方式访问元素,速度快。对于有序数组,还可使用二分查找提高检索速度。
缺点:如果要检索具体某个值,或者插入值(按一定顺序)会整体移动,效率较低 [示意图]
2、链式存储方式的分析
优点:在一定程度上对数组存储方式有优化(比如:插入一个数值节点,只需要将插入节点,链接到链表中即可, 删除效率也很好)。
缺点:在进行检索时,效率仍然较低,比如(检索某个值,需要从头节点开始遍历)
3、树存储方式的分析
能提高数据存储,读取的效率, 比如利用 二叉排序树(Binary Sort Tree),既可以保证数据的检索速度,同时也可以保证数据的插入,删除,修改的速度
二、二叉树的常用术语
- 节点
- 根节点
- 父节点
- 子节点
- 叶子节点 (没有子节点的节点)
- 节点的权(节点值)
- 路径(从root节点找到该节点的路线)
- 层
- 子树
- 树的高度(最大层数)
- 森林 :多颗子树构成森林
三、二叉树的概念
- 树有很多种,每个节点最多只能有两个子节点的一种形式称为二叉树。
- 二叉树的子节点分为左节点和右节点
- 如果该二叉树的所有叶子节点都在最后一层,并且结点总数= 2^n -1 , n 为层数,则我们称为满二叉树。
- 如果该二叉树的所有叶子节点都在最后一层或者倒数第二层,而且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续,我们称为完全二叉树。
四、遍历
1、前序遍历
1 //编写前序遍历的方法 2 public void preOrder() { 3 System.out.println(this); //先输出父结点 4 //递归向左子树前序遍历 5 if(this.left != null) { 6 this.left.preOrder(); 7 } 8 //递归向右子树前序遍历 9 if(this.right != null) { 10 this.right.preOrder(); 11 } 12 }
2、中序遍历
1 //中序遍历 2 public void infixOrder() { 3 4 //递归向左子树中序遍历 5 if(this.left != null) { 6 this.left.infixOrder(); 7 } 8 //输出父结点 9 System.out.println(this); 10 //递归向右子树中序遍历 11 if(this.right != null) { 12 this.right.infixOrder(); 13 } 14 }
3、后序遍历
1 //后序遍历 2 public void postOrder() { 3 if(this.left != null) { 4 this.left.postOrder(); 5 } 6 if(this.right != null) { 7 this.right.postOrder(); 8 } 9 System.out.println(this); 10 }
五、完整代码
1 2 3 import org.junit.Test; 4 5 /** 6 * @author z 7 * @createdate 2019-08-15 11:14 8 */ 9 public class BinaryTreeTest { 10 @Test 11 public void test() { 12 BinaryTree b = new BinaryTree(); 13 HeroNode root = new HeroNode(1, "宋江"); 14 HeroNode n1 = new HeroNode(2, "吴用"); 15 HeroNode n2 = new HeroNode(3, "玉麒麟"); 16 HeroNode n3 = new HeroNode(4, "关胜"); 17 18 b.setRoot(root); 19 root.setLeft(n1); 20 root.setRight(n2); 21 n1.setLeft(n3); 22 b.proOrder(); 23 24 b.findproOrder(5); 25 } 26 } 27 28 class BinaryTree { 29 public void setRoot(HeroNode root) { 30 this.root = root; 31 } 32 33 private HeroNode root; 34 35 //前序遍历 36 public void proOrder() { 37 if (this.root != null) { 38 this.root.proOrder(); 39 } else { 40 System.out.println("二叉树为空"); 41 } 42 } 43 44 //中序遍历 45 public void infixOrder() { 46 if (this.root != null) { 47 this.root.infixOrder(); 48 } else { 49 System.out.println("二叉树为空"); 50 } 51 } 52 53 //后序遍历 54 public void postOrder() { 55 if (this.root != null) { 56 this.root.postOrder(); 57 } else { 58 System.out.println("二叉树为空"); 59 } 60 } 61 62 //前序查找 63 public void findproOrder(int no) { 64 if (this.root != null) { 65 HeroNode heroNode = this.root.findproOrder(no); 66 if (heroNode != null) 67 System.out.println(heroNode); 68 else 69 System.out.println("未知的英雄编号:" + no); 70 } else { 71 System.out.println("二叉树为空"); 72 } 73 } 74 75 //中序查找 76 public void findinfixOrder(int no) { 77 if (this.root != null) { 78 HeroNode heroNode = this.root.findinfixOrder(no); 79 if (heroNode != null) 80 System.out.println(heroNode); 81 else 82 System.out.println("未知的英雄编号:" + no); 83 } else { 84 System.out.println("二叉树为空"); 85 } 86 } 87 88 //后序查找 89 public void findpostOrder(int no) { 90 if (this.root != null) { 91 HeroNode heroNode = this.root.findpostOrder(no); 92 if (heroNode != null) 93 System.out.println(heroNode); 94 else 95 System.out.println("未知的英雄编号:" + no); 96 } else { 97 System.out.println("二叉树为空"); 98 } 99 } 100 } 101 102 class HeroNode { 103 private int no; 104 private String name; 105 private HeroNode left; //默认null 106 private HeroNode right; //默认null 107 108 @Override 109 public String toString() { 110 return "HeroNode{" + 111 "no=" + no + 112 ", name='" + name + '\'' + 113 /*", left=" + left + 114 ", right=" + right +*/ 115 '}'; 116 } 117 118 public HeroNode(int no, String name) { 119 this.no = no; 120 this.name = name; 121 } 122 123 public int getNo() { 124 return no; 125 } 126 127 public void setNo(int no) { 128 this.no = no; 129 } 130 131 public String getName() { 132 return name; 133 } 134 135 public void setName(String name) { 136 this.name = name; 137 } 138 139 public HeroNode getLeft() { 140 return left; 141 } 142 143 public void setLeft(HeroNode left) { 144 this.left = left; 145 } 146 147 public HeroNode getRight() { 148 return right; 149 } 150 151 public void setRight(HeroNode right) { 152 this.right = right; 153 } 154 155 //编写前序遍历的方法 156 public void proOrder() { 157 System.out.println(this); 158 if (this.left != null) { 159 this.left.proOrder(); 160 } 161 if (this.right != null) { 162 this.right.proOrder(); 163 } 164 } 165 166 //编写中序遍历的方法 167 public void infixOrder() { 168 if (this.left != null) { 169 this.left.infixOrder(); 170 } 171 System.out.println(this); 172 if (this.right != null) { 173 this.right.infixOrder(); 174 } 175 } 176 177 //编写后序遍历的方法 178 public void postOrder() { 179 if (this.left != null) { 180 this.left.postOrder(); 181 } 182 if (this.right != null) { 183 this.right.postOrder(); 184 } 185 System.out.println(this); 186 } 187 188 //编写前序查找的方法 189 public HeroNode findproOrder(int no) { 190 if (this.no == no) { 191 return this; 192 } 193 //1.则判断当前结点的左子节点是否为空,如果不为空,则递归前序查找 194 //2.如果左递归前序查找,找到结点,则返回 195 HeroNode h = null; 196 if (this.left != null) { 197 h = this.left.findproOrder(no); 198 } 199 if (h != null) { 200 return h; 201 } 202 //1.左递归前序查找,找到结点,则返回,否继续判断, 203 //2.当前的结点的右子节点是否为空,如果不空,则继续向右递归前序查找 204 if (this.right != null) { 205 h = this.right.findproOrder(no); 206 } 207 return h; 208 } 209 210 //编写中序查找的方法 211 public HeroNode findinfixOrder(int no) { 212 HeroNode h = null; 213 if (this.left != null) { 214 h = this.left.findinfixOrder(no); 215 } 216 if (h != null) { 217 return h; 218 } 219 if (this.no == no) { 220 return this; 221 } 222 if (this.right != null) { 223 h = this.right.findinfixOrder(no); 224 } 225 return h; 226 } 227 228 //编写后序查找的方法 229 public HeroNode findpostOrder(int no) { 230 HeroNode h = null; 231 if (this.left != null) { 232 h = this.left.findpostOrder(no); 233 } 234 if (h != null) { 235 return h; 236 } 237 if (this.right != null) { 238 h = this.right.findpostOrder(no); 239 } 240 if (h != null) { 241 return h; 242 } 243 if (this.no == no) { 244 return this; 245 } 246 return null; 247 } 248 }