堆排序

· 堆数据结构的效率使它引出一种出奇简单,却很有效率的排序算法,称为堆排序。

· 堆排序的基本思想是使用普通的 insert()例程在堆中插入全部无序的数据项,然后重复用remove()的例程,就可以按序移除所有的数据项。

ForExample:

for (j=0; j<size; j++)
    theHeap.insert(anArray[j]);    // 将没有排序的数组插入堆
for (j=0; j<size; j++)
    anArray[j] = theHeap.remove(); // 利用堆结构的特性,再将其挨个拿出,得到的就是已经排好序的数组

      因为inset()remove() 方法操作的时间复杂度都是O(logN),并且每个方法都要执行N次。所以整个排序操作需要O(N*logN)。这和快速排序在算法复杂度上是一样的,但是它又没有快排速度快因为 trickleDown 中的 while 循环中的操作比快排里循环中的操作多。
      但是我可以做一些事情,加快它的速度:1. 节省时间 2. 节省内存

1.节省时间

向下筛选到适当的位置上:

      如果在堆中插入N个新的数据项,则需要应用 trickleUp() 方法N次,但是可能使用所有的数据项在数组中都是任意排列的,然后再重新排列成堆,这样就只应用了N/2次的trickleDown()方法。这个可以使速度稍稍提升。

由两个正确的子堆形成的一个正确的堆:

      如果有一个不遵守堆有序条件的项占据了根的位置,而它们两个子堆却都是正确的堆,用 trickleDown() 方法也能够创建一个正确的堆。
      这就提出了一个把无序数组变成堆的方法。对(潜在的)堆底层的节点应用 trickleDown() 方法,也就是说,从数组末端的节点开始。然后上行直到根的各个节点都应用此方法。在每一步应用方法时,该节点下面的子堆都是正确的堆,因为已经对他们用过 trickleDown 方法了。在对根应用了 trickleDown 之后,无序数组将转化为堆。
      注意在底层的终端节点,就是那些没有子节点的节点,都已经时正确的堆了,因为他们是单节点的树,没有违背堆的条件。因此,不用堆这些节点应用 trickleDown。可以从节点N/2-1,即最右一个有子节点的节点开始,而不是从节点N-1,最后一个节点开始应用 trickleDown 。这样筛选操作就只需要执行 insert 方法的 N 次的一半就够了。

2.节省内存

使用同一个数组:

      原始代码片段显示了数组中的无序数据,然后把数据插入堆中,最后从堆中移除它并把它有序的写回到数组中,这个过程需要两个大小为N的数组:初始数组和用于堆的数组。
      事实上,堆和初始数组可以使用同一个数组。这样堆排序所需的存储空间减少了一半,除了初始数组之外不需要额外的存储空间。
      每次从堆顶移除一个数据项,堆数组的末端单元就会变为空,堆减少一个节点。可以把最近移除的节点放到这个新空出的单元中。移除的节点越来越多,堆数组就越来越少,但是有序数组却越来越大。因此,只要稍微计划一下,有序数组和堆数组就可以共同使用一块存储空间。

上代码:

/**
 * 使用二叉树实现的堆 类
 */
public class Heap{
    private Node[] heapArray;
    private int maxSize;
    private int currentSize;

    public Heap(int max) {
        maxSize = max;
        currentSize = 0;
        heapArray = new Node[maxSize];
    }

    public boolean isEmpty() {
        return currentSize == 0;
    }

    public void insertAt(int index, Node newNode) {
        heapArray[index] = newNode;
    }

    public void incrementSize() {
        currentSize++;
    }

    public Node remove() {
        Node root = heapArray[0];
        heapArray[0] = heapArray[--currentSize];
        trickleDown(0); // 向下调整
        return root;
    }

