编程设计之Java程序员

数据结构——树

2018-07-12  本文已影响178人  我哈啊哈啊哈

目录

1、什么是树

2、相关术语

3、二叉树

3.1、二叉树的类型

3.2、二叉树的性质

3.3、二叉树的结构

3.4、二叉树的遍历

3.4.1、遍历的分类
3.4.1.1、前序遍历
3.4.1.2、中序遍历
3.4.1.3、后序遍历
3.4.1.4、层次遍历

4、通用树(N叉树)

4.1、通用树的表示

4.2、代码实现

5、线索二叉树

5.1、常规二叉树遍历的问题

5.2、线索二叉树的动机

5.3、线索二叉树的类型

5.4、线索二叉树的代码实现

5.5、线索二叉树的示例

5.6、线索二叉树的操作

5.6.1、中序线索二叉树中查找中序后继
5.6.2、中序线索二叉树的中序遍历
5.6.3、中序线索二叉树中查找前序后继
5.6.4、中序线索二叉树的前序遍历
5.6.5、中序线索二叉树插入结点

6、表达式树

6.1、表达式树举例

6.2、表达式树代码实现

7、二叉搜索树

7.1、二叉搜索树的作用

7.2、二叉搜索树的性质

7.3、二叉搜索树的代码实现

7.4、二叉搜索树的操作

7.4.1、在二叉搜索树中寻找元素
7.4.2、在二叉搜索树中寻找最小元素
7.4.3、在二叉搜索树中寻找最大元素
7.4.4、在二叉搜索树中寻找中序序列前驱和后继
7.4.5、在二叉搜索树中插入元素
7.4.6、在二叉搜索树中删除元素

8、平衡二叉搜索树

8.1、完全平衡二叉搜索树

8.2、AVL树

8.2.1、AVL树的性质
8.2.2、AVL树的最小/最大结点
8.2.3、AVL树的定义
8.2.4、AVL树的旋转
8.2.4.1、单旋转
8.2.4.2、双旋转
8.2.5、AVL树的插入操作

正文

1、什么是树

2、相关术语

图2-1 树 图2-2 树的层 图2-3 斜树

3、二叉树

3.1、二叉树的类型

3.2、二叉树的性质

假定树的高度为h,且根结点的深度为0。


图3-5 二叉树性质

从图3-5可得出如下性质:

3.3、二叉树的结构

public class BinaryTreeNode {
    private int data;
    private BinaryTreeNode left;
    private BinaryTreeNode right;

    public int getData(){
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public BinaryTreeNode getLeft() {
        return left;
    }

    public void setLeft(BinaryTreeNode left) {
        this.left = left;
    }

    public BinaryTreeNode getRight() {
        return right;
    }

    public void setRight(BinaryTreeNode right) {
        this.right = right;
    }
}

3.4、二叉树的遍历

访问树中所有结点的过程叫做遍历,遍历的目标是按照某种特定的顺序访问树的所有结点。

3.4.1、遍历的分类
3.4.1.1、前序遍历
C:\Application\java\jdk\bin\java.exe ..."
前序遍历结果:
1
2
4
5
3
6
7

Process finished with exit code 0
    public void PreOrder(BinaryTreeNode root){
        if(root!=null){
            System.out.print(root.getData());
            PreOrder(root.getLeft());
            PreOrder(root.getRight());
        }
    }
   public void PreOrderNonRecursive(BinaryTreeNode root){
        if(root==null){
            return;
        }
        Stack s=new Stack();
        while (true){
            while (root!=null){
                System.out.print(root.getData());
                s.push(root);
                root=root.getLeft();
            }
            if(s.isEmpty()){
                break;
            }
            root=(BinaryTreeNode) s.pop();
            root=root.getRight();
        }
    }
3.4.1.2、中序遍历
C:\Application\java\jdk\bin\java.exe ..."
中序遍历结果:
4
2
5
1
6
3
7

Process finished with exit code 0
    public void InOrder(BinaryTreeNode root){
        if(root!=null){
            InOrder(root.getLeft());
            System.out.print(root.getData());
            InOrder(root.getRight());
        }
    }
    public void InOrderNonRecursive(BinaryTreeNode root){
        if(root==null){
            return;
        }
        Stack s=new Stack();
        while (true){
            while (root!=null){
                s.push(root);
                root=root.getLeft();
            }
            if(s.isEmpty()){
                break;
            }
            root=(BinaryTreeNode)s.pop();
            System.out.print(root.getData()+"\n");
            root=root.getRight();
        }
    }
3.4.1.3、后序遍历
C:\Application\java\jdk\bin\java.exe ..."
后序遍历结果:
4
5
2
6
7
3
1

Process finished with exit code 0
    public void PostOrder(BinaryTreeNode root){
        if(root!=null){
            PostOrder(root.getLeft());
            PostOrder(root.getRight());
            System.out.print(root.getData());
        }
    }
     public void PostOrderNonRecursive(BinaryTreeNode root){
        Stack stack=new Stack();
        while (true){
            if(root!=null){
                //寻找最左叶子结点
                stack.push(root);
                root=root.getLeft();
            }else {
                if(stack.isEmpty()){
                    return;
                }else {
                    //判断当前结点是否有右子节点
                    if(stack.top().getRight==null){
                        root=stack.pop();
                        System.out.print(root.getData()+"\n");
                        //判断该结点是否为栈顶右子节点
                        while(root==stack.top().getRight()){
                            System.out.print(stack.top().getData()+"\n");
                            root=stack.pop();
                            if(stack.isEmpty()){
                                return;
                            }
                        }
                    }
                }
                if(!stack.isEmpty()){
                    //遍历结点右子树
                    root=stack.top().getRight();
                }else {
                    root=null;
                }
            }
        }
    }
3.4.1.4、层次遍历
C:\Application\java\jdk\bin\java.exe ..."
层次遍历结果:
1
2
3
4
5
6
7

Process finished with exit code 0
public void LevelOrder(BinaryTreeNode root){
        BinaryTreeNode temp;
        LLBinaryTreeQueue queue=LLBinaryTreeQueue.creteQueue();
        if(root==null){
            return;
        }
        queue.enQueue(root);
        while (!queue.isEmpty()){
            temp=queue.deQueue();
            //处理当前结点
            System.out.print(temp.getData()+"\n");
            if(temp.getLeft()!=null){
                queue.enQueue(temp.getLeft());
            }
            if(temp.getRight()!=null){
                queue.enQueue(temp.getRight());
            }
        }
    }

4、通用树(N叉树)

4.1、通用树的表示

4.2、代码实现

public class TreeNode {
    private int data;
    private TreeNode firstChild;
    private TreeNode nextSibling;

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public TreeNode getFirstChild() {
        return firstChild;
    }

