java二叉树之堆

堆是完全二叉树的分布逻辑,结构可以用数组或树等实现。在完全二叉树的基础上,加上父节点优先级始终大于子节点优先级的规则,是一种弱序结构。

堆一般用于优先级存储结构,插入和删除根节点比较方便,但是查找、顺序遍历和删除其他关键字节点就不爽了。

堆排序是一种与快排复杂度相同O(NlogN)的排序,效率略低于快排,但是它很稳定,不受数据分布影响,而快排最差效率可降低到O(N*N)。


1,基于数组的堆
import java.util.Random;

class Item {
    int data;

    public Item(int data) {
        this.data = data;
    }
}

public class Heap {
    private Item[] array;
    private int maxSize;
    private int curSize;

    public Heap(int size) {
        maxSize = size;
        curSize = 0;
        array = new Item[size];
    }

    public boolean insert(int key) {
        if (curSize == maxSize)
            return false;
        array[curSize] = new Item(key);
        trickleUp(curSize++);
        return true;
    }

    private void trickleUp(int index) {
        Item bottom = array[index];// 把这个要插入的先提出来
        int parent = (index - 1) / 2;// 父节点索引
        while (index > 0 && array[parent].data < bottom.data) {
            array[index] = array[parent];// 父节点下移
            index = parent;
            parent = (parent - 1) / 2;
        }
        array[index] = bottom;
    }

    public Item remove() {
        Item root = array[0];
        array[0] = array[--curSize];
        trickleDown(0);
        return root;
    }

    private void trickleDown(int index) {
        Item top = array[index];
        int large;
        while (index < curSize / 2) {// 此时至少一个子节点
            int left = 2 * index + 1;
            int right = left + 1;
            // 包含了有无右节点2种情况
            if (right < curSize && array[right].data > array[left].data)
                large = right;
            else
                large = left;
            if (top.data >= array[large].data)
                break;
            array[index] = array[large];
            index = large;
        }
        array[index] = top;
    }
//改变节点优先级
    public boolean change(int index, int newValue) {
        if (index < 0 || index >= curSize)
            return false;
        int oldData = array[index].data;
        if (newValue > oldData)
            trickleUp(index);
        else
            trickleDown(index);
        return true;
    }
//逐层显示,格式没调的好看
    public void display() {
        int n = 1;
        while (true) {
            for (int i = n - 1; i < 2 * n - 1; i++) {
                if (i >= curSize) {
                    System.out.println("\n---------------------------------");
                    return;
                }
                System.out.print(array[i].data + " ");
            }
            System.out.println();
            n *= 2;
        }

    }

//堆排序

public void sort(Item[] beforeSort){
        array=beforeSort;
        maxSize=curSize=beforeSort.length;
        for(int i=0;i<maxSize;i++)
            System.out.print(array[i].data+"/");
        System.out.println();
        //对含有子节点的节点向下调整,一直调整到根,就将无序数组转换成了堆
        for(int j=maxSize/2-1;j>=0;j--)
            trickleDown(j);
        //每次将最高优先级的数据按从数组末端到数组开头的顺序放进去,这样排序不需要第二个数组,直接将无序数组转变成有序数组
        for(int k=maxSize-1;k>0;k--){
            array[k]=remove();

        }

//排序后输出

        for(int i=0;i<maxSize;i++)
            System.out.print(array[i].data+"/");
        System.out.println();
    }
    public static void main(String[] args) {
        Heap heap = new Heap(31);
        Random rand = new Random();
        for (int i = 0; i < 20; i++)
            heap.insert(rand.nextInt(100));
        heap.display();
        heap.remove();
        heap.display();
    }
}

2,使用树构建堆,称为树堆

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

//问题在于找到树的最后一个节点的位置(插入及删除)
class Node {
    Node parent;
    Node left;
    Node right;
    int data;

    public Node(int data) {
        this.data = data;
        parent = null;
        left = null;
        right = null;
    }
}

class TreeHeap {
    Node root;
    int size;

    public TreeHeap() {
        root = null;
        size = 0;
    }

    public void insert(int key) {
        Node current = root;

        Node temp = null;

        //得到要插入位置的信息(逆序的):如要插入位置为1011,表示从根节点到插入位置是右左右右的方向

        List<Integer> list = sizeToCode(++size);

    //从list倒数第二个开始遍历到首个,就是位置的信息

        for (int i = list.size() - 2; i >= 0; i--) {
            temp = current;
            if (list.remove(i) == 1)//1就向右,0向左
                current = current.right;
            else
                current = current.left;
        }
        if (current == root)//第一次插入
            root = new Node(key);
        else {
            current = new Node(key);
            current.parent = temp;
            if (size % 2 == 0)
                temp.left = current;
            else
                temp.right = current;
            thickleUp(current);//跟第一种方法类似的调整
        }
    }

    // 改变节点数据即可
    private void thickleUp(Node node) {
        Node temp = node;
        while (node != root) {
            if (temp.data > node.parent.data) {
                node.data = node.parent.data;
                node = node.parent;
            } else
                break;
        }
        node.data = temp.data;
    }

    public Node remove() {
        if (size == 0)
            return null;
        Node temp = root;
        Node current = root;
        List<Integer> list = sizeToCode(size);
        for (int i = list.size() - 2; i >= 0; i--) {
            if (list.remove(i) == 1)
                current = current.right;
            else
                current = current.left;
        }
        if (current == root) {// size=1
            root = null;
            size--;
        } else {
            root.data = current.data;
            current = null;
            size--;
            thickleDown(root);
        }
        return temp;
    }

    private void thickleDown(Node node) {
        int key = node.data;
        Node large;

        while (node != null) {

            if (node.left != null && node.right != null) {
                if (node.left.data > node.right.data)
                    large = node.left;
                else
                    large = node.right;
                if (key >= large.data)
                    break;
                else {
                    node.data = large.data;
                    node = large;
                }

            } else if (node.left != null) {
                if (key >= node.left.data)
                    break;
                else {
                    node.data = node.left.data;
                    node = node.left;
                }
            } else if (node.right != null) {
                if (key >= node.right.data)
                    break;
                else {
                    node.data = node.right.data;
                    node = node.right;
                }
            } else
                break;
        }
        node.data = key;

    }

    // 找出最后一个节点所处位置,通过树的大小,将其转换成二进制编码,
    // 如第5个节点,5对应二进制101,去掉开头的1,即左右即是它的位置
    // 此时返回的是逆序的,遍历的时候倒过来即可
    public List<Integer> sizeToCode(int size) {
        List<Integer> list = new ArrayList<Integer>();
        while (size >= 1) {
            list.add(size % 2);
            size = size / 2;
        }
        return list;
    }

    public static void main(String[] args) {
        TreeHeap heap = new TreeHeap();
        Random rand = new Random(137);
        int[] array = new int[20];
        for (int i = 0; i < 20; i++) {
            array[i] = rand.nextInt(1000);
        }
        for (int a : array)
            heap.insert(a);
        while (heap.size > 0) {
            System.out.print(heap.remove().data + "/");
        }
    }
}



猜你喜欢

转载自blog.csdn.net/qq_26567507/article/details/80173587
今日推荐