(五)二叉树(Java实现堆排序、中序排序)

树:一个结点下面有多个子结点。            二叉树:一个节点下面最多有两个子结点

关于二叉树的一些概念,可以参考:https://www.cnblogs.com/idorax/p/6441043.html

下面主要是总结以下两点:

1.使用数组表示二叉树,例:堆排序(这里演示小根堆);

2.使用链表表示二叉树,例:中序排序。


一、用数组表示二叉树

在此不再深究二叉树的种种定义,用数组表示二叉树里讲的是完全二叉树,下面就简述为二叉树,

!!!程序里面就是一个数组,为了方便理解,我们把它画成了一颗二叉树,大脑里把它想象成一颗二叉树,程序里面是不存在二叉树的,也不存在父结点,子结点这些概念,我们唯一知道的就是数组的长度,父结点的个数,子结点的。

先看下面的例子:

[2,5,4,8,1,7,3,9,6]

注意:第一个元素放什么数都是没有意义的,所以上面这个数组中元素2是不用考虑的。


1.建堆

1.1儿子节点中比较出最大的儿子结点;

1.2最大的儿子结点与父节结比,若比父结点大,就与父结点交换。


下面我们来推算一下父结点个数、父结点索引、儿子结点索引:

数组长度123456789...n
父结点个数011122334...(n-1)/2
父结点索引-1111~21~21~31~31~4...1 ~ (n-1)/2
左儿子结点索引=父结点索引*2  ; 右儿子结点索引=父结点索引*2+1

 

下面用代码实现 建堆
package com.review08.sort;

public class HeapSort {
    public static void buildheap(int[] arr) {
        int size = arr.length;
        //循环所有的父节点(从后向前循环)
        for(int i=(size-1)/2; i>=1; i--) {
            //1.1先找到最大的儿子
            int maxIndex = i*2;//假设最大的儿子是左儿子
            //右儿子存在,且比左儿子大
            if(maxIndex+1<size && arr[maxIndex+1]>arr[maxIndex]) {
                maxIndex ++; //把右儿子标记为最大的儿子
            }
            //1.2最大的儿子与父亲比较,儿子大的话,就交换
            if(arr[maxIndex]>arr[i]) {
                int temp = arr[maxIndex];
                arr[maxIndex] = arr[i];
                arr[i] = temp;
            }
        }
    }
    public static void main(String[] args) { //测试
        int []arr = {2,5,4,8,1,7,3,9,6};
        buildheap(arr);
        for (int i=1; i<arr.length; i++) {
            System.out.print(arr[i]+"  ");
        }
    }
}

打印结果:


2.将根节点与最后一个结点交换

上面代码中for循环(外)后面加上:

int temp = arr[1];
arr[1] = arr[size-1];
arr[size-1] = temp;

3.重复第1步和第2步,(每一次重复都不考虑从根节点换下来的那个结点,要排序的部分越来越小)

最后排序方法的代码为(只加了while循环和size--):

package com.review08.sort;

public class HeapSort {
    public static void buildheap(int[] arr) {
        int size = arr.length;
        while(size>2) {
            //循环所有的父结点(从后向前循环)
            for(int i=(size-1)/2; i>=1; i--) {
                //1.1先找到最大的儿子
                int maxIndex = i*2;//假设最大的儿子是左儿子
                //右儿子存在,且比左儿子大
                if(maxIndex+1<size && arr[maxIndex+1]>arr[maxIndex]) {
                    maxIndex ++; //把右儿子标记为最大的儿子
                }
                //1.2最大的儿子与父亲比较,儿子大的话,就交换
                if(arr[maxIndex]>arr[i]) {
                    int temp = arr[maxIndex];
                    arr[maxIndex] = arr[i];
                    arr[i] = temp;
                }
            }
            //2.将根节点与最后一个结点交换
            int data = arr[1];
            arr[1] = arr[size-1];
            arr[size-1] = data;

            size --;
        }
    }

    public static void main(String[] args) {
        int []arr = {2,5,4,8,1,7,3,9,6};
        buildheap(arr);
        for (int i=1; i<arr.length; i++) {
            System.out.print(arr[i]+"  ");
        }
    }
}

