您的位置:首页 > 编程语言 > Java开发

二叉树、二叉搜索树、AVL树的java实现

2016-04-24 23:16 736 查看
数据结构一直都是断断续续的看,总是觉得理解的不够深入,特别是对树的理解,一直都很浅显,今儿又看了一遍,来做个总结吧。

首先,树中的一些概念:

1、树的节点包含一个数据元素,以及若干指向其子树的分支。节点拥有的子树的数量称为节点的度。节点的最大层次称为树的深度或高度

2、二叉树是一种树形结构,其特点是每个节点至多有两棵子树,且子树有左右之分,次序不能随意颠倒。

3、满二叉树:一棵深度为k且有2^k - 1个节点的二叉树,称之为满二叉树。

4、完全二叉树:对一个深度为k,节点个数为n的二叉树,当且仅当每个节点都与深度为k的满二叉树中编号从1至n的节点一一对应时,称之为完全二叉树。

5、哈夫曼树:又称最优树,是一类带权路径长度最短的树。

6、二叉查找树(Binary Search Tree):对任一个节点,左子树的所有值都小于它,右子树的所有值都大于它。

7、AVL树:带有平衡条件的二叉查找树。

8、伸展树:任一个节点被访问后,它就要经过一系列的AVL树的旋转,将该节点放到根上。

9、B-树:另外一种常用的查找树。有几个特点,对于M阶的B树,首先,根节点如果不是叶子,则其子节点的数量在2-M之间。其次,除根之外,所有的非叶子节点的子节点数在M/2 - M之间。最后,所有的叶子节点有相同的深度。

概念讲到这儿,下面来实战几种树。这里主要讲三种,分别是:

1、普通二叉树

2、二叉搜索树

3、AVL树

详细的代码下载地址:https://github.com/BLYang7/DataStructure/tree/master/BinNode

普通二叉树:讲一些树结构的定义,普通生成、前序中序后序遍历的实现(递归实现和用栈来实现)、层次遍历、获取二叉树的深度、判断是否平衡、寻找两个节点最近的共同父节点。

package com.BinNode;

import java.util.LinkedList;
import java.util.Queue;
import java.util.Stack;