    public void trickleDown(int index) {
        int largerChild;
        Node top = heapArray[index];
        while (index < currentSize/2) {
            int leftChild = 2 * index + 1;
            int rightChild = leftChild + 1;
            if (rightChild < currentSize && heapArray[leftChild].getKey() < heapArray[rightChild].getKey())
                largerChild = rightChild;
            else
                largerChild = leftChild;

            if (top.getKey() >= heapArray[largerChild].getKey()) break;

            heapArray[index] = heapArray[largerChild];
            index = largerChild;
        }
        heapArray[index] = top;
    }

    public void displayHeap() { // 树状方式显示
        int nBlanks = 32; // 处理空格的输入
        int itemsPerRow = 1;
        int column = 0;
        int j = 0;
        String dots = ".............................";
        System.out.println(dots + dots);
        while (currentSize > 0) {
            if (column == 0)
                for (int k=0; k<nBlanks; k++)
                    System.out.print(" ");
            System.out.print(heapArray[j].getKey());
            if (++j == currentSize) break;  //显示完了
            if (++column == itemsPerRow) {
                nBlanks /= 2;
                itemsPerRow *= 2;
                column = 0;
                System.out.println();
            } else
                for (int k=0; k<nBlanks*2-2; k++) System.out.print(' ');
        }
        System.out.println("\n" + dots + dots);
    }

    public void displayArray() {
        for (int j=0; j<maxSize; j++) {
            System.out.print(heapArray[j].getKey() + "   ");
        }
        System.out.println();
    }
}

/**
 * 堆排序 类
 */
import java.util.*;
public class HeapSort {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int size, j;
        System.out.println("Enter number of items:");
        size = sc.nextInt();
        Heap theHeap = new Heap(size);
        for (j=0; j<size; j++) {
            int random = (int)(java.lang.Math.random() * 100);  // 创建随机数  作为堆排序的数据项
            Node newNode = new Node(random);
            theHeap.insertAt(j, newNode);   // 把随机生成的数据创建成节点后 放入堆中
            theHeap.incrementSize();    //  数量递增
        }
        // 将随机生成无序的数组显示出来看一下
        System.out.print("Random:");
        theHeap.displayArray();

        // 从最后一个元素的父节点开始向下调整,一直倒根
        // 调整完之后就变成标准的堆
        for (j=size/2-1; j>=0; j--)
            theHeap.trickleDown(j);
        System.out.print("Heap:");
        theHeap.displayArray(); // 按数组顺序打印
        theHeap.displayHeap();  // 按树状顺序打印

        // 通过循环删除最大项  再把删除的数据放到数组中指定的位置
        // 产生的结果就是一个从小到大的有序数组
        for (j=size-1; j>=0; j--) {
            Node biggestNode = theHeap.remove();    // 取出最大的数据项
            theHeap.insertAt(j, biggestNode);
        }
        System.out.print("Sorted:");
        theHeap.displayArray();
    }
}

执行结束之后是这样:

Enter number of items:
50
Random:29   12   91   89   69   27   13   68   37   80   32   29   20   90   72   7   4   19   44   4   13   79   61   60   15   73   48   14   16   0   98   65   39   96   55   48   19   64   99   8   11   21   39   6   75   47   60   25   40   47   
Heap:99   96   98   89   80   73   91   68   64   69   79   60   48   90   72   65   55   48   44   11   39   75   61   40   47   20   27   14   16   0   13   7   39   4   12   19   19   37   29   8   4   21   13   6   32   47   60   25   29   15   
..........................................................
                                99
                96                              98
        89              80              73              91
    68      64      69      79      60      48      90      72
  65  55  48  44  11  39  75  61  40  47  20  27  14  16  0  13
 739412191937298421136324760252915
..........................................................
Sorted:0   4   4   6   7   8   11   12   13   13   14   15   16   19   19   20   21   25   27   29   29   32   37   39   39   40   44   47   47   48   48   55   60   60   61   64   65   68   69   72   73   75   79   80   89   90   91   96   98   99   

Process finished with exit code 0
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值