堆排序

来源:互联网 发布:机器人对话数据库 编辑:程序博客网 时间:2024/06/03 17:11

最近在学习算法,学习过不久之后总会忘得一干二净,但是算法对程序员又很重要,也不得不学啊!昨天重新学习了堆排序,今天花时间重新整理一下思路。

预备知识
堆排序是通过二叉堆实现的,堆是一颗被完全填满的二叉树,例如下图是一颗完全二叉树。
这里写图片描述

如果把所有的元素放到数组中:
这里写图片描述
如果把某个根节点的下标设为i,那么它的左子节点的下标的2i+1,右子节点的下标的2i。这个规律是进行堆操作的基础。
规定:在一个堆中,对于每一个节点x,x的父节点的小于(或等于)x,根节点除外。
如下图,是一个树堆:
这里写图片描述
对于堆的操作,又分为上滤和下滤。
上滤:将一个元素插入到堆中,在堆的最后一个元素后面新建一个空的位置,如果插入该元素不破坏堆的性质,那么插入完成,如果破坏了堆的性质,那么就要对其进行调整。要在上图中插入一个元素1,首先建立一个空的位置
这里写图片描述
,如果把1插入,会破坏堆的性质,那么就把1和它的父节点进行比较,它的父节点比它大,那么把它和父节点进行交换:
这里写图片描述
如果把1插入,那么还是不符合堆的性质,那么就在和1的父节点进行比较,进行位置交换:
这里写图片描述
如果它还有父节点,就接着进行比较,发现它的父节点比它小为止:
这里写图片描述
最后操作完成,这个过程称为上滤。
下滤:假如要删除一个最小的元素,那很简单,只要删除根节点就可以了,但是,删除之后会破坏堆的性质,我们把堆中的最后一个元素放到跟节点,假设删除完成,例如要删除1,删除之后,把最后一个元素放到根节点:
这里写图片描述
但是这样会破坏堆的性质,我们从子节点中选出一个较小的元素,先从左子节点开始,左子节点元素比8小,进行交换:
这里写图片描述
继续进行交换:
这里写图片描述
最后8被放到正确的位置,整个操作称为下滤。
如果每次把树堆中的最小元素输出到一个数组中,那么它就是一个从小到大的排序,堆排序就是基于这样的思想。

把思路缕清楚之后,就可以开始分析代码了。

//接收一个数组public static void heapsort(int[] a) {        for (int i = a.length/2 - 1; i >= 0; i--) {            //这里传入的是最后一个节点的父节点,至于为什么这么做,可以参看前面的分析,另外一个是数组的长度,既是最后一个节点。            percDown(a, i, a.length);   //排序的核心        }    }

为了方便得到一个元素的左子节点,单独写一个方法

private static int leftChild(int i) {        return 2 * i + 1;    }
private static void percDown(int[] a, int i, int n) {        int child;         int tmp;           //如果该节点位置小于数组的元素,就一直进行比较,最后把该节点的子节点附给i        for (tmp = a[i]; leftChild(i) < n; i = child) {            //得到该节点的左子节点            child = leftChild(i);              //我们假设每个节点都有两个孩子            //如果该节点不是最后一个元素并且该左子节点小于右子节点,继续深入找到最后在左右子节点中找到较小的一个            if (child != n - 1 && a[child] < a[child + 1]) {                child++;            }            //如果小于该节点,直接赋值。            if (tmp < a[child]) {                a[i] = a[child];            } else {                break;            }        }        //把该节点放到正确的位置        a[i] = tmp;    }

堆排序的代码还是很简单的,最重要的还是要理解排序的思想。
至于堆排序的时间复杂度,嗯……数学不是太好,直接从书上拿来:2NlogN-O(N)

PS:每次看了不久之后,就会忘,不会还得看呀!

参考资料:《数据结构与算法分析》。


回头来看,突然发现排序不是完整的,今天把代码补全》》
代码其实很简单,去遍历堆,始终把第一个元素和j进行交换,然后下滤。

public static void heapsort(int[] a) {        for (int i = a.length/2 - 1; i >= 0; i--) {            percDown(a, i, a.length);        }        ***for (int j = a.length - 1; j > 0; j--) {            swapReferences(a, 0, j);            percDown(a, 0, j);        }***    }
public static void swapReferences(int[] a, int i, int n) {        int temp = a[n];        a[n] = a[i];        a[i] = temp;    }
原创粉丝点击