/**
* 树结构
*/
public class TreeNode {

/**
* 定义一棵二叉树
*/
private static class BinNode{
public int Element;
public BinNode Left;
public BinNode Right;
public BinNode(int element){
this.Element = element;
}
public BinNode(int element, BinNode left, BinNode right){
this.Element = element;
this.Left = left;
this.Right = right;
}
public boolean isLeaf(){
return this.Left == null && this.Right == null;
}
}

/**
* 测试数据的构建
*/
public static BinNode root = new BinNode(0);
public static BinNode left = new BinNode(1);
public static BinNode right = new BinNode(2);
public static BinNode leftLeft = new BinNode(3);
public static BinNode leftRight = new BinNode(4);
public static BinNode rightLeft = new BinNode(5);
public static BinNode rightRight = new BinNode(6);
static{
root.Left = left;
root.Right = right;
left.Left = leftLeft;
left.Right = leftRight;
right.Left = rightLeft;
right.Right = rightRight;
}

/**
* 前序遍历,先遍历跟节点,再遍历左子树,后遍历右子树
* @param root 待遍历的根节点
*/
private static void preOrder(BinNode root){
if( root == null ){
return;
}
System.out.print(root.Element + "  "); //遍历打印输出
preOrder(root.Left);
preOrder(root.Right);
}

/**
* 中序遍历,先遍历左节点,再遍历跟节点,后遍历右子树
* @param root 待遍历的根节点
*/
private static void middleOrder(BinNode root){
if( root == null ){
return;
}
middleOrder(root.Left);
System.out.print( root.Element + "  " );
middleOrder(root.Right);
}

/**
* 后序遍历, 先遍历左节点,再遍历右节点,最后遍历根节点
* @param root 待遍历的根节点
*/
private static void postOrder(BinNode root){
if( root == null ){
return;
}
postOrder(root.Left);
postOrder(root.Right);
System.out.print( root.Element + "  " );
}

/**
* 前序遍历,不用递归实现,使用栈来实现
*/
private static void preOrder1(BinNode root){
Stack stack = new Stack();
BinNode temp = root;

//入栈
while( temp != null ){
System.out.print( temp.Element + "  " );
if( temp.Right != null ){
stack.push(temp.Right);
}
temp = temp.Left;
}

//出栈
while( stack.size() > 0 ){
temp = (BinNode) stack.pop();
while( temp != null ){
System.out.print(temp.Element + "  " );
if( temp.Right != null ){
stack.push(temp.Right);
}
temp = temp.Left;
}
}
}

/**
* 中序遍历,不用递归实现,使用栈区来实现
* @param root
*/
private static void middleOrder1( BinNode root ){
Stack stack = new Stack();
BinNode temp = root;

//入栈
while( temp != null ){
if( temp != null ){
stack.push(temp);
}
temp = temp.Left;
}

//出栈
while( stack.size() > 0 ){
temp = (BinNode) stack.pop();
System.out.print( temp.Element + "  ");
if( temp.Right != null ){
temp = temp.Right;
stack.push(temp);
while( temp != null ){
if( temp.Left != null){
stack.push(temp.Left);
}
temp = temp.Left;
}
}
}
}

/**
* 后序遍历,不用递归实现,使用栈来记录。这里有点繁,根节点还是要重新装载进栈
* @param root
*/
private static void postOrder1( BinNode root ){
Stack stack = new Stack();
BinNode temp = root;

//入栈
while(temp != null){
if(temp != null){
stack.push(temp);
}
temp = temp.Left;
}

//出栈
while( stack.size() > 0 ){
BinNode lastVisit = temp;
temp = (BinNode) stack.pop();
if(temp.Right == null || temp.Right == lastVisit){
System.out.print( temp.Element + "  " );
} else if ( temp.Left == lastVisit ){
stack.push(temp);  //再装载进去
temp = temp.Right;
stack.push(temp);
while( temp != null ){
if( temp.Left != null ){
stack.push(temp.Left);
}
temp = temp.Left;
}
}
}
}

/**
* 逐层打印二叉树的每一层 层次遍历
* 采用广度搜索优先的遍历方法,利用队列的方式实现
*/
private static void printTree( BinNode root ){
if( root == null ){
return;
}
BinNode tmp = null;
Queue<BinNode> queue = new LinkedList<BinNode>();
//队列中把根节点加进去
queue.add(root);
//遍历整棵二叉树
while( !queue.isEmpty() ){
tmp = (BinNode) queue.poll();
System.out.print( tmp.Element + "  ");

//将左节点加入到队列中
if( tmp.Left != null ){
queue.add(tmp.Left);
}

//将右节点加入到队列中
if( tmp.Right != null ){
queue.add(tmp.Right);
}
}
}

/**
* 获取二叉树的深度
*/
private static int getDepth(BinNode root){
if( root == null ){
return 0;
}
int leftLength = getDepth(root.Left);
int rightLength = getDepth(root.Right);

return (leftLength > rightLength ? leftLength : rightLength) + 1;
}

/**
* 判断是否为平衡二叉树
* 平衡的条件是:左右子树的深度相差不超过1
*/
private static boolean isBalanceTree(BinNode root){
if( root == null ){
return true;
}

int leftLength = getDepth(root.Left);
int rightLength = getDepth(root.Right);

int distance = (leftLength > rightLength) ? (leftLength - rightLength) : (rightLength - leftLength);

if( distance > 1 ){
return false;
} else {
return isBalanceTree(root.Left) && isBalanceTree(root.Right);
}
}

/**
* 寻找两个节点最近的共同父节点
* 将两个节点的父节点栈进行对比,一次弹出相同的节点,最后一次弹出的相同节点即为最近的公共父节点
*/
private static BinNode findParentNode(BinNode root, BinNode node1, BinNode node2){

java.util.Stack stack1 = new java.util.Stack();
getPositionByNode(root, node1, stack1);

java.util.Stack stack2 = new java.util.Stack();
getPositionByNode(root, node2, stack2);

BinNode tempNode = null;
while( stack1.peek() == stack2.peek() ){
tempNode = (BinNode) stack1.pop();
stack2.pop();
}
return tempNode;
}
//把某个节点的所有父节点依次添加到栈中
private static boolean getPositionByNode(BinNode root, BinNode node, java.util.Stack stack){

if( root == null ){
return false;
}
else if( root == node ){
stack.push(root);
return true;
}
else if( getPositionByNode(root.Left, node, stack) || getPositionByNode(root.Right, node, stack) ){
stack.push(root);
return true;
}
return false;
}

//测试
public static void main(String[] args) {
//前序遍历,递归调用
preOrder(root);
System.out.println();

//中序遍历,递归调用
middleOrder(root);
System.out.println();

//后序遍历,递归调用
postOrder(root);
System.out.println();

//前序遍历,栈来实现
preOrder1(root);
System.out.println();

//中序遍历,栈来实现
middleOrder1(root);
System.out.println();

//后序遍历,栈来实现
postOrder1(root);
System.out.println();

//层次遍历
printTree(root);
System.out.println();

//获取二叉树的深度
System.out.println(getDepth(root));

//判断是否为平衡二叉树
System.out.println(isBalanceTree(root));

//寻找两个节点最近的共同父节点
System.out.println(findParentNode(root, leftLeft, leftRight).Element);
}
}


