堆排序
1. 申明
C 实现:
c
typedef struct {
int* data; // 数据域
int capacity; // 最大容量
int len; // 约束堆的数据长度
}MinHeap;
Java 实现:
java
public class MinHeap<T extends Comparable<T>> {
private T[] data; // 数据域
private int capacity; // 容量
private int len; // 数据域
}
2. 创建
C 实现:
c
MinHeap* createMinHeap(int n) {
MinHeap* heap = malloc(sizeof(MinHeap));
if (heap == NULL) return NULL;
heap->data = malloc(sizeof(int) * (n + 1));
if (heap->data == NULL) {
free(heap->data);
return NULL;
}
heap->len = 0;
heap->capacity = n;
memset(heap->data, 0, sizeof(int) * (n + 1));
return heap;
}
Java 实现:
java
@SuppressWarnings("unchecked")
public MinHeap(int capacity) {
this.capacity = capacity;
this.len = 0;
this.data = (T[])new Comparable[capacity + 1];
}
3. 插入
- 在最后一个位置,插入新元素
- 上浮,找到这个新元素的父节点,如果父节点大,那么就交换父子
- 继续将交换的父节点值当作待确定值,再和它的父节点比较
- 不断循环,直到达到根节点或者满足堆的性质
C 实现:
c
static void siftUp(MinHeap* heap, int k) {
while (k > 1 && heap->data[k] < heap->data[k / 2]) {
int tmp = heap->data[k];
heap->data[k] = heap->data[k / 2];
heap->data[k / 2] = tmp;
k /= 2;
}
}
void insertMinHeap(MinHeap* heap, int e) {
if (heap->len >= heap->capacity) {
printf("Heap Full\n");
}
// 在末端插入元素
heap->data[++heap->len] = e;
siftUp(heap, heap->len);
}
Java 实现:
java
public void insertMiniHeap(T e) {
if (this.len >= this.capacity) {
throw new IllegalStateException("Heap is full");
}
this.data[++this.len] = e;
siftUp(this.len);
}
private void siftUp(int index) {
while (index > 1 && this.data[index].compareTo(this.data[index / 2]) > 0) {
T tmp = this.data[index];
this.data[index] = this.data[index / 2];
this.data[index / 2] = tmp;
index /= 2;
}
}
4. 提取
- 完全二叉树,删除一个元素,只能删除最后一个元素
- 删除堆顶,取出堆顶的元素值,用最后一个元素来替代根
- 下沉,如果有右孩子,比较左孩子和右孩子谁最小
- 最小的进行交换
C 实现:
c
static void siftDown(MinHeap* heap, int k) {
while (2 * k <= heap->len) {
int index = 2 * k;
if (2 * k + 1 <= heap->len && heap->data[index + 1] < heap->data[index]) {
index = 2 * k + 1;
}
if (heap->data[index] > heap->data[k]) {
break;
}
int tmp = heap->data[index];
heap->data[index] = heap->data[k];
heap->data[k] = tmp;
k = index;
}
}
int extractMinHeap(MinHeap* heap) {
int ret = heap->data[1];
heap->data[1] = heap->data[heap->len--];
siftDown(heap, 1);
return ret;
}
Java 实现:
java
public T extractMinHeap() {
T ret = this.data[1];
this.data[1] = this.data[this.len--];
siftDown(1);
return ret;
}
private void siftDown(int index) {
while (2 * index < this.len) {
int swapIndex = 2 * index;
if (swapIndex + 1 <= this.len && this.data[swapIndex].compareTo(this.data[swapIndex + 1]) > 0) {
swapIndex++;
}
if (this.data[swapIndex].compareTo(this.data[index]) > 0) {
break;
}
T tmp = this.data[index];
this.data[index] = this.data[swapIndex];
this.data[swapIndex] = tmp;
index = swapIndex;
}
}
5. 释放
C 实现:
c
void releaseMinHeap(MinHeap* heap) {
if (heap == NULL) return;
if (heap->data) {
free(heap->data);
heap->data = NULL;
}
free(heap);
}
6. 完整实现
C 实现:
minHeap.h
c
#pragma once
// 小顶堆结构,采用完全二叉树存储,从第1个位置开始存储,满足[i/2, i, 2i, 2i+1]
typedef struct {
int* data; // 用顺序存储方式来保存堆数据
int len; // 约束堆的数据长度
int capacity; // 最大容量
}MinHeap;
MinHeap* createMinHeap(int n);
// 动态插入数据,满足小顶堆性质
void insertMinHeap(MinHeap* heap, int e);
// 提取元素,从根上提取,保证每次提取时丢失最值
int extractMinHeap(MinHeap* heap);
void releaseMinHeap(MinHeap* heap);
minHeap.c
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "minHeap.h"
MinHeap* createMinHeap(int n) {
MinHeap* heap = malloc(sizeof(MinHeap));
if (heap == NULL) return NULL;
// 从1号索引开始存放数据
heap->data = malloc(sizeof(int) * (n + 1));
if (heap->data == NULL) {
free(heap);
return NULL;
}
memset(heap->data, 0, sizeof(int) * (n + 1));
heap->len = 0;
heap->capacity = n;
return heap;
}
void releaseMinHeap(MinHeap* heap) {
if (heap == NULL) return;
if (heap->data) {
free(heap->data);
heap->data = NULL;
}
free(heap);
}
// 从k索引位置处开始判断,进行上浮操作
static void siftUp(MinHeap* heap, int k) {
// k/2表示父节点,如果父节点大,那么和父节点交换
while (k > 1 && heap->data[k / 2] > heap->data[k]) {
int tmp = heap->data[k];
heap->data[k] = heap->data[k / 2];
heap->data[k / 2] = tmp;
k /= 2;
}
}
void insertMinHeap(MinHeap* heap, int e) {
// 1. 在二叉堆中最后位置插入元素
if (heap->len + 1 > heap->capacity) {
printf("MinHeap Space Small\n");
return;
}
heap->data[++heap->len] = e;
// 2. 根据小顶堆性质进行上移
siftUp(heap, heap->len);
}
static void siftDown(MinHeap* heap, int k) {
// 有左孩子
while (2 * k <= heap->len) {
int index = 2 * k;
// 存在右孩子,并且右孩子小于左孩子
if (index + 1 <= heap->len && heap->data[index + 1] < heap->data[index]) {
index += 1;
}
if (heap->data[k] <= heap->data[index]) {
break;
}
int tmp = heap->data[index];
heap->data[index] = heap->data[k];
heap->data[k] = tmp;
k = index;
}
}
// 提取一定是从根节点开始,用补位的方式,进行下沉
int extractMinHeap(MinHeap* heap) {
if (heap->len <= 0) {
return 0;
}
int ret = heap->data[1];
heap->data[1] = heap->data[heap->len--];
siftDown(heap, 1);
return ret;
}
main.c
c
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "minHeap.h"
void test01() {
int data[] = { 9, 3, 7, 6, 5, 1, 10, 2 };
int n = 20;
MinHeap* minHeap = createMinHeap(n);
for (int i = 0; i < sizeof(data) / sizeof(data[0]); i++) {
insertMinHeap(minHeap, data[i]);
}
printf("array: ");
for (int i = 1; i <= minHeap->len; i++) {
printf("%d\t", minHeap->data[i]);
}
printf("\n");
printf("extra: ");
for (int i = 0; i < sizeof(data) / sizeof(data[0]); i++) {
printf("%d\t", extractMinHeap(minHeap));
}
printf("\n");
releaseMinHeap(minHeap);
}
test02() {
int n = 10;
MinHeap* heap = createMinHeap(n);
for (int i = 0; i < n; i++) {
int e = rand() % 100 + 1;
insertMinHeap(heap, e);
printf("%d\t", e);
}
printf("\n");
for (int i = 0; i < n; i++) {
printf("%d\t", extractMinHeap(heap));
}
printf("\n");
}
int main() {
srand((unsigned int)time(NULL));
test01();
test02();
}
Java 实现:
MiniHeap
java
package com.sonnet.heap;
public class MinHeap<T extends Comparable<T>> {
private final T[] data; // 数据域
private final int capacity; // 容量
private int len; // 数据域
@SuppressWarnings("unchecked")
public MinHeap(int capacity) {
this.capacity = capacity;
this.len = 0;
this.data = (T[])new Comparable[capacity + 1];
}
public void insertMiniHeap(T e) {
if (this.len >= this.capacity) {
throw new IllegalStateException("Heap is full");
}
this.data[++this.len] = e;
siftUp(this.len);
}
private void siftUp(int index) {
while (index > 1 && this.data[index].compareTo(this.data[index / 2]) < 0) {
T tmp = this.data[index];
this.data[index] = this.data[index / 2];
this.data[index / 2] = tmp;
index /= 2;
}
}
public T extractMinHeap() {
T ret = this.data[1];
this.data[1] = this.data[this.len--];
siftDown(1);
return ret;
}
private void siftDown(int index) {
while (2 * index < this.len) {
int swapIndex = 2 * index;
if (swapIndex + 1 <= this.len && this.data[swapIndex].compareTo(this.data[swapIndex + 1]) > 0) {
swapIndex++;
}
if (this.data[swapIndex].compareTo(this.data[index]) > 0) {
break;
}
T tmp = this.data[index];
this.data[index] = this.data[swapIndex];
this.data[swapIndex] = tmp;
index = swapIndex;
}
}
}
Test
java
package com.sonnet.heap;
import java.util.Random;
public class Test {
public static void main(String[] args) {
int n = 10;
Random random = new Random();
MinHeap<Integer> table = new MinHeap<>(n);
for (int i = 0; i < n; i++) {
int e = random.nextInt(1, 100);
System.out.print(e + "\t");
table.insertMiniHeap(e);
}
System.out.println();
for (int i = 0; i < n; i++) {
System.out.print(table.extractMinHeap() + "\t");
}
System.out.println();
}
}