前面我们已经学习了一些线性结构的数据结构和算法,接下来我们开始学习非线性结构的内容。
二叉树
前面显示增、删、查、遍历方法,完整代码在最后面。
/**
* 为什么我们要学习树结构。
* 1、有序数组插入数据项和删除数据项太慢。
* 2、链表查找数据太慢。
* 3、在树中能非常快速的查找、插入、删除数据,结合了有序数组和链表的优点
* 4、暂时还不知道
*/
结点打包类
1 public class BinaryTree { 2 //数据项(对象什么都可以) 3 public long data; 4 //左孩子 5 public BinaryTree leftChiled; 6 //右孩子 7 public BinaryTree rightChiled; 8 9 public BinaryTree(int value) { 10 this.data = value; 11 } 12 13 }
1 public void add(int value) { 2 // 封装结点 3 BinaryTree newnode = new BinaryTree(value); 4 5 BinaryTree parent;// 引用父节点 6 7 BinaryTree current = root;// current引用根结点 8 9 if (root == null) {// 如果root为null,也就是第一次插入 10 root = newnode; 11 return; 12 } else { 13 while (true) { 14 15 parent = current;// 父节点指向指向当前节点,保存上一个结点 16 if (current.data > value) { 17 current = current.leftChiled; 18 if (current == null) { 19 parent.leftChiled = newnode; 20 return; 21 } 22 } else { 23 current = current.rightChiled; 24 25 if (current == null) { 26 parent.rightChiled = newnode; 27 return; 28 } 29 } 30 31 } 32 } 33 }
一、删除节点是二叉树操作中最复杂的。在删除之前首先要查找要删的节点。找到节点后,这个要删除的节点可能会有三种情况需要考虑。
1.该节点是叶子节点,没有子节点。
要删除叶节点,只需要改变该节点的父节点的引用值,将指向该节点的引用设置为null就可以了。
2.该节点有一个子节点。
改变父节点的引用,将其直接指向要删除节点的子节点。
3.该节点有两个子节点。
要删除有两个子节点的节点,就需要使用它的中序后继来替代该节点。
费我九牛二虎之力还是给整出来了。
1 public boolean delete(long value) {// 删除 2 BinaryTree current = root;// current保存根结点 3 BinaryTree parent = root;// parent保存parent的父节点 4 boolean isLeftchild = true; 5 while (current.data != value) { 6 parent = current; 7 // 进行比较,比较当前值和查找值的大小 8 if (current.data > value) { 9 current = current.leftChiled; 10 isLeftchild = true;// true为左子树 11 } else { 12 current = current.rightChiled; 13 isLeftchild = false; 14 } 15 if (current == null) { 16 return false; 17 } 18 } 19 if (current.leftChiled == null && current.rightChiled == null) {// 第1种情况 20 if (isLeftchild) { 21 parent.leftChiled = null; 22 } else { 23 parent.rightChiled = null; 24 } 25 } else if (current.rightChiled == null) {// 第2种情况 26 if (current == root) { 27 root = current.leftChiled; 28 } else if (isLeftchild) { 29 parent.leftChiled = current.leftChiled; 30 } else { 31 parent.rightChiled = current.leftChiled; 32 } 33 } else if (current.leftChiled == null) { 34 if (current == root) { 35 root = current.rightChiled; 36 } else if (isLeftchild) { 37 parent.leftChiled = current.rightChiled; 38 } else { 39 parent.rightChiled = current.rightChiled; 40 } 41 } else {// 第3种情况 42 BinaryTree successor = getSuccessor(current);// 根结点开始,successor存放中序后继结点 43 if (current == root) {// 替换工作 44 root = successor; 45 } else if (isLeftchild) {// 要删除的父节点的左子节点 46 parent.leftChiled = successor; 47 } else {// 要删除的父节点的右子节点 48 parent.rightChiled = successor; 49 } 50 successor.leftChiled = current.leftChiled;// 中序后继结点引用删除的左边的结点 51 } 52 53 return true; 54 } 55 56 public BinaryTree getSuccessor(BinaryTree delBin) {// 找中序后继结点方法 57 // delBin为要删除的结点 58 BinaryTree successor = delBin;// successor为查找的中序后继结点 59 BinaryTree successorParent = delBin; // successor的父节点 60 BinaryTree current = delBin.rightChiled; // 当前开始遍历的结点 61 62 while (current != null) {// 这里完成elBin.rightChiled是叶子结点 63 successorParent = successor;// successorParent保存上一个successor的引用 64 successor = current;// 循环完成以后,successor保存的就是中序后继结点 65 current = current.leftChiled; 66 } 67 // 中序后继结点有两种情况,就是delBin.rightChiled是否为叶子结点的两种情况 68 69 if (successor != delBin.rightChiled) {// elBin.rightChiled不是叶子结点,中序后继结点是左边的 70 successorParent.leftChiled = successor.rightChiled;// 中序后继结点后面肯定只有右子节点 71 successor.rightChiled = delBin.rightChiled;// 中序后继结点的右子节点指向要删除的右边的结点 72 // successor.rightChiled指向要删除的rightChiled 73 // 交换成功 74 } 75 return successor; 76 77 }
二、
1 public BinaryTree find(int value) {// 查找 2 // 引用当前结点,从根结点开始 3 BinaryTree current = root; 4 // 循环,只要查找值不等于当前结点的数据项 5 while (current.data != value) { 6 // 进行比较,比较当前值和查找值的大小 7 if (current.data > value) { 8 current = current.leftChiled; 9 } else { 10 current = current.rightChiled; 11 } 12 if (current == null) { 13 return null; 14 } 15 16 } 17 return current; 18 }
遍历二叉树,有3种遍历方法。其中,中序遍历最为常用,因为可以让特定的数据排序输出,前面我们已经学习了递归方法,接下来我们也使用递归方法实现。
前序遍历。
(1)访问根结点
(2)前序遍历左子树
(3)前序遍历右子树
(1)访问根结点
(2)前序遍历左子树
(3)前序遍历右子树
1 public void frontOrder(BinaryTree localBin) {// 前序 2 if (localBin != null) {// 递归结束条件 3 System.out.println(localBin.data); 4 frontOrder(localBin.leftChiled); 5 frontOrder(localBin.rightChiled); 6 } 7 }
中序遍历。
(1)中序遍历左子树
(2)访问根结点
(3)中序遍历右子树
1 public void middleOrder(BinaryTree localBin) {// 前序 2 if (localBin != null) {// 递归结束条件 3 middleOrder(localBin.leftChiled); 4 System.out.println(localBin.data); 5 middleOrder(localBin.rightChiled); 6 } 7 }
后序遍历。
(1)后序遍历左子树
(2)后序遍历右子树
(3)访问根结点
1 public void endOrder(BinaryTree localBin) {// 前序 2 if (localBin != null) {// 递归结束条件 3 endOrder(localBin.leftChiled); 4 endOrder(localBin.rightChiled); 5 System.out.println(localBin.data); 6 } 7 }
前辈的头发秃光不是没有原因的,哈哈哈。
1 package binarytree; 2 3 public class TestTree { 4 // 根结点 5 public BinaryTree root; 6 7 public static void main(String[] args) { 8 TestTree tt = new TestTree(); 9 tt.add(10); 10 tt.add(5); 11 tt.add(4); 12 tt.add(6); 13 tt.add(12); 14 tt.add(11); 15 tt.add(14); 16 tt.add(13); 17 18 tt.middleOrder(tt.root); 19 tt.delete(10); 20 System.out.println(); 21 tt.middleOrder(tt.root); 22 23 } 24 25 public void frontOrder(BinaryTree localBin) {// 前序 26 if (localBin != null) {// 递归结束条件 27 System.out.print(localBin.data + " "); 28 frontOrder(localBin.leftChiled); 29 frontOrder(localBin.rightChiled); 30 } 31 } 32 33 public void middleOrder(BinaryTree localBin) {// 中序 34 if (localBin != null) {// 递归结束条件 35 middleOrder(localBin.leftChiled); 36 System.out.print(localBin.data + " "); 37 middleOrder(localBin.rightChiled); 38 } 39 } 40 41 public void endOrder(BinaryTree localBin) {// 后序 42 if (localBin != null) {// 递归结束条件 43 endOrder(localBin.leftChiled); 44 endOrder(localBin.rightChiled); 45 System.out.print(localBin.data + " "); 46 } 47 } 48 49 public void add(int value) { 50 // 封装结点 51 BinaryTree newnode = new BinaryTree(value); 52 53 BinaryTree parent;// 引用父节点 54 55 BinaryTree current = root;// current引用根结点 56 57 if (root == null) {// 如果root为null,也就是第一次插入 58 root = newnode; 59 return; 60 } else { 61 while (true) { 62 63 parent = current;// 父节点指向指向当前节点,保存上一个结点 64 if (current.data > value) { 65 current = current.leftChiled; 66 if (current == null) { 67 parent.leftChiled = newnode; 68 return; 69 } 70 } else { 71 current = current.rightChiled; 72 73 if (current == null) { 74 parent.rightChiled = newnode; 75 return; 76 } 77 } 78 79 } 80 } 81 } 82 83 public BinaryTree find(int value) {// 查找 84 // 引用当前结点,从根结点开始 85 BinaryTree current = root; 86 // 循环,只要查找值不等于当前结点的数据项 87 while (current.data != value) { 88 // 进行比较,比较当前值和查找值的大小 89 if (current.data > value) { 90 current = current.leftChiled; 91 } else { 92 current = current.rightChiled; 93 } 94 if (current == null) { 95 return null; 96 } 97 98 } 99 return current; 100 } 101 102 public boolean delete(long value) {// 删除 103 BinaryTree current = root;// current保存根结点 104 BinaryTree parent = root;// parent保存parent的父节点 105 boolean isLeftchild = true; 106 while (current.data != value) { 107 parent = current; 108 // 进行比较,比较当前值和查找值的大小 109 if (current.data > value) { 110 current = current.leftChiled; 111 isLeftchild = true;// true为左子树 112 } else { 113 current = current.rightChiled; 114 isLeftchild = false; 115 } 116 if (current == null) { 117 return false; 118 } 119 } 120 if (current.leftChiled == null && current.rightChiled == null) {// 第1种情况 121 if (isLeftchild) { 122 parent.leftChiled = null; 123 } else { 124 parent.rightChiled = null; 125 } 126 } else if (current.rightChiled == null) {// 第2种情况 127 if (current == root) { 128 root = current.leftChiled; 129 } else if (isLeftchild) { 130 parent.leftChiled = current.leftChiled; 131 } else { 132 parent.rightChiled = current.leftChiled; 133 } 134 } else if (current.leftChiled == null) { 135 if (current == root) { 136 root = current.rightChiled; 137 } else if (isLeftchild) { 138 parent.leftChiled = current.rightChiled; 139 } else { 140 parent.rightChiled = current.rightChiled; 141 } 142 } else {// 第3种情况 143 BinaryTree successor = getSuccessor(current);// 根结点开始,successor存放中序后继结点 144 if (current == root) {// 替换工作 145 root = successor; 146 } else if (isLeftchild) {// 要删除的父节点的左子节点 147 parent.leftChiled = successor; 148 } else {// 要删除的父节点的右子节点 149 parent.rightChiled = successor; 150 } 151 successor.leftChiled = current.leftChiled;// 中序后继结点引用删除的左边的结点 152 } 153 154 return true; 155 } 156 157 public BinaryTree getSuccessor(BinaryTree delBin) {// 找中序后继结点方法 158 // delBin为要删除的结点 159 BinaryTree successor = delBin;// successor为查找的中序后继结点 160 BinaryTree successorParent = delBin; // successor的父节点 161 BinaryTree current = delBin.rightChiled; // 当前开始遍历的结点 162 163 while (current != null) {// 这里完成elBin.rightChiled是叶子结点 164 successorParent = successor;// successorParent保存上一个successor的引用 165 successor = current;// 循环完成以后,successor保存的就是中序后继结点 166 current = current.leftChiled; 167 } 168 // 中序后继结点有两种情况,就是delBin.rightChiled是否为叶子结点的两种情况 169 170 if (successor != delBin.rightChiled) {// elBin.rightChiled不是叶子结点,中序后继结点是左边的 171 successorParent.leftChiled = successor.rightChiled;// 中序后继结点后面肯定只有右子节点 172 successor.rightChiled = delBin.rightChiled;// 中序后继结点的右子节点指向要删除的右边的结点 173 // successor.rightChiled指向要删除的rightChiled 174 // 交换成功 175 } 176 return successor; 177 178 } 179 }