二叉搜索树:这里重点是节点的插入和节点的删除,请看代码:

package com.BinNode;
/**
* 二叉搜索树 Binary Search Tree
*
* 此程序实现一个二叉查找树的功能,可以进行动态插入、删除关键字;
* 查询给定关键字、最小关键字、最大关键字;转换为有序列表(用于排序)
*/
import java.util.ArrayList;
import java.util.List;

public class BinarySearchTree {

// 树的根结点
private TreeNode root = null;

// 遍历结点列表
private List<TreeNode> nodelist = new ArrayList<TreeNode>();

// 树的节点类
private class TreeNode {

private int key;
private TreeNode leftChild;
private TreeNode rightChild;
private TreeNode parent;

public TreeNode(int key, TreeNode leftChild, TreeNode rightChild,
TreeNode parent) {
this.key = key;
this.leftChild = leftChild;
this.rightChild = rightChild;
this.parent = parent;
}

public int getKey() {
return key;
}

public String toString() {
String leftkey = (leftChild == null ? "" : String
.valueOf(leftChild.key));
String rightkey = (rightChild == null ? "" : String
.valueOf(rightChild.key));
return "(" + leftkey + " , " + key + " , " + rightkey + ")";
}
}

/**
* isEmpty: 判断二叉查找树是否为空;若为空,返回 true ,否则返回 false .
*
*/
public boolean isEmpty() {
if (root == null) {
return true;
} else {
return false;
}
}

/**
* TreeEmpty: 对于某些二叉查找树操作(比如删除关键字)来说,若树为空,则抛出异常。
*/
public void TreeEmpty() throws Exception {
if (isEmpty()) {
throw new Exception("树为空!");
}
}

/**
* search: 在二叉查找树中查询给定关键字
*
* @param key 给定关键字
* @return 匹配给定关键字的树结点
*/
public TreeNode search(int key) {
TreeNode pNode = root;
while (pNode != null && pNode.key != key) {
if (key < pNode.key) {
pNode = pNode.leftChild;
} else {
pNode = pNode.rightChild;
}
}
return pNode;
}

/**
* minElemNode: 获取二叉查找树中的最小关键字结点
*
* @return 二叉查找树的最小关键字结点
* @throws Exception 若树为空,则抛出异常
*/
public TreeNode minElemNode(TreeNode node) throws Exception {
if (node == null) {
throw new Exception("树为空!");
}
TreeNode pNode = node;
while (pNode.leftChild != null) {
pNode = pNode.leftChild;
}
return pNode;
}

/**
* maxElemNode: 获取二叉查找树中的最大关键字结点
*
* @return 二叉查找树的最大关键字结点
* @throws Exception 若树为空,则抛出异常
*/
public TreeNode maxElemNode(TreeNode node) throws Exception {
if (node == null) {
throw new Exception("树为空!");
}
TreeNode pNode = node;
while (pNode.rightChild != null) {
pNode = pNode.rightChild;
}
return pNode;
}

/**
* successor: 获取给定结点在中序遍历顺序下的后继结点
*
* @param node 给定树中的结点
* @return 若该结点存在中序遍历顺序下的后继结点,则返回其后继结点;否则返回 null
* @throws Exception
*/
public TreeNode successor(TreeNode node) throws Exception {
if (node == null) {
return null;
}

// 若该结点的右子树不为空,则其后继结点就是右子树中的最小关键字结点
if (node.rightChild != null) {
return minElemNode(node.rightChild);
}
// 若该结点右子树为空
TreeNode parentNode = node.parent;
while (parentNode != null && node == parentNode.rightChild) {
node = parentNode;
parentNode = parentNode.parent;
}
return parentNode;
}

/**
* precessor: 获取给定结点在中序遍历顺序下的前趋结点
*
* @param node  给定树中的结点
* @return 若该结点存在中序遍历顺序下的前趋结点,则返回其前趋结点;否则返回 null
* @throws Exception
*/
public TreeNode precessor(TreeNode node) throws Exception {
if (node == null) {
return null;
}

// 若该结点的左子树不为空,则其前趋结点就是左子树中的最大关键字结点
if (node.leftChild != null) {
return maxElemNode(node.leftChild);
}
// 若该结点左子树为空
TreeNode parentNode = node.parent;
while (parentNode != null && node == parentNode.leftChild) {
node = parentNode;
parentNode = parentNode.parent;
}

return parentNode;
}

/**
* insert: 将给定关键字插入到二叉查找树中
*
* @param key 给定关键字
*/
public void insert(int key) {
TreeNode parentNode = null;
TreeNode newNode = new TreeNode(key, null, null, null);
TreeNode pNode = root;
if (root == null) {
root = newNode;
return;
}

while (pNode != null) {
parentNode = pNode;
if (key < pNode.key) {
pNode = pNode.leftChild;
} else if (key > pNode.key) {
pNode = pNode.rightChild;
} else {
// 树中已存在匹配给定关键字的结点,则什么都不做直接返回
return;
}
}

if (key < parentNode.key) {
parentNode.leftChild = newNode;
newNode.parent = parentNode;
} else {
parentNode.rightChild = newNode;
newNode.parent = parentNode;
}

}

/**
* insert: 从二叉查找树中删除匹配给定关键字相应的树结点
*
* @param key 给定关键字
*/
public void delete(int key) throws Exception {
TreeNode pNode = search(key);
if (pNode == null) {
throw new Exception("树中不存在要删除的关键字!");
}
delete(pNode);
}

/**
* delete: 从二叉查找树中删除给定的结点.
*
* @param pNode 要删除的结点。前置条件是给定结点在二叉查找树中已经存在
* @throws Exception
*/
private void delete(TreeNode pNode) throws Exception {
if (pNode == null) {
return;
}
if (pNode.leftChild == null && pNode.rightChild == null) { // 该结点既无左孩子结点,也无右孩子结点
TreeNode parentNode = pNode.parent;
if (pNode == parentNode.leftChild) {
parentNode.leftChild = null;
} else {
parentNode.rightChild = null;
}
return;
}
if (pNode.leftChild == null && pNode.rightChild != null) { // 该结点左孩子结点为空,右孩子结点非空
TreeNode parentNode = pNode.parent;
if (pNode == parentNode.leftChild) {
parentNode.leftChild = pNode.rightChild;
pNode.rightChild.parent = parentNode;
} else {
parentNode.rightChild = pNode.rightChild;
pNode.rightChild.parent = parentNode;
}
return;
}
if (pNode.leftChild != null && pNode.rightChild == null) { // 该结点左孩子结点非空,右孩子结点为空
TreeNode parentNode = pNode.parent;
if (pNode == parentNode.leftChild) {
parentNode.leftChild = pNode.leftChild;
pNode.rightChild.parent = parentNode;
} else {
parentNode.rightChild = pNode.leftChild;
pNode.rightChild.parent = parentNode;
}
return;
}

// 该结点左右孩子结点均非空,则删除该结点的后继结点,并用该后继结点取代该结点
TreeNode successorNode = successor(pNode);
delete(successorNode);
pNode.key = successorNode.key;
}

/**
* inOrderTraverseList: 获得二叉查找树的中序遍历结点列表
*
* @return 二叉查找树的中序遍历结点列表
*/
public List<TreeNode> inOrderTraverseList() {
if (nodelist != null) {
nodelist.clear();
}
inOrderTraverse(root);
return nodelist;
}

/**
* inOrderTraverse: 对给定二叉查找树进行中序遍历
*
* @param root 给定二叉查找树的根结点
*/
private void inOrderTraverse(TreeNode root) {
if (root != null) {
inOrderTraverse(root.leftChild);
nodelist.add(root);
inOrderTraverse(root.rightChild);
}
}

/**
* toStringOfOrderList: 获取二叉查找树中关键字的有序列表
*
* @return 二叉查找树中关键字的有序列表
*/
public String toStringOfOrderList() {
StringBuilder sbBuilder = new StringBuilder(" [ ");
for (TreeNode p : inOrderTraverseList()) {
sbBuilder.append(p.key);
sbBuilder.append(" ");
}
sbBuilder.append("]");
return sbBuilder.toString();
}

/**
* 获取该二叉查找树的字符串表示
*/
public String toString() {
StringBuilder sbBuilder = new StringBuilder(" [ ");
for (TreeNode p : inOrderTraverseList()) {
sbBuilder.append(p);
sbBuilder.append(" ");
}
sbBuilder.append("]");
return sbBuilder.toString();
}

public TreeNode getRoot() {
return root;
}

public static void testNode(BinarySearchTree bst, TreeNode pNode)
throws Exception {
System.out.println("本结点: " + pNode);
System.out.println("前趋结点: " + bst.precessor(pNode));
System.out.println("后继结点: " + bst.successor(pNode));
}

public static void testTraverse(BinarySearchTree bst) {
System.out.println("二叉树遍历:" + bst);
System.out.println("二叉查找树转换为有序列表: " + bst.toStringOfOrderList());
}

// 测试
public static void main(String[] args) {
try {
BinarySearchTree bst = new BinarySearchTree();
System.out.println("查找树是否为空? " + (bst.isEmpty() ? "是" : "否"));
int[] keys = new int[] { 15, 6, 18, 3, 7, 13, 20, 2, 9, 4 };
for (int key : keys) {
bst.insert(key);
}
System.out.println("查找树是否为空? " + (bst.isEmpty() ? "是" : "否"));
TreeNode minkeyNode = bst.minElemNode(bst.getRoot());
System.out.println("最小关键字: " + minkeyNode.getKey());
testNode(bst, minkeyNode);
TreeNode maxKeyNode = bst.maxElemNode(bst.getRoot());
System.out.println("最大关键字: " + maxKeyNode.getKey());
testNode(bst, maxKeyNode);
System.out.println("根结点关键字: " + bst.getRoot().getKey());
testNode(bst, bst.getRoot());
testTraverse(bst);
System.out.println("****************************** ");
testTraverse(bst);
} catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}

}


