一.链式存储的二叉树的操作
1.遍历二叉树
- 先序遍历:根-->左-->右
- 中序遍历:左-->根-->右
- 后序遍历:左-->右-->根
- 层级遍历:逐层从左到输出
2.二叉树结点的查找
结点的查找也可以分为先序查找,中序查找和后序查找。方式和遍历方式相似。
3.删除二叉树结点
- 二叉树抽象类代码
package cn.kimtian.tree;
/**
* 链式存储的二叉树
*
* @author kimtian
*/
public class BinaryTree {
/**
* 根结点
*/
TreeNode root;
/**
* 设置根结点
*
* @param root 根结点
*/
public void setRoot(TreeNode root) {
this.root = root;
}
/**
* 获取根结点
*
* @return TreeNode 根结点
*/
public TreeNode getRoot() {
return root;
}
/**
* 先序遍历
*/
public void frontShow() {
if (root != null) {
root.frontShow();
}
}
/**
* 中序遍历
*/
public void middleShow() {
if (root != null) {
root.middleShow();
}
}
/**
* 后序遍历
*/
public void behindShow() {
if (root != null) {
root.behindShow();
}
}
/**
* 查找二叉树的结点--先序查找
*
* @param i 查找的结点的值
*/
public TreeNode frontSearch(int i) {
return root.frontSearch(i);
}
/**
* 查找二叉树的结点--中序查找
*
* @param i 查找的结点的值
*/
public TreeNode middleSearch(int i) {
return root.middleSearch(i);
}
/**
* 查找二叉树的结点--后序查找
*
* @param i 查找的结点的值
*/
public TreeNode behindSearch(int i) {
return root.behindSearch(i);
}
/**
* 删除二叉树的结点/子树
*
* @param i 查找的结点的值
*/
public void deleteTreeNode(int i) {
if (root.value == i) {
root = null;
} else {
root.deleteTreeNode(i);
}
}
}
- 二叉树的结点
package cn.kimtian.tree;
/**
* 树里面的结点
*
* @author kimtian
*/
public class TreeNode {
/**
* 树里面的值,结点的权
*/
int value;
/**
* 左儿子
*/
TreeNode leftNode;
/**
* 右儿子
*/
TreeNode rightNode;
public TreeNode(int value) {
this.value = value;
}
public void setLeftNode(TreeNode leftNode) {
this.leftNode = leftNode;
}
public void setRightNode(TreeNode rightNode) {
this.rightNode = rightNode;
}
/**
* 先序遍历--递归思想
*/
public void frontShow() {
//先遍历当前结点的内容
System.out.print(value + " ");
//左结点
if (leftNode != null) {
leftNode.frontShow();
}
//右结点
if (rightNode != null) {
rightNode.frontShow();
}
}
/**
* 中序遍历
*/
public void middleShow() {
//左结点
if (leftNode != null) {
leftNode.middleShow();
}
System.out.print(value + " ");
//右结点
if (rightNode != null) {
rightNode.middleShow();
}
}
/**
* 后序遍历
*/
public void behindShow() {
//左结点
if (leftNode != null) {
leftNode.behindShow();
}
//右结点
if (rightNode != null) {
rightNode.behindShow();
}
System.out.print(value + " ");
}
/**
* 层级遍历
*/
public void levelShow(TreeNode treeNode) {
//如果根结点为空,直接返回
if (treeNode == null) {
return;
} else {
//新建一个队列,存放树的结点
Queue<TreeNode> q = new ArrayDeque<TreeNode>();
//将根结点加入队列中
q.add(treeNode);
TreeNode cur;
//如果队列不为空,循环输出
while (!q.isEmpty()) {
// 获取当前队列的队头
cur = q.peek();
//输出队头的值
System.out.print(cur.value + " ");
//将队头结点的左结点入队
if (cur.leftNode != null) {
q.add(cur.leftNode);
}
//将队头结点的右结点入队
if (cur.rightNode != null) {
q.add(cur.rightNode);
}
//将队头出队列
q.poll();
}
}
}
/**
* 先序查找
*
* @param i 查找的结点的值
*/
public TreeNode frontSearch(int i) {
TreeNode target = null;
//对比当前结点的值
if (value == i) {
return this;
}
//当前结点的值不是要查找的结点
else {
//查找左儿子
if (leftNode != null) {
target = leftNode.frontSearch(i);
}
if (target != null) {
return target;
}
//查找右儿子
if (rightNode != null) {
target = rightNode.frontSearch(i);
}
if (target != null) {
return target;
}
}
return target;
}
/**
* 中序查找
*
* @param i 查找的结点的值
*/
public TreeNode middleSearch(int i) {
TreeNode target = null;
//对比左儿子的值
if (leftNode != null) {
target = leftNode.middleSearch(i);
}
if (target != null) {
return target;
}
//对比当前结点的值
if (value == i) {
return this;
}
//查找右儿子
if (rightNode != null) {
target = rightNode.middleSearch(i);
}
if (target != null) {
return target;
}
return target;
}
/**
* 后序查找
*
* @param i 查找的结点的值
*/
public TreeNode behindSearch(int i) {
TreeNode target = null;
//对比左儿子的值
if (leftNode != null) {
target = leftNode.behindSearch(i);
}
if (target != null) {
return target;
}
//查找右儿子
if (rightNode != null) {
target = rightNode.behindSearch(i);
}
if (target != null) {
return target;
}
//对比当前结点的值
if (value == i) {
return this;
}
return target;
}
/**
* 删除二叉树的结点/子树
*
* @param i 要删除的结点的值
*/
public void deleteTreeNode(int i) {
TreeNode parent = this;
//判断左儿子
if (parent.leftNode != null && parent.leftNode.value == i) {
parent.leftNode = null;
return;
}
//判断右边儿子
if (parent.rightNode != null && parent.rightNode.value == i) {
parent.rightNode = null;
return;
}
//递归检查并删除左儿子
parent = leftNode;
if (parent != null) {
parent.deleteTreeNode(i);
}
//递归检查并删除右儿子
parent = rightNode;
if (parent != null) {
parent.deleteTreeNode(i);
}
}
}
(3)测试类。
package cn.kimtian.tree;
/**
* 测试链式存储的二叉树
*
* @author kimtian
*/
public class TestBinaryTree {
public static void main(String[] args) {
//创建一棵树
BinaryTree binaryTree = new BinaryTree();
//创建一个根结点
TreeNode root = new TreeNode(1);
//将根结点赋给树
binaryTree.setRoot(root);
//创建一个子左结点
TreeNode leftTwo = new TreeNode(2);
//创建一个子右结点
TreeNode rightTwo = new TreeNode(3);
//给根的左结点赋值
root.setLeftNode(leftTwo);
//给根的右结点赋值
root.setRightNode(rightTwo);
//第三层结点
TreeNode llThree = new TreeNode(4);
TreeNode lrThree = new TreeNode(5);
TreeNode rlThree = new TreeNode(6);
TreeNode rrThree = new TreeNode(7);
leftTwo.setLeftNode(llThree);
leftTwo.setRightNode(lrThree);
rightTwo.setLeftNode(rlThree);
rightTwo.setRightNode(rrThree);
//先序遍历
binaryTree.frontShow();
System.out.println();
//中序遍历
binaryTree.middleShow();
System.out.println();
//后序遍历
binaryTree.behindShow();
System.out.println();
System.out.println("====================");
//先序查找
TreeNode result = binaryTree.frontSearch(5);
System.out.println(result);
System.out.println(result == lrThree);
TreeNode result1 = binaryTree.frontSearch(10);
System.out.println(result1);
System.out.println();
System.out.println("====================");
//中序查找
TreeNode result2 = binaryTree.middleSearch(5);
System.out.println(result2);
System.out.println(result2 == lrThree);
TreeNode result3 = binaryTree.middleSearch(10);
System.out.println(result3);
System.out.println();
System.out.println("====================");
//后序查找
TreeNode result4 = binaryTree.behindSearch(5);
System.out.println(result4);
System.out.println(result4 == lrThree);
TreeNode result5 = binaryTree.behindSearch(10);
System.out.println(result5);
//先序遍历
binaryTree.frontShow();
System.out.println();
//删除一个子树
binaryTree.deleteTreeNode(1);
//先序遍历
binaryTree.frontShow();
System.out.println();
}
}
二.顺序存储的二叉树的操作
顺序存储的二叉树通常情况只考虑完全二叉树。
- 第n个元素的左子结点是2*n+1
- 第n个元素的右子结点是2*n+2
- 第n个元素的父结点是(n-1)/2
1.遍历二叉树
- 先序遍历:根-->左-->右
- 中序遍历:左-->根-->右
- 后序遍历:左-->右-->根
package cn.kimtian.tree.arraybinarytree;
/**
* 顺序存储的二叉树
*
* @author kimtian
*/
public class ArrayBinaryTree {
/**
* 一个存储树的数组
*/
int[] data;
public ArrayBinaryTree(int[] data) {
this.data = data;
}
/**
* 前序遍历
*
* @param index 从哪个结点开始遍历
*/
public void frontShow(int index) {
if (data == null || data.length == 0) {
return;
}
//先遍历当前结点的内容
System.out.print(data[index] + " ");
//处理左子结点
if (2 * index + 1 < data.length) {
frontShow(2 * index + 1);
}
//处理右子结点
if (2 * index + 2 < data.length) {
frontShow(2 * index + 2);
}
}
/**
* 前序遍历 重载方法
*/
public void frontShow() {
frontShow(0);
}
/**
* 中序遍历
*
* @param index 从哪个结点开始遍历
*/
public void middleShow(int index) {
if (data == null || data.length == 0) {
return;
}
//处理左子结点
if (2 * index + 1 < data.length) {
middleShow(2 * index + 1);
}
//先遍历当前结点的内容
System.out.print(data[index] + " ");
//处理右子结点
if (2 * index + 2 < data.length) {
middleShow(2 * index + 2);
}
}
/**
* 中序遍历
*/
public void middleShow() {
middleShow(0);
}
/**
* 后序遍历
*
* @param index 从哪个结点开始遍历
*/
public void behindShow(int index) {
if (data == null || data.length == 0) {
return;
}
//处理左子结点
if (2 * index + 1 < data.length) {
behindShow(2 * index + 1);
}
//处理右子结点
if (2 * index + 2 < data.length) {
behindShow(2 * index + 2);
}
//先遍历当前结点的内容
System.out.print(data[index] + " ");
}
/**
* 后序遍历
*/
public void behindShow() {
behindShow(0);
}
}
三.线索二叉树
线索二叉树能线性地遍历二叉树,从而比递归的中序遍历更快。使用线索二叉树也能够方便的找到一个节点的父节点,这比显式地使用父亲节点指针或者栈效率更高。
先序线索化二叉树:如果没有左结点,则指向先序遍历的前一个结点。如果没有右结点,则指向先序遍历的后一个结点。
中序线索化二叉树:如果没有左结点,则指向中序遍历的前一个结点。如果没有右结点,则指向中序遍历的后一个结点。
后序线索化二叉树:如果没有左结点,则指向后序遍历的前一个结点。如果没有右结点,则指向后序遍历的后一个结点。
线索化二叉树时,一个结点的前一个节点,叫前驱结点。一个结点的后一个结点,叫后继结点。
1.线索二叉树的实现
可以找一个标记,来标记二叉树的左右结点,究竟指定的是左、右子树还是前驱结点和后继结点。