    public void setFirstChild(TreeNode firstChild) {
        this.firstChild = firstChild;
    }

    public TreeNode getNextSibling() {
        return nextSibling;
    }

    public void setNextSibling(TreeNode nextSibling) {
        this.nextSibling = nextSibling;
    }
}

5、线索二叉树

5.1、常规二叉树遍历的问题

5.2、线索二叉树的动机

5.3、线索二叉树的类型

根据线索中存储的信息是依据前序、中序或后序排列,线索二叉树有以下3种类型:

5.4、线索二叉树的结构

如图所示:


图5-1 线索二叉树结构示例 图5-2 常规二叉树与线索二叉树区别

代码实现:

public class ThreadedBinaryTreeNode {
    private ThreadedBinaryTreeNode left;
    private int LTag;
    private int data;
    private int RTag;
    private ThreadedBinaryTreeNode right;

    public ThreadedBinaryTreeNode getLeft() {
        return left;
    }

    public void setLeft(ThreadedBinaryTreeNode left) {
        this.left = left;
    }

    public int getLTag() {
        return LTag;
    }

    public void setLTag(int LTag) {
        this.LTag = LTag;
    }

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public int getRTag() {
        return RTag;
    }

    public void setRTag(int RTag) {
        this.RTag = RTag;
    }

    public ThreadedBinaryTreeNode getRight() {
        return right;
    }