AVL树:平衡二叉树,对于这种树,涉及到旋转。当新插入一个节点时,要判断节点插入之后当前树是否平衡,如果不平衡,肯定是当前插入的节点破坏了原来节点的平衡关系。需要讲三层的子树旋转成两层。具体的几种旋转如图示:



上代码:

package com.BinNode;
/**
* AVL树
*
*/
public class AVLTree<T extends Comparable<? super T>> {

/**
* AVL树节点的定义
*/
private static class AvlNode<T> {
AvlNode(T theElement) {
this(theElement, null, null);
}
AvlNode(T theElement, AvlNode<T> lt, AvlNode<T> rt) {
element = theElement;
left = lt;
right = rt;
height = 0;
}
T element; // 节点中的数据
AvlNode<T> left; // 左儿子
AvlNode<T> right; // 右儿子
int height; // 节点的高度
}

// avl树根
private AvlNode<T> root;

public AVLTree() {
root = null;
}

// 在avl树中插入数据,重复数据复略
public void insert(T x) {
root = insert(x, root);
}

// 在avl中删除数据,没有实现
public void remove(T x) {
System.out.println("Sorry, remove unimplemented");
}

// 在avl树中找最小的数据
public T findMin() throws Exception {
if (isEmpty())
throw new Exception("空");
return findMin(root).element;
}

// 在avl树中找最大的数据
public T findMax() throws Exception {
if (isEmpty())
throw new Exception("空");
return findMax(root).element;
}

// 搜索
public boolean contains(T x) {
return contains(x, root);
}

public void makeEmpty() {
root = null;
}

public boolean isEmpty() {
return root == null;
}

// 排序输出avl树
public void printTree() {
if (isEmpty())
System.out.println("Empty tree");
else
printTree(root);
}

/**
* AVL树的插入,重点就是这儿了
* @param x 要插入的元素
* @param t 树的根节点
* @return
*/
private AvlNode<T> insert(T x, AvlNode<T> t) {
if (t == null){
return new AvlNode<T>(x, null, null);
}

int compareResult = x.compareTo(t.element);

if (compareResult < 0) {
t.left = insert(x, t.left);// 将x插入左子树中

// 打破平衡
if (height(t.left) - height(t.right) == 2){
// LL型(左左型)
if (x.compareTo(t.left.element) < 0){
t = rotateWithLeftChild(t);
}
// LR型(左右型)
else{
t = doubleWithLeftChild(t);
}
}

} else if (compareResult > 0) {
// 将x插入右子树中
t.right = insert(x, t.right);

// 打破平衡
if (height(t.right) - height(t.left) == 2){
// RR型(右右型)
if (x.compareTo(t.right.element) > 0){
t = rotateWithRightChild(t);
}
else{
// RL型
t = doubleWithRightChild(t);
}
}
} else{
; // 重复数据,什么也不做
}

t.height = Math.max(height(t.left), height(t.right)) + 1;// 更新高度
return t;
}

// 带左子树旋转,适用于LL型
private AvlNode<T> rotateWithLeftChild(AvlNode<T> k2) {
AvlNode<T> k1 = k2.left;
k2.left = k1.right;
k1.right = k2;
k2.height = Math.max(height(k2.left), height(k2.right)) + 1;
k1.height = Math.max(height(k1.left), k2.height) + 1;
return k1;
}

// 带右子树旋转,适用于RR型
private AvlNode<T> rotateWithRightChild(AvlNode<T> k1) {
AvlNode<T> k2 = k1.right;
k1.right = k2.left;
k2.left = k1;
k1.height = Math.max(height(k1.left), height(k1.right)) + 1;
k2.height = Math.max(height(k2.right), k1.height) + 1;
return k2;
}

// 双旋转,适用于LR型
private AvlNode<T> doubleWithLeftChild(AvlNode<T> k3) {
k3.left = rotateWithRightChild(k3.left);
return rotateWithLeftChild(k3);
}

// 双旋转,适用于RL型
private AvlNode<T> doubleWithRightChild(AvlNode<T> k1) {
k1.right = rotateWithLeftChild(k1.right);
return rotateWithRightChild(k1);
}

// 找最小
private AvlNode<T> findMin(AvlNode<T> t) {
if (t == null)
return t;

while (t.left != null)
t = t.left;
return t;
}

// 找最大
private AvlNode<T> findMax(AvlNode<T> t) {
if (t == null)
return t;

while (t.right != null)
t = t.right;
return t;
}

// 搜索(查找)
private boolean contains(T x, AvlNode<T> t) {
while (t != null) {
int compareResult = x.compareTo(t.element);

if (compareResult < 0)
t = t.left;
else if (compareResult > 0)
t = t.right;
else
return true; // Match
}

return false; // No match
}

// 前序遍历avl树
private void printTree(AvlNode<T> t) {
if (t != null) {
System.out.println(t.element);
printTree(t.left);
printTree(t.right);
}
}

// 求高度
private int height(AvlNode<T> t) {
return t == null ? -1 : t.height;
}

// Test program
public static void main(String[] args) {
AVLTree<Integer> t = new AVLTree<Integer>();
final int NUMS = 100;
final int GAP = 37;

for (int i = GAP; i != 0; i = (i + GAP) % NUMS){
t.insert(i);
}

t.printTree();
}
}


至此,三种常见的树就完成了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: