数据结构---------堆排序
2017-03-01 08:33
218 查看
说到堆排序,它的思想来源于优先队列,我们首先来说一下什么是优先队列。
一、优先队列
1.优先队列的定义:优先队列是允许以下两种操作的数据结构:Insert(插入)相当于入队、DeleteMin(删除最小元)。
2.使用数据结构:相比于单链表和二叉排序树的缺点,我们使用二叉堆这种结构,它有两个重要的性质就是:
结构性——完全二叉树
堆序行——小顶堆或大顶堆
3.实现:因为完全二叉树有规律,我们使用数组实现,对于数组中任一位置i上的元素,其左儿子为2*i,右儿子为 2*i+1,父亲为 i/2,(下标从1开始)。
4.思想:
插入:将一个元素X插入到堆中后,我们在下一个位置创建一个空穴,如果X不破坏堆结构,那么Insert完成。否则,我们把该空穴的父亲节点移动到该空穴,直到X被放入到空穴为止。我们把这种操作称为上滤。
删除最小元:删除最小元后产生一个空穴,堆中最后一个元素X必须移动到堆中的某个地方,如果X可以被放到空穴,那么DeleleMin完成,否则,将空穴较小的儿子移入空穴,直至X被移动到正确的位置。我们把这种操作称为下滤。
5.实现:
声明:
实现:
二、堆排序
1.思路:将要排序的数据构建成小顶堆,交换根与最后节点,下滤重新构建堆。
2.实现:
数组实现,下标从0开始:
3.分析:堆排序的时间复杂度为O(longN),构建N个数据的二叉堆需要O(N)时间,DeleteMin需要O(NlogN)时间。
堆排序是一个稳定的排序,无论起始的数据序列是如何的。
一、优先队列
1.优先队列的定义:优先队列是允许以下两种操作的数据结构:Insert(插入)相当于入队、DeleteMin(删除最小元)。
2.使用数据结构:相比于单链表和二叉排序树的缺点,我们使用二叉堆这种结构,它有两个重要的性质就是:
结构性——完全二叉树
堆序行——小顶堆或大顶堆
3.实现:因为完全二叉树有规律,我们使用数组实现,对于数组中任一位置i上的元素,其左儿子为2*i,右儿子为 2*i+1,父亲为 i/2,(下标从1开始)。
4.思想:
插入:将一个元素X插入到堆中后,我们在下一个位置创建一个空穴,如果X不破坏堆结构,那么Insert完成。否则,我们把该空穴的父亲节点移动到该空穴,直到X被放入到空穴为止。我们把这种操作称为上滤。
删除最小元:删除最小元后产生一个空穴,堆中最后一个元素X必须移动到堆中的某个地方,如果X可以被放到空穴,那么DeleleMin完成,否则,将空穴较小的儿子移入空穴,直至X被移动到正确的位置。我们把这种操作称为下滤。
5.实现:
声明:
#include <stdio.h> #include <stdlib.h> #define ElementType int /* 优先队列数组实现 */ typedef struct HeapStrcut //堆结构 { int Capcity; int Size; ElementType *Elements; }*PriorityQueue; PriorityQueue Initialize(int MaxElements); //创建初始化优先队列 void Insert(ElementType X, PriorityQueue H);//插入 ElementType DeleteMin(PriorityQueue H); //删除最小节点 int IsEmpty(PriorityQueue H); //判断优先队列是否为空 int IsFull(PriorityQueue H); //判断优先队列是否已满
实现:
/* 创建初始化优先队列 小顶堆,最小元在根节点 */ PriorityQueue Initialize(int MaxElements) { PriorityQueue H; //申请优先队列结构空间 H = (PriorityQueue)malloc(sizeof(struct HeapStrcut)); if(NULL == H) { printf("Out of space.\n"); exit(-1); } //申请存储数据数组Elements空间 H->Elements = (ElementType *)malloc(sizeof(ElementType)*MaxElements); if (H->Elements == NULL) { printf("Out of space.\n"); exit(-1); } //初始化优先队列 H->Capcity = MaxElements; H->Size = 0; } /* 插入,交换而实施的赋值语句为d+1 */ void Insert(ElementType X, PriorityQueue H) { int i; if(IsFull(H)) { printf("The PriorityQueue is full.\n"); exit(-1); } for (i = ++H->Size; H->Elements[i / 2] > X; i /= 2) H->Elements[i] = H->Elements[i / 2]; H->Elements[i] = X; } //我的插入写法,交换而实施的赋值语句为3d void Insert2(ElementType X, PriorityQueue H) { int i, tmp; if (IsFull(H)) { printf("The PriorityQueue is full.\n"); exit(-1); } H->Elements[++H->Size] = X; tmp = H->Elements[H->Size]; for (i = H->Size; i / 2 > 0; i /= 2) if (H->Elements[i / 2] > H->Elements[i]) H->Elements[i] = H->Elements[i / 2]; else break; H->Elements[i] = tmp; } /* 删除最小节点 */ ElementType DeleteMin(PriorityQueue H) { int i, child; ElementType MinElement, LastElement; if (IsEmpty(H)) { printf("The PriorityQueue is empty.\n"); exit(-1); } MinElement = H->Elements[1]; LastElement = H->Elements[H->Size--]; for (i = 1; i * 2 <= H->Size; i = child) { //查找较小的子节点 child = i * 2; if (child != H->Size && H->Elements[child + 1] < H->Elements[child]) child++; //下滤操作 if (LastElement > H->Elements[child]) H->Elements[i] = H->Elements[child]; else break; } H->Elements[i] = LastElement; return MinElement; } /* 判断优先队列是否为空 */ int IsEmpty(PriorityQueue H) { return H->Size == 0; } /* 判断优先队列是否已满 */ int IsFull(PriorityQueue H) { return H->Size == H->Capcity; } /* 显示优先队列元素 */ void show(PriorityQueue H) { int i; for (i = 1; i <= H->Size; i++) printf("%d\t", H->Elements[i]); printf("\n"); } int main() { int max_elements, flag=1, choose, value; //构建优先队列 printf("开始构建优先队列\n"); printf("请输入优先队列大小:\n"); scanf("%d", &max_elements); PriorityQueue H = Initialize(max_elements); //优先队列操作 while (flag) { printf("请输入操作,1.插入 2.删除最小元 3.显示优先队列元素.\n"); scanf("%d", &choose); switch (choose) { case 1: printf("请输入插入元素\n"); scanf("%d", &value); Insert(value, H); break; case 2: DeleteMin(H); show(H); break; case 3: show(H); break; default: flag = 0; break; } } return 0; }
二、堆排序
1.思路:将要排序的数据构建成小顶堆,交换根与最后节点,下滤重新构建堆。
2.实现:
数组实现,下标从0开始:
/* 堆排序 思路:堆排序分为构建堆、删除最值、调整堆即上滤下滤操作。 方法:使用数组代替ADT(抽象数据类型)的方式实现初始堆数组下标从零开始。 总结:时间复杂度为N*logN。 */ //上滤操作 void PercDown(ElementType A[], int i, int N) //i为父节点下标 { int replChild; //下滤操作时用来与根节点交换的孩子节点下标 ElementType tmp; //一般利用交换进行的排序都要有一个tmp变量 //每次开始都要取出最顶点下标,所以可以写在for语句头中作为初始条件 //遍历的条件就是父节点还有孩子 //执行完此次下滤操作后需要找到下次下滤操作的父节点,所以我们也可以写到for语句头 for (tmp = A[i]; 2*i+1 < N; i = replChild) //i = replChild是在语句体执行之后执行的 { if (2 * i + 1 != N - 1 && A[2 * i + 1] < A[2 * i + 1 + 1])//若右孩子存在且右孩子大则将右孩子下滤 replChild = 2 * i + 1 + 1; if (2 * i + 1 != N - 1 && A[2 * i + 1] > A[2 * i + 1 + 1])//若右孩子存在且右孩子小则将左孩子下滤 replChild = 2 * i + 1; if(2 * i + 1 == N - 1) //若右孩子不存在则将左孩子下滤 replChild = 2 * i + 1; //书上方法 //replChild = 2*i+1; //if (replChild != N - 1 && A[replChild + 1] > A[replChild]) // replChild++; //满足孩子比父亲数据大,将孩子数据赋值父亲数据, //否则,不满足堆结构,不需要下滤跳出循环 if (tmp < A[replChild]) A[i] = A[replChild]; else break; } A[i] = tmp; } //堆排序 void HeapSort(ElementType A[], int N) { int i, temp;; for (i = N / 2; i >= 0; i--) PercDown(A, i, N); for (i = N - 1; i > 0; i--) { //交换根与最后的节点数据 temp = A[0]; A[0] = A[i]; A[i] = temp; PercDown(A, 0, i); } }
3.分析:堆排序的时间复杂度为O(longN),构建N个数据的二叉堆需要O(N)时间,DeleteMin需要O(NlogN)时间。
堆排序是一个稳定的排序,无论起始的数据序列是如何的。
相关文章推荐
- [数据结构]堆排序
- [数据结构]堆排序
- 数据结构 — 堆排序
- C++数据结构 排序 二分 插入 冒泡 基数 归并 直选 快排 希尔 堆排序
- 数据结构系列之堆排序(基于数组实现)
- 重学数据结构系列之——堆及堆排序
- 【数据结构】将一组数据升序排序(利用堆排序)
- 数据结构试验-快速排序-堆排序
- 浅谈算法和数据结构(5):优先级队列与堆排序
- 数据结构之选择排序之堆排序(参考严蔚敏数据结构)
- 堆排序的代码实现 - 数据结构和算法93
- 一步步学习数据结构和算法之堆排序效率分析及java实现
- 数据结构-选择排序-堆排序
- 【数据结构】堆排序
- [数据结构] 二叉堆,堆排序,优先级队列
- 浅谈算法和数据结构: 五 优先级队列与堆排序
- 堆数据结构的实现以及堆排序
- 浅谈算法和数据结构: 五 优先级队列与堆排序
- 【数据结构】用模版实现大小堆、实现优先级队列,以及堆排序
- 数据结构之堆排序