Heapsort排序思路
将整个数组看作一个二叉树heap, 下标0为堆顶层, 下标1, 2为次顶层, 然后每层就是"3,4,5,6", "7, 8, 9, 10, 11, 12, 13, 14", ..., 对于其中的每一个非叶子节点, 其子节点的下标为 2 * pos + 1 和 2 * pos + 2
循环进行以下工作:
- 标记当前堆的右边界
- 将堆初始化为大顶堆
- 将堆顶置换到右边界, 同时将右边界左移一位(即将堆缩小一格)
将堆初始化为大顶堆的处理
- 从最后一个非叶子节点开始, 其下标为 length / 2 - 1, length为当前的堆大小, 往前挨个处理
- 检查节点值, 对节点与其子节点的值大小i进行比较, 如果子节点值比当前节点大, 将最大值的那个子节点换上来
- 如果发生了交换, 那么要对发生交换的这个子节点位置也进行检查, 如果还有交换, 则继续往下查, 直到达到叶子节点
public class DemoHeapSort { // 待排序的数组 private int[] ints; // 当前堆的右边界(下标) private int limit; public DemoHeapSort(int ... ints) { this.ints = ints; this.limit = ints.length - 1; } public void sortAll() { while (limit > 0) { resort(); swap(0, limit); limit--; } } public void resort() { // 起点pos为当前堆length / 2 - 1 for (int i = (limit + 1) / 2 - 1; i >= 0; i--) { int pos = i; while (true) { pos = check(pos); if (pos == 0) { break; } } } } /** * 检查pos位置, 自上而下判断是否都满足大顶堆的要求 * 其左右子节点的下标为 2*pos+1 和 2*pos+2 * * @param pos 当前要检查的下标 * @return 如果无调整, 则返回0, 如果有调整, 则返回被调整的子节点下标 */ private int check(int pos) { int posLeft = 2 * pos + 1; int posRight = 2 * pos + 2; if (posLeft > limit) { // 没有子节点 return 0; } if (posRight > limit) { // 仅有左节点 if (ints[pos] < ints[posLeft]) { swap(pos, posLeft); return posLeft; } else { return 0; } } // 左右节点都有 if (ints[posLeft] > ints[posRight]) { if (ints[pos] < ints[posLeft]) { swap(pos, posLeft); return posLeft; } else { return 0; } } else { if (ints[pos] < ints[posRight]) { swap(pos, posRight); return posRight; } else { return 0; } } } public void print() { for (int a : ints) { System.out.printf("%3d", a); } System.out.print(" "); } private void swap(int a, int b) { int tmp = ints[a]; ints[a] = ints[b]; ints[b] = tmp; } public static void main(String[] args) { DemoHeapSort hs = new DemoHeapSort(10, 97, 9, 1,63, 64, 8, 17, 33, 7, 21, 0, 7, 75, 13, 18, 2, 99, 87); hs.sortAll(); hs.print(); } }
C代码, 在原数组上排序
void heap_sort(int *nums, int size) { for (int i = 0; i < size; i++) { int num = *(nums + i); printf("%d ", num); } printf(" "); for (int i = 0; i < size; i++) { int length = size - i; for (int pos = length / 2 - 1; pos >= 0; pos-- ) { int p = pos; do { p = check(nums + i, length - 1, p); } while (p != 0); } for (int j = 0; j < size; j++) { int num = *(nums + j); printf("%d ", num); } printf(" %d ", i); } } int check(int *nums, int limit, int pos) { int ls = pos * 2 + 1; int rs = pos * 2 + 2; if (ls > limit) { return 0; } int p = *(nums + pos); if (rs > limit) { int l = *(nums + ls); if (l > p) { swap(nums + ls, nums + pos); return ls; } else { return 0; } } else { int l = *(nums + ls); int r = *(nums + rs); if (l > r) { if (l > p) { swap(nums + ls, nums + pos); return ls; } else { return 0; } } else { if (r > p) { swap(nums + rs, nums + pos); return rs; } else { return 0; } } } } void swap(int *a, int *b) { int t = *a; *a = *b; *b = t; }