    public void setRight(ThreadedBinaryTreeNode right) {
        this.right = right;
    }
}

5.5、线索二叉树的示例

5.6、线索二叉树的操作

5.6.1、中序线索二叉树中查找中序后继
ThreadedBinaryTreeNode InorderSuccessor(ThreadedBinaryTreeNode P){
        ThreadedBinaryTreeNode position;
        if(P.getRTag()==0){
            return P.getRight();
        }else {
            position=P.getRight();
            while (position.getLTag()==1){
                position=position.getLeft();
            }
            return position;
        }
    }
5.6.2、中序线索二叉树的中序遍历
void InorderTraversal(ThreadedBinaryTreeNode root){
        ThreadedBinaryTreeNode p=InorderPrecursor(root);
        while (p!=root){
            p=InorderPrecursor(p);
            System.out.print(p.getData());
        }
    }
5.6.3、中序线索二叉树中查找前序后继
ThreadedBinaryTreeNode PreorderSuccessor(ThreadedBinaryTreeNode p){
        ThreadedBinaryTreeNode position;
        if(p.getLTag()==1){
            return p.getLeft();
        }else {
            position=p;
            while (position.getRTag()==0){
                position=position.getRight();
            }
            return p.getRight();
        }
    }
5.6.4、中序线索二叉树的前序遍历
void PreorderTraversal(ThreadedBinaryTreeNode root){
        ThreadedBinaryTreeNode p=PreorderSuccessor(root);
        while (p!=root){
            p=PreorderSuccessor(p);
            System.out.print(p.getData());
        }
    }
5.6.5、中序线索二叉树插入结点

假设有两个结点P和Q,现在要将Q连接到P的右边。

void InsertRightInoderTBT(ThreadedBinaryTreeNode p,ThreadedBinaryTreeNode q){
        ThreadedBinaryTreeNode temp;
        q.setRight(p.getRight());
        q.setRTag(p.getRTag());
        q.setLeft(p);
        q.setLTag(0);
        p.setRTag(1);
        if(q.getRTag()==1){
            //第二种情况
            temp=q.getRight();
            while (temp.getLTag()==1){
                temp=temp.getLeft();
            }
            temp.setLeft(q);
        }
    }

6、表达式树

6.1、表达式树举例

6.2、表达式树代码实现

BinaryTreeNodeChar BuildExprTree(char[] postfixExpr,int size){
        LLBinaryTreeStack s=new LLBinaryTreeStack();
        for(int i=0;i<size;i++){
            if(postfixExpr[i]!='+'&&postfixExpr[i]!='-'&&postfixExpr[i]!='*'&&postfixExpr[i]!='/'){
                BinaryTreeNodeChar newNode=new BinaryTreeNodeChar(postfixExpr[i],null,null);
                s.push2(newNode);
            }
            else {
                BinaryTreeNodeChar T2=s.pop2();
                BinaryTreeNodeChar T1=s.pop2();
                BinaryTreeNodeChar newNode=new BinaryTreeNodeChar(postfixExpr[i],T1,T2);
                s.push2(newNode);
            }
        }
        return s.top2();
    }

7、二叉搜索树

7.1、二叉搜索树的作用

7.2、二叉搜索树的性质

7.3、二叉搜索树的代码实现

public class BinarySearchTreeNode {
    private int data;
    private BinarySearchTreeNode left;
    private BinarySearchTreeNode right;

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public BinarySearchTreeNode getLeft() {
        return left;
    }

    public void setLeft(BinarySearchTreeNode left) {
        this.left = left;
    }

    public BinarySearchTreeNode getRight() {
        return right;
    }

    public void setRight(BinarySearchTreeNode right) {
        this.right = right;
    }
}

7.4、二叉搜索树的操作

7.4.1、在二叉搜索树中寻找元素
    BinarySearchTreeNode FindRecursive(BinarySearchTreeNode root,int data){
        if(root==null){
            return null;
        }
        if(data<root.getData()){
            return FindRecursive(root.getLeft(),data);
        }else if(data>root.getData()){
            return FindRecursive(root.getRight(),data);
        }
        return root;
    }
    BinarySearchTreeNode Find(BinarySearchTreeNode root,int data){
        if(root==null){
            return null;
        }
        while (root!=null){
            if(data==root.getData()){
                return root;
            }else if(data<root.getData()){
                root=root.getLeft();
            }else {
                root=root.getRight();
            }
        }
        return null;
    }
7.4.2、在二叉搜索树中寻找最小元素
    BinarySearchTreeNode FindMinRecursive(BinarySearchTreeNode root){
        if(root==null){
            return null;
        }else {
            if(root.getLeft()==null){
                return root;
            }else {
                return FindMinRecursive(root.getLeft());
            }
        }
    }
    BinarySearchTreeNode FindMin(BinarySearchTreeNode root){
        if(root==null){
            return null;
        }
        while (root.getLeft()!=null){
            root=root.getLeft();
        }
        return root;
    }
7.4.3、在二叉搜索树中寻找最大元素
    BinarySearchTreeNode FindMaxRecursive(BinarySearchTreeNode root){
        if(root==null){
            return null;
        }else {
            if(root.getRight()==null){
                return root;
            }else {
                return FindMaxRecursive(root.getRight());
            }
        }
    }
    BinarySearchTreeNode FindMax(BinarySearchTreeNode root){
        if(root==null){
            return null;
        }
        while (root.getRight()!=null){
            root=root.getRight();
        }
        return root;
    }
7.4.4、在二叉搜索树中寻找中序序列前驱和后继
7.4.5、在二叉搜索树中插入元素
    BinarySearchTreeNode Insert(BinarySearchTreeNode root,int data){
        if(root==null){
            root=new BinarySearchTreeNode();
            root.setData(data);
            root.setLeft(null);
            root.setRight(null);
        }else {
            if(data<root.getData()){
                root.setLeft(Insert(root.getLeft(),data));
            }else if(data>root.getData()){
                root.setRight(Insert(root.getRight(),data));
            }
        }
        return root;
    }
7.4.6、在二叉搜索树中删除元素
    BinarySearchTreeNode Delete(BinarySearchTreeNode root,int data){
        BinarySearchTreeNode temp;
        if(root==null){
            System.out.print("Element not there in tree");
        }
        else if(data<root.getData()){
            root.left=Delete(root.getLeft(),data);
        }
        else if(data>root.getData()){
            root.right=Delete(root.getRight(),data);
        }
        else {//找到该元素
            if(root.getLeft()!=null&&root.getRight()!=null){
                //用左子树的最大值代替
                temp=FindMax(root.getLeft());
                root.setData(temp.getData());
                root.left=Delete(root.getLeft(),root.getData());
            }else{
                //一个孩子结点
                if(root.getLeft()==null){
                    root=root.getRight();
                }
                if(root.getRight()==null){
                    root=root.getLeft();
                }
            }
        }
        return root;
    }

8、平衡二叉搜索树

8.1、完全平衡二叉搜索树

8.2、AVL树

8.2.1、AVL树的性质

如下图8-2 所示,左边的树不是AVL树,右边的是AVL树。


图8-2 AVL树示例
8.2.2、AVL树的最小/最大结点树

假定AVL树的高度是h,N(h)表示高度为h的AVL树的结点数。

8.2.3、AVL树的定义
public class AVLTreeNode {
    private int data;
    private int height;
    private AVLTreeNode left;
    private AVLTreeNode right;

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public AVLTreeNode getLeft() {
        return left;
    }

