java二叉树--顺序二叉树和线索化二叉树(3)
一、顺序二叉树
1.特点
- 顺序二叉树只考虑完全二叉树
- 第n个元素的左子节点 为2*n+1
- 第n个元素的右子节点 为2*n+2
- 第n个元素的父节点为(n-1)/2
- n表示二叉树中的第几个元素
2.遍历
- 与普通二叉树类似,仅举出前序遍历
package tree; import java.util.Stack; public class ArrBinaryTree { int[] arr; public ArrBinaryTree(int[] arr) { this.arr=arr; } public void preOrder() { this.preOrder1(0); } private void preOrder(int i) { Stack<Integer> stack = new Stack<>(); stack.push(i); while (!stack.isEmpty()){ int cur=stack.pop(); System.out.println(arr[cur]); if(2*cur+2<arr.length) stack.push( 2*cur+2); if(2*cur+1<arr.length) stack.push( 2*cur+1); } } //递归前序遍历 private void preOrder1(int i) { //如果数组为空,或者 arr.length = 0 if(arr == null || arr.length == 0) { System.out.println("数组为空,不能按照二叉树的前序遍历"); } //输出当前这个元素 System.out.println(arr[i]); //向左递归遍历 if (2*i+1<arr.length) preOrder(2*i+1); //向右递归遍历 if(2*i+2<arr.length) preOrder(2*i+2); } }
package tree; public class ArrBinaryTreeDemo { public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5, 6, 7 }; //创建一个 ArrBinaryTree ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr); arrBinaryTree.preOrder(); // 1,2,4,5,3,6,7 } }
-
前中后序
package tree; import java.util.Stack; public class ArrBinaryTree { int[] arr; public ArrBinaryTree(int[] arr) { this.arr = arr; } public void preOrder() { this.preOrder1(0); } private void preOrder(int i) { Stack<Integer> stack = new Stack<>(); stack.push(i); while (!stack.isEmpty()) { int cur = stack.pop(); System.out.print(arr[cur] + "==>"); if (2 * cur + 2 < arr.length) stack.push(2 * cur + 2); if (2 * cur + 1 < arr.length) stack.push(2 * cur + 1); } } //递归前序遍历 private void preOrder1(int i) { //如果数组为空,或者 arr.length = 0 if (arr == null || arr.length == 0) { System.out.println("数组为空,不能按照二叉树的前序遍历"); } //输出当前这个元素 System.out.print(arr[i] + "==>"); //向左递归遍历 if (2 * i + 1 < arr.length) preOrder(2 * i + 1); //向右递归遍历 if (2 * i + 2 < arr.length) preOrder(2 * i + 2); } public void infixOrder() { this.infixOrder(0); } private void infixOrder(int i) { Stack<Integer> stack = new Stack<>(); while (!stack.isEmpty() || i < arr.length) { if (i < arr.length) { stack.push(i); i = i * 2 + 1; } else { i = stack.pop(); //输出当前这个元素 System.out.print(arr[i] + "==>"); i = 2 * i + 2; } } } private void infixOrder1(int i) { //如果数组为空,或者 arr.length = 0 if (arr == null || arr.length == 0) { System.out.println("数组为空,不能按照二叉树的前序遍历"); } //向左递归遍历 if (2 * i + 1 < arr.length) infixOrder(2 * i + 1); //输出当前这个元素 System.out.print(arr[i] + "==>"); //向右递归遍历 if (2 * i + 2 < arr.length) infixOrder(2 * i + 2); } public void postOrder() { this.postOrder(0); } private void postOrder(int i) { Stack<Integer> s1 = new Stack<>(); Stack<Integer> s2 = new Stack<>(); s1.push(i); while (!s1.isEmpty()){ i=s1.pop(); s2.push(i); if(2*i+1<arr.length) s1.push(2*i+1); if(2*i+2<arr.length) s1.push(2*i+2); } while (!s2.isEmpty()){ //输出当前这个元素 System.out.print(arr[s2.pop()] + "==>"); } } private void postOrder2(int i) { Stack<Integer> stack = new Stack<>(); int last = arr.length; while (i < arr.length) { stack.push(i); i = 2 * i + 1; } while (!stack.isEmpty()) { i = stack.pop(); if (2 * i + 2 < arr.length && 2 * i + 2 != last) { stack.push(i); i=2*i+2; while (i < arr.length) { stack.push(i); i = 2 * i + 1; } } else { //输出当前这个元素 System.out.print(arr[i] + "==>"); last = i; } } } private void postOrder1(int i) { //如果数组为空,或者 arr.length = 0 if (arr == null || arr.length == 0) { System.out.println("数组为空,不能按照二叉树的前序遍历"); } //向左递归遍历 if (2 * i + 1 < arr.length) postOrder(2 * i + 1); //向右递归遍历 if (2 * i + 2 < arr.length) postOrder(2 * i + 2); //输出当前这个元素 System.out.print(arr[i] + "==>"); } }
二、线索化二叉树
- 对于n个结点的二叉树,在二叉链存储结构中有n+1个空链域,利用这些空链域存放在某种遍历次序下该结点的前驱结点和后继结点的指针,这些指针称为线索,加上线索的二叉树称为线索二叉树
- 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种
- 线索链表解决了无法直接找到该结点在某种遍历序列中的前驱和后继结点的问题,解决了二叉链表找左、右孩子困难的问题
- 优点:利用线索二叉树进行中序遍历时,不必采用堆栈处理,速度较一般二叉树的遍历速度快,且节约存储空间。任意一个结点都能直接找到它的前驱和后继结点
- 缺点:结点的插入和删除麻烦,且速度也较慢,线索子树不能共用。
package tree; public class ThreadedBinaryTree { Node root; Node pre = null; //为了实现线索化,需要创建要给指向当前结点的前驱结点的指针 //在递归进行线索化时,pre 总是保留前一个结点 public ThreadedBinaryTree() { } public Node getRoot() { return root; } public void setRoot(Node root) { this.root = root; } public ThreadedBinaryTree(Node root) { this.root = root; } public void repreOrder() { if (root == null) return; else root.repreOrder(); } public void reinfixOrder() { if (root == null) return; else root.reinfixOrder(); } public void repostOrder() { if (root == null) return; else root.repostOrder(); } public void preOrder() { if (root == null) return; else root.preOrder(); } public void infixOrder() { if (root == null) return; else root.infixOrder(); } public void postOrder() { if (root == null) return; else root.postOrder(); } //前序查找 public Node preOrderSearch(int i) { if (root == null) return null; else return root.preOrderSearch(i); } public Node infixOrderSearch(int i) { if (root == null) return null; else return root.infixOrderSearch(i); } public Node postOrderSearch(int i) { if (root == null) return null; else return root.postOrderSearch(i); } public Node preOrderSearch1(int i) { if (root == null) return null; else return root.preOrderSearch1(i); } public Node infixOrderSearch1(int i) { if (root == null) return null; else return root.infixOrderSearch1(i); } public Node postOrderSearch1(int i) { if (root == null) return null; else return root.postOrderSearch1(i); } public Node postOrderSearch2(int i) { if (root == null) return null; else return root.postOrderSearch2(i); } public void delNode(int i) { if (root == null) return; if (root.id == i) root = null; else root.delNode(i); } public Node delNode1(Node root, int i) { if (root == null) return root; if (root.id == i) { if (root.right == null) { // 这里第二次操作目标值:最终删除的作用 return root.left; } Node cur = root.right; while (cur.left != null) { cur = cur.left; } Node node = new Node(root.id, root.name); root.id = cur.id; root.name = cur.name; cur.id = node.id; cur.name = node.name;// 这里第一次操作目标值:交换目标值其右子树最左面节点。仅仅是值替换,引用没有交换 } root.left = delNode1(root.left, i); root.right = delNode1(root.right, i); return root; } public void threadedNodes2() { this.threadedNodes2(root); } //编写对二叉树进行中序线索化的方法 private void threadedNodes2(Node node) { //如果node==null, 不能线索化 if (node == null) return; //处理后继结点 threadedNodes2(node.left); //!!! 每处理一个结点后,让当前结点是下一个结点的前驱结点 if (node.left == null) { node.left = pre; node.setLeftType(1); } if (pre != null && pre.right == null) { pre.right = node; pre.setRightType(1); } pre = node; //(三)在线索化右子树 threadedNodes2(node.right); } //按照后继方式遍历 public void threadedList21() { if (root == null) return; Node node = root; //循环的找到leftType == 1的结点,第一个找到就是8结点 //后面随着遍历而变化,因为当leftType==1时,说明该结点是按照线索化 //处理后的有效结点 while (node != null) { while (node.getLeftType() == 0) node = node.left; System.out.print(node + "==>"); while (node.getRightType() == 1) { node = node.right; System.out.print(node + "==>"); } node = node.right; } } //按照前驱方式遍历 public void threadedList22() { if (root == null) return; Node node = root; while (node.right != null && node.getRightType() == 0) { node = node.right; } while (node != null) { System.out.print(node + "==>"); if (node.getLeftType() == 1) { node = node.left; } else { node = node.left; while (node.right != null && node.getRightType() == 0) { node = node.right; } } } } //前序线索化 public void threadedNodes1() { threadedNodes1(root); } private void threadedNodes1(Node node) { if (node == null) return; if (node.left == null) { node.left = pre; node.setLeftType(1); } if (pre != null && pre.right == null) { pre.right = node; pre.setRightType(1); } pre = node; if (node.getLeftType() == 0) threadedNodes1(node.left); if (node.getRightType() == 0) threadedNodes1(node.right); } //按照前驱方式遍历 public void threadedList12() { Node node = root; while (node != null) { while (node.getLeftType() == 0) { System.out.print(node + "==>"); node = node.left; } System.out.print(node + "==>"); node = node.right; } } public void threadedNodes3() { this.threadedNodes3(root); } private void threadedNodes3(Node node) { if (node == null) return; threadedNodes3(node.left); threadedNodes3(node.right); if (node.left == null) { node.left = pre; node.setLeftType(1); } if (pre != null && pre.right == null) { pre.right = node; pre.setRightType(1); } pre = node; } public void threadedList32() { //1、找后序遍历方式开始的节点 Node node = root; while (node != null && node.getLeftType() == 0) { node = node.left; } Node pre = null; while (node != null) { if (node.getRightType() == 1) { System.out.print(node + "==>"); pre = node; node = node.right; } else { //如果上个处理的节点是当前节点的右节点 if (node.right == pre) { System.out.print(node + "==>"); if (node == root) { return; } pre = node; node = node.parent; } else { //如果从左节点的进入则找到有子树的最左节点 node = node.right; while (node != null && node.getLeftType() == 0) { node = node.left; } } } } } }
package tree; public class ThreadedBinaryTreeDemo { public static void main(String[] args) { //测试一把中序线索二叉树的功能 Node root = new Node(1, "tom"); Node node2 = new Node(3, "jack"); Node node3 = new Node(6, "smith"); Node node4 = new Node(8, "mary"); Node node5 = new Node(10, "king"); Node node6 = new Node(14, "dim"); //二叉树,后面我们要递归创建, 现在简单处理使用手动创建 root.setLeft(node2); root.setRight(node3); node2.setLeft(node4); node2.setRight(node5); node3.setLeft(node6); //测试中序线索化 ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree(); threadedBinaryTree.setRoot(root); // 前序线索化 //threadedBinaryTree.threadedNodes1(); // 中序线索化 // threadedBinaryTree.threadedNodes2(); // 后续线索化 threadedBinaryTree.threadedNodes3(); //测试: 以10号节点测试 // Node leftNode = node5.getLeft(); // Node rightNode = node5.getRight(); // System.out.println("10号结点的前驱结点是 =" + leftNode); //3 // System.out.println("10号结点的后继结点是=" + rightNode); //1 // // //当线索化二叉树后,能在使用原来的遍历方法 //threadedBinaryTree.infixOrder(); // System.out.println("使用线索化的方式遍历 线索化二叉树"); //threadedBinaryTree.threadedList21(); // 8, 3, 10, 1, 14, 6 // System.out.println("倒序遍历"); // threadedBinaryTree.threadedList22(); //System.out.println("使用线索化的方式遍历 线索化二叉树"); //threadedBinaryTree.threadedList12(); // 8, 3, 10, 1, 14, 6 System.out.println("使用线索化的方式遍历 线索化二叉树"); threadedBinaryTree.threadedList32(); // 8, 3, 10, 1, 14, 6 } }
package tree; import java.util.Stack; public class Node { int id; String name; Node left; Node right; Node parent; //说明 //1. 如果leftType == 0 表示指向的是左子树, 如果 1 则表示指向前驱结点 //2. 如果rightType == 0 表示指向是右子树, 如果 1表示指向后继结点 private int leftType; private int rightType; 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; } public Node getLeft() { return left; } public void setLeft(Node left) { this.left = left; //记录节点的父节点(后序线索化遍历时使用) if(this.left != null) { this.left.parent = this; } } public Node getRight() { return right; } public void setRight(Node right) { this.right = right; if(this.right != null) { this.right.parent = this; } } public Node() { } @Override public String toString() { return "Node [no=" + id + ", name=" + name + "]"; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Node(int id, String name) { this.id = id; this.name = name; } //递归前序遍历 public void repreOrder() { System.out.print(this + "==>"); if (this.left != null) this.left.repreOrder(); if (this.right != null) this.right.repreOrder(); } //递归中序遍历 public void reinfixOrder() { if (this.left != null) this.left.reinfixOrder(); System.out.print(this + "==>"); if (this.right != null) this.right.reinfixOrder(); } //递归后序遍历 public void repostOrder() { if (this.left != null) this.left.repostOrder(); if (this.right != null) this.right.repostOrder(); System.out.print(this + "==>"); } public void preOrder() { Stack<Node> stack = new Stack<>(); //根节点入栈 stack.push(this); // 如果栈为空,程序结束 while (!stack.isEmpty()) { // 判断栈是否为空,如果不为空,弹出栈顶元素并打印 Node cur = stack.pop(); System.out.print(cur + "==>"); // 如果有右子树,压栈 if (cur.right != null) { stack.push(cur.right); } // 如果有左子树,压栈 if (cur.left != null) { stack.push(cur.left); } } } public void infixOrder() { Stack<Node> stack = new Stack<>(); Node node = this; // 如果栈为空且当前节点为空,则结束 while (!stack.isEmpty() || node != null) { if (node != null) { stack.push(node);// 如果节点不为空,压栈,沿着左子树走一步 node = node.left; } else { // 判断栈是否为空,如果不为空,弹出栈顶元素并打印 node = stack.pop(); System.out.print(node + "==>"); node = node.right; }// 如果节点为空,则出栈,打印,沿着右子树走一步 } } public void postOrder() { Stack<Node> s1 = new Stack<>(); // Stack<HeroNode> s2 = new Stack<>(); // // s1.push(this); // while (!s1.isEmpty()) { // HeroNode node = s1.pop(); // s2.push(node); // //如果栈s1不为空,执行以下操作:弹出一个元素,入栈s2, // if (node.left != null) { // s1.push(node.left); // } // if (node.right != null) { // s1.push(node.right); // } // } // while (!s2.isEmpty()) { // System.out.print(s2.pop() + "==>"); // } Node node = this; Node lastnode = null; while (node != null) { // 将所有的左节点当做根入栈 s1.push(node); node = node.left; } while (!s1.isEmpty()) { node = s1.pop(); // 如果有右节点,处理右节点,lastnode标记访问过的右节点 if (node.right != null && node.right != lastnode) { s1.push(node); node = node.right; while (node != null) { s1.push(node); node = node.left; } } else { // 没有右节点说明是根 System.out.print(node + " "); lastnode = node; } } } //递归方式查找节点 public Node preOrderSearch(int i) { System.out.println("进入前序查找"); //比较当前结点是不是 if (this.id == i) return this; //1.则判断当前结点的左子节点是否为空,如果不为空,则递归前序查找 //2.如果左递归前序查找,找到结点,则返回 Node node = null; if (this.left != null) { node = this.left.preOrderSearch(i); } if (node != null) return node; //2.当前的结点的右子节点是否为空,如果不空,则继续向右递归前序查找 if (this.right != null) { node = this.right.preOrderSearch(i); } return node; } public Node infixOrderSearch(int i) { Node node = null; if (this.left != null) { node = this.left.infixOrderSearch(i); } System.out.println("进入中序查找"); if (node != null) return node; //比较当前结点是不是 if (this.id == i) return this; if (this.right != null) { node = this.right.infixOrderSearch(i); } return node; } public Node postOrderSearch(int i) { Node node = null; if (this.left != null) { node = this.left.postOrderSearch(i); } if (node != null) return node; if (this.right != null) { node = this.right.postOrderSearch(i); } if (node != null) return node; System.out.println("进入后序查找"); if (this.id == i) return this; return node; } public Node preOrderSearch1(int i) { System.out.println("前序非递归查找~~"); Stack<Node> stack = new Stack<>(); stack.push(this); while (!stack.isEmpty()) { Node node = stack.pop(); if (node.id == i) return node; if (node.right != null) stack.push(node.right); if (node.left != null) stack.push(node.left); } return null; } public Node infixOrderSearch1(int i) { System.out.println("中序非递归查找~~"); Stack<Node> stack = new Stack<>(); Node node = this; while (!stack.isEmpty() || node != null) { if (node != null) { stack.push(node); node = node.left; } else { node = stack.pop(); if (node.id == i) return node; node = node.right; } } return null; } public Node postOrderSearch1(int i) { System.out.println("后序非递归查找~~"); Stack<Node> s1 = new Stack<>(); Stack<Node> s2 = new Stack<>(); Node node = this; s1.push(node); while (!s1.isEmpty()){ node=s1.pop(); s2.push(node); if(node.left!=null){ s1.push(node.left); } if(node.right!=null){ s1.push(node.right); } } while (!s2.isEmpty()){ node=s2.pop(); if(node.id==i) return node; } return null; } public Node postOrderSearch2(int i) { System.out.println("后序非递归查找~~"); Stack<Node> s1 = new Stack<>(); Node node = this; Node lasrnode = null; while (node!=null){ s1.push(node); node=node.left; } while (!s1.isEmpty()){ node=s1.pop(); if(node.right!=null&& node.right!=lasrnode){ s1.push(node); node=node.right; while (node!=null){ s1.push(node); node=node.left; } }else { if(node.id==i) return node; lasrnode=node; } } return null; } public void delNode(int i) { if(this.left!=null&&this.left.id==i){ this.left=null; return; } if(this.right!=null&&this.right.id==i){ this.right=null; return; } if(this.left!=null) this.left.delNode(i); if(this.right!=null) this.right.delNode(i); } }
浙公网安备 33010602011771号