Java实现堆排序
2015-05-18 18:42
337 查看
(二叉)堆是一个完全二叉树,分为大根堆和小根堆。大根堆是指树中除根结点外的所有结点的值都小于等于其父结点的堆;小根堆是指树中除根节点外的所有结点的值都大于等于其父结点的堆。从定义可知,大根堆的根结点是堆中的最大结点,小根堆的根结点是堆中的最小结点。本文基于大根堆实现对数组元素排序。大根堆的图形描述如下:
基于大根堆实现对数组元素排序有三个步骤:
(1)实现下降结点的子程序;
(2)实现建堆子程序;
(3)实现堆排序。
接下来具体描述。
(1)实现下降结点的子程序
结点i的左子树和右子树都是大根堆,但是以结点i为根结点的子树可能不是大根堆,也就是说,结点i可能小于其子结点,这时需要在树中下降i结点的值,使以结点i为根结点的子树成为大根堆。实现该功能的伪代码如下:
Java代码如下:
(2)实现建堆子程序
建堆就是使堆满足大根堆的特性,也就是使堆中所有结点都满足大根堆的特性。假设有保存堆的数组A[1...n],我们很容易知道,结点n的双亲结点(n/2,整数除法)之后的节点都是叶子结点,这些结点可以理解为是只包含一个元素的大根堆,它们满足大根堆性质,我们只需要对从n/2结点开始到1结点的所有结点调用maxHeapify方法使之满足大根堆性质,从而使整个二叉树成为一个大根堆。之所以要从n/2结点开始,是因为使用maxHeapify需要保证结点的子树是大根堆,而在数组中孩子结点一定在双亲结点的右边,所以首先可以保证的就是n/2结点的孩子结点是大根堆。
建堆的伪代码如下:
Java代码如下:
(3)实现堆排序
我们知道,大根堆的根结点是最大的节点,所以,只需要重复这个步骤就能对数组中的元素排序:从数组中取出第一个元素,然后把最后一个元素放在根节点的位置,然后堆得长度减1,然后对第一个元素调用maxHeapify方法使之满足大根堆性质。这样我们就能得到一系列从大到小排列的元素,但在实际中,我们是交换第一个元素和最后一个元素,最后数组就变为一个数组元素从小到大排列的数组。
堆排序伪代码如下:
上面的循环中没有到达1,是因为2结束时,堆中只有一个元素了,它是有序的。
Java代码如下:
堆排序类的完整代码如下:
基于大根堆实现对数组元素排序有三个步骤:
(1)实现下降结点的子程序;
(2)实现建堆子程序;
(3)实现堆排序。
接下来具体描述。
(1)实现下降结点的子程序
结点i的左子树和右子树都是大根堆,但是以结点i为根结点的子树可能不是大根堆,也就是说,结点i可能小于其子结点,这时需要在树中下降i结点的值,使以结点i为根结点的子树成为大根堆。实现该功能的伪代码如下:
Java代码如下:
/** * 数组从下标1开始存储堆中元素; * * @param a 保存堆得数组 * @param i 堆中需要下降的元素 * @param heapLength 数组中最右边堆元素的位置 */ public <T extends Comparable<T>> void maxHeapify(T[] a, int i, int heapLength) { int l = left(i); int r = right(i); int largest = 0; /** * 下面两个if条件句用来找到三个元素中的最大元素的位置largest; * l <= heapLength说明l在数组内,i非叶子结点; */ if (l <= heapLength && a[i].compareTo(a[l]) < 0) { largest = l; } else { largest = i; } // r <= heapLength说明r在数组内 if (r <= heapLength && a[largest].compareTo(a[r]) < 0) { largest = r; } // 如果i处元素不是最大的,就把i处的元素与最大处元素交换,交换会使元素下降 if (i != largest) { T temp = a[i]; a[i] = a[largest]; a[largest] = temp; // 递归下沉 maxHeapify(a, largest, heapLength); } }
(2)实现建堆子程序
建堆就是使堆满足大根堆的特性,也就是使堆中所有结点都满足大根堆的特性。假设有保存堆的数组A[1...n],我们很容易知道,结点n的双亲结点(n/2,整数除法)之后的节点都是叶子结点,这些结点可以理解为是只包含一个元素的大根堆,它们满足大根堆性质,我们只需要对从n/2结点开始到1结点的所有结点调用maxHeapify方法使之满足大根堆性质,从而使整个二叉树成为一个大根堆。之所以要从n/2结点开始,是因为使用maxHeapify需要保证结点的子树是大根堆,而在数组中孩子结点一定在双亲结点的右边,所以首先可以保证的就是n/2结点的孩子结点是大根堆。
建堆的伪代码如下:
Java代码如下:
public <T extends Comparable<T>> void buildMaxHeap(T[] a, int heapLength) { int lengthParent = parent(heapLength); // 最初,parent(length)之后的所有元素都是叶子结点 for(int i = lengthParent; i >= 1; i--){ maxHeapify(a, i, heapLength); } }
(3)实现堆排序
我们知道,大根堆的根结点是最大的节点,所以,只需要重复这个步骤就能对数组中的元素排序:从数组中取出第一个元素,然后把最后一个元素放在根节点的位置,然后堆得长度减1,然后对第一个元素调用maxHeapify方法使之满足大根堆性质。这样我们就能得到一系列从大到小排列的元素,但在实际中,我们是交换第一个元素和最后一个元素,最后数组就变为一个数组元素从小到大排列的数组。
堆排序伪代码如下:
上面的循环中没有到达1,是因为2结束时,堆中只有一个元素了,它是有序的。
Java代码如下:
public <T extends Comparable<T>> void sort(T[] a) { // TODO Auto-generated method stub // 最初,堆的长度就是整个数组的长度 int heapLength = a.length - 1; buildMaxHeap(a, heapLength); for(int i = heapLength; i >= 2; i--){ T temp = a[i]; a[i] = a[1]; a[1] = temp; heapLength--; maxHeapify(a, 1, heapLength); } }
堆排序类的完整代码如下:
public class HeapSort{ public <T extends Comparable<T>> void sort(T[] a) { // TODO Auto-generated method stub // 最初,堆的长度就是整个数组的长度 int heapLength = a.length - 1; buildMaxHeap(a, heapLength); for(int i = heapLength; i >= 2; i--){ T temp = a[i]; a[i] = a[1]; a[1] = temp; heapLength--; maxHeapify(a, 1, heapLength); } } public int left(int i) { return 2 * i; } public int right(int i) { return 2 * i + 1; } public int parent(int i) { // i为根结点 if (i == 1) { return 0; } return i / 2; } /** * 数组从下标1开始存储堆中元素; * @param a 保存堆得数组 * @param i 堆中需要下降的元素 * @param heapLength 数组中最右边堆元素的位置 */ public <T extends Comparable<T>> void maxHeapify(T[] a, int i, int heapLength) { int l = left(i); int r = right(i); int largest = 0; /** * 下面两个if条件句用来找到三个元素中的最大元素的位置largest; * l <= heapLength说明l在数组内,i非叶子结点; */ if (l <= heapLength && a[i].compareTo(a[l]) < 0) { largest = l; } else { largest = i; } // r <= heapLength说明r在数组内 if (r <= heapLength && a[largest].compareTo(a[r]) < 0) { largest = r; } // 如果i处元素不是最大的,就把i处的元素与最大处元素交换,交换会使元素下降 if (i != largest) { T temp = a[i]; a[i] = a[largest]; a[largest] = temp; // 递归下沉 maxHeapify(a, largest, heapLength); } } public <T extends Comparable<T>> void buildMaxHeap(T[] a, int heapLength) { int lengthParent = parent(heapLength); // 最初,parent(length)之后的所有元素都是叶子结点 for(int i = lengthParent; i >= 1; i--){ maxHeapify(a, i, heapLength); } } public static void main(String[] args) { Integer[] heap = new Integer[]{0, 3, 1, 5, 12, 7}; new HeapSort().sort(heap); for(Integer integer : heap){ System.out.println(integer); } } }参考 算法导论 第六章 堆排序
相关文章推荐
- 排序算法复习(Java实现)(二): 归并排序,堆排序,桶式排序,基数排序
- 排序算法复习(Java实现):插入,冒泡,选择,Shell,快速排序, 归并排序,堆排序,桶式排序,基数排序
- 排序算法复习(Java实现)(二): 归并排序,堆排序,桶式排序,基数排序
- 【转】排序算法复习(Java实现) (二): 归并排序,堆排序,桶式排序,基数排序
- 排序算法复习(Java实现)(二): 归并排序,堆排序,桶式排序,基数排序
- 【转】排序算法复习(Java实现)(二): 归并排序,堆排序,桶式排序,基数排序
- 程序员必知的8大排序(二)-------简单选择排序,堆排序(java实现)
- 程序员必知的8大排序(二)-------简单选择排序,堆排序(java实现)
- 堆排序的java实现
- 堆排序(java实现)
- 堆排序Java实现
- 基本算法-堆排序及其Java实现
- [原]堆排序的java实现
- 几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- 元素排序几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- 几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- 算法学习之堆排序(java实现)
- heapsort堆排序(3种语言实现 c/java/python)
- 算法导论堆排序Java实现
- 堆排序详解以及java实现