    public void setLeft(AVLTreeNode left) {
        this.left = left;
    }

    public AVLTreeNode getRight() {
        return right;
    }

    public void setRight(AVLTreeNode right) {
        this.right = right;
    }
}
8.2.4、AVL树的旋转
8.2.4.1、单旋转
    AVLTreeNode SingleRotateLeft(AVLTreeNode X){
        AVLTreeNode W=X.getLeft();
        //将结点W的右子树设置为结点X的左孩子
        X.setLeft(W.getRight());
        //将结点X设置为W的右孩子
        W.setRight(X);
        //计算结点X的高度
        X.setHeight(Math.max(Height(X.getLeft()),Height(X.getRight()))+1);
        //计算结点W的高度
        W.setHeight(Math.max(Height(W.getLeft()),X.getHeight())+1);
        return W;
    }

    AVLTreeNode SingleRotateRight(AVLTreeNode W){
        AVLTreeNode X=W.getRight();
        //将结点X的左子树设置为W的右孩子
        W.setRight(X.getLeft());
        //将结点W设置为结点X的左孩子
        X.setLeft(W);
        //计算结点W的高度
        W.setHeight(Math.max(Height(W.getLeft()),Height(W.getRight()))+1);
        //计算结点X的高度
        X.setHeight(Math.max(Height(X.getRight()),W.getHeight())+1);
        return X;
    }
8.2.4.2、双旋转
    AVLTreeNode DoubleRotatewithLeft(AVLTreeNode Z){
        Z.setLeft(SingleRotateRight(Z.getLeft()));
        return SingleRotateLeft(Z);
    }

代码实现如下:

    AVLTreeNode DoubleRotatewithRight(AVLTreeNode X){
        X.setLeft(SingleRotateLeft(X.getRight()));
        return SingleRotateRight(X);
    }
8.2.5、AVL树的插入操作
    AVLTreeNode Insert(AVLTreeNode root,AVLTreeNode parent,int data){
        if(root==null){
            root=new AVLTreeNode();
            root.setData(data);
            root.setHeight(0);
            root.setLeft(null);
            root.setRight(null);
        }
        else if(data<root.getData()){
            root.setLeft(Insert(root.getLeft(),root,data));
            if((Height(root.getLeft())-Height(root.getRight()))==2){
                if(data<root.getLeft().getData()){
                    //插入在左孩子的左子树中,对root执行左左旋转操作
                    root=SingleRotateLeft(root);
                }else {
                    //插入在左孩子的右子树中,对root执行左右旋转操作
                    root=DoubleRotatewithLeft(root);
                }
            }
        }
        else if(data>root.getData()){
            root.setRight(Insert(root.getRight(),root,data));
            if((Height(root.getRight())-Height(root.getLeft()))==2){
                if(data>root.getRight().getData()){
                    //插入在右孩子的右子树中,对root执行右右旋转操作
                    root=SingleRotateRight(root);
                }else {
                    //插入在右孩子的左子树中,对root执行右左旋转操作
                    root=DoubleRotatewithRight(root);
                }
            }
        }
        /*
            否则,数据已经在树中,不必做任何操作
         */
        root.setHeight(Math.max(Height(root.getLeft()),Height(root.getRight()))+1);
        return root;
    }
上一篇下一篇

猜你喜欢

热点阅读