其实堆排序的效率并不高,用的不多,不过我们可以借鉴它的思想,扩展我们的思维。



二、用链表表示二叉树

这里讲的二叉树就是普通的二叉树!!!

和之前写LinkedList一样,先要定义一个结点类:

package com.review07.linkedTree;

public class Node {
    private int value;
    private Node left;
    private Node right;

    public Node(int value) {
        this.value = value;
    }

    public int getValue() {

        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public Node getLeft() {
        return left;
    }

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

    public Node getRight() {
        return right;
    }

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


2.1、添加结点

下面模拟顺序二叉树添加结点的过程:

添加以下结点:6,9,5,1,3,4,7,8,0

下面用代码模拟添加结点:

package com.review07.linkedTree;

//顺序二叉树(小的在左边,大的在右边)
public class DoubleTree {
    Node root;//根节点

    //添加是第一个数作为根节点,后面添加的依次与根节点比较
    public void add(int value) {
        Node newNode = new Node(value);
        if(root == null) { //空二叉树
            root = newNode;
        }else{
            Node temp = root;  //从根开始比较起
            while(true) {
                if(value<temp.getValue()) { //小的放左边
                    if(temp.getLeft() == null) {
                        temp.setLeft(newNode);
                        break;
                    }else {
                        temp = temp.getLeft();
                    }
                }else {   //大的放右边
                    if(temp.getRight() == null) {
                        temp.setRight(newNode);
                        break;
                    }else {
                        temp = temp.getRight();
                    }
                }
            }
        }
    }

}


2.2、二叉树的遍历

树的遍历可以分为前序遍历、中序遍历和后序遍历

所谓的前序、中序、后序是相对于根结点而言的。前序表示先遍历根结点,再分别遍历左右两个结点;中序表示先遍历左结点,再遍历根结点,最后遍历右边的结点;后序遍历表示先分别遍历左右两个结点,最后遍历根结点

看下面几个例子,可以比较比较:



从上面两个例子可以看到:(1)前序遍历时根结点始终在第一个被遍历,(2)后序遍历时根结点始终在最后一个被遍历

(3)中序遍历时根结点的位置是不确定的,但是很明显可以看到的是中序遍历后,结点值都是从小到大排列的!


由此我们可以用中序遍历进行排序,不过前提是——要排序的二叉树必须是顺序二叉树!

package com.review07.linkedTree;

//顺序二叉树(小的在左边,大的在右边)
public class DoubleTree {
    Node root;//根结点

    //添加是第一个数作为根结点,后面添加的依次与根结点比较
    public void add(int value) {
        Node newNode = new Node(value);
        if(root == null) {
            root = newNode;
        }else{
            Node temp = root;
            while(true) {
                if(value<temp.getValue()) { //小的放左边
                    if(temp.getLeft() == null) {
                        temp.setLeft(newNode);
                        break;
                    }else {
                        temp = temp.getLeft();
                    }
                }else {   //大的放右边
                    if(temp.getRight() == null) {
                        temp.setRight(newNode);
                        break;
                    }else {
                        temp = temp.getRight();
                    }
                }
            }
        }
    }

    public void show() {
        showNode(root);
    }

    public void showNode(Node parentnode) { //这里用到了递归!!!
//        System.out.print("前序遍历"+parentnode.getValue()+" "); //放在这里表示前序遍历
        if(parentnode.getLeft() != null) {
            showNode(parentnode.getLeft());
        }
        System.out.print("中序遍历"+parentnode.getValue()+" ");  //放在这里表示中序遍历
        if(parentnode.getRight() != null) {
            showNode(parentnode.getRight());
        }
//        System.out.print("后序遍历"+parentnode.getValue()+" ");    //放在这里表示后序遍历
    }

    public static void main(String[] args) {
        DoubleTree doubleTree = new DoubleTree();
        doubleTree.add(6);
        doubleTree.add(4);
        doubleTree.add(5);
        doubleTree.add(2);
        doubleTree.add(8);
        doubleTree.add(1);
        doubleTree.add(9);

        doubleTree.show();
    }
}

中序遍历:





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值