几大内部排序算法(三)--堆(优先队列)和堆排序
2017-12-12 19:30
363 查看
这儿是我的笔记,希望大家可以友好交流!!谢谢#__#
这是最后一篇关于排序的了,在说堆排序前想先说下优先队列的ADT实现,优先队列最常见的用标准二叉堆实现,标准二叉堆其实就是用完全文二叉树实现的,由于完全二叉树的特点,仅仅用数组就可以实现(其实是将数抽象了,数组化了)。
(一)优先队列–标准二叉堆ADT实现
1:堆的结构性:当用标记法时(0位置用最小或最大的值填充)对数组中任意i上的元素,其左儿子在2*i上,右儿子在2*i+1上,其父亲在i/2;
当不用标记法时,对数组中任意i上的元素,其左儿子在2*i+1上,右儿子在2*i+1+1上,其父亲在(i-1)/2;
:2:堆的有序性:想要快速的找到最小的(大)的元,最小(大)的元应该在根节点上,数组的0或1位置上(看是不是用了标记法),此外,每个带有儿子的节点,其值要比孩子大或者小。
3:基本操作:
Insert:为将某元素x插入,应该在最后面创建一个空穴,若x可以直接放入而不破坏堆的有序性,则直接插入,否则将父亲移到空穴位置,空穴上移动了一层往根方向(上滤)。
【注意,在后面堆排序建堆时,也是从后面的非叶子节点开始检验调整的】
DeleteMax/Min:相当于出列,删除最小或最大的元素,把最小的元素从根处删除,根处多了一个空穴,则需要将最后一个元素相应的位置。若此时x放到空穴上,比根节点两个儿子中较大的元素大,则x放到空穴后完成;若x不能放,则选择2个儿子较大的放到空穴,空穴下移动(下滤),直到x能当道合适的位置。
4:其他操作:
DecreseKey(P,deta,H)
给在某个位置p处的元素降低deta,再通过调整达到平衡。
IncreseKey(P,deta,H)
给在某个位置p处的元素增加deta,再通过调整达到平衡。
以下是没有用标记法的大根堆ADT。
头文件
实现:
关于小堆的也很简单,只要理解了deletamax后,其他的increaseKe就很简单。
(二)堆排序
堆排序,其实就是借助于每次从根处得到最大的(小)的值,连续N次就可以排好了,为了不用临时数组,所以不借助ADT的实现,直接从无序的数组(无序堆序列)开始先建一个有序堆,然后开始排序。
第一步:把无序堆建成有序堆
从后面的非叶子节点开始(N/2,可以画出而得到)调整,使得有子节点的根大(或者小)
for(i= N/2;i>=0;i–)调整这么多非叶子结点
第二步:多次排序调整
交换第1各元素和最后一个元素,并减小的对的大小,此时根处的有序性被破坏,所以从根处调整。
实现:
一个很关键的函数,从堆A的某个位置index调整
建堆
排序
测试代码可以用“排序算法(一)中的测试代码”。
用数组实现的优先队列不能支持合并操作,所以还有后面会和树,散列表那些一起补记回来。
这是最后一篇关于排序的了,在说堆排序前想先说下优先队列的ADT实现,优先队列最常见的用标准二叉堆实现,标准二叉堆其实就是用完全文二叉树实现的,由于完全二叉树的特点,仅仅用数组就可以实现(其实是将数抽象了,数组化了)。
(一)优先队列–标准二叉堆ADT实现
1:堆的结构性:当用标记法时(0位置用最小或最大的值填充)对数组中任意i上的元素,其左儿子在2*i上,右儿子在2*i+1上,其父亲在i/2;
当不用标记法时,对数组中任意i上的元素,其左儿子在2*i+1上,右儿子在2*i+1+1上,其父亲在(i-1)/2;
:2:堆的有序性:想要快速的找到最小的(大)的元,最小(大)的元应该在根节点上,数组的0或1位置上(看是不是用了标记法),此外,每个带有儿子的节点,其值要比孩子大或者小。
3:基本操作:
Insert:为将某元素x插入,应该在最后面创建一个空穴,若x可以直接放入而不破坏堆的有序性,则直接插入,否则将父亲移到空穴位置,空穴上移动了一层往根方向(上滤)。
【注意,在后面堆排序建堆时,也是从后面的非叶子节点开始检验调整的】
DeleteMax/Min:相当于出列,删除最小或最大的元素,把最小的元素从根处删除,根处多了一个空穴,则需要将最后一个元素相应的位置。若此时x放到空穴上,比根节点两个儿子中较大的元素大,则x放到空穴后完成;若x不能放,则选择2个儿子较大的放到空穴,空穴下移动(下滤),直到x能当道合适的位置。
4:其他操作:
DecreseKey(P,deta,H)
给在某个位置p处的元素降低deta,再通过调整达到平衡。
IncreseKey(P,deta,H)
给在某个位置p处的元素增加deta,再通过调整达到平衡。
以下是没有用标记法的大根堆ADT。
头文件
#ifndef _BIAOZHUNERCHADUI_H_ #define _BIAOZHUNERCHADUI_H_ typedef (*OPERATOR)(); typedef enum { false = 0, true, }bool; struct Heap; typedef struct Heap HEAP; typedef struct Heap* HEAP_T; HEAP_T Heap_Init(HEAP_T HP,int N); //为了build——heap bool Heap_Insert(HEAP_T HP,OPERATOR cmp,void* data); void* Heap_DeleteMax(HEAP_T HP,OPERATOR cmp,OPERATOR myfree,void* sxw); void Heap_DeleteAll(HEAP_T HP,OPERATOR op,void* sxw); int Heap_GetSize(HEAP_T HP); int Heap_GetLen(HEAP_T HP); void Heap_Printf(HEAP_T HP); #endif
实现:
#include"biaozhunerchadui.h" #include<stdlib.h> /**因为有0*/ //#define leftchild(i) (2*i+1) //#define father(child) (child-1)/2 struct Heap { void** data; int size; int len; }; HEAP_T Heap_Init(HEAP_T HP,int N) { if(N<0) { printf("堆的大小不合适而exit(0)\n"); exit(0); } HP = (HEAP_T)malloc(sizeof(struct Heap)); if(HP==NULL) return NULL; HP->size = N; HP->len = -1; HP->data = (void**)malloc(HP->size * sizeof(void*)); if(HP->data == NULL) { free(HP); HP = NULL; return NULL; } for(int i = 0; i < HP->size; i++) { HP->data[i] = NULL; } return HP; } bool Heap_Insert(HEAP_T HP,OPERATOR cmp,void* data)//为了build——heap { if(data == NULL || HP == NULL) { printf("插入失败,数据为空或堆为空\n"); return false; } //前面HP->len初始化为-1,所以为了能用上0,先加上去 int index = HP->len+1; if(HP->data[index] == NULL) { HP->data[index] = (void*)malloc(sizeof(void*)); if(HP->data[index] == NULL) { printf("插入时分配空间失败\n"); return false; } } while(index!=0) { if(cmp!=NULL) { if(cmp(data,HP->data[index/2])) { HP->data[index] = HP->data[index/2]; HP->data[index/2] = data; index = index/2; } else { HP->data[index] = data; HP->len++; return true; } } else { if(*(int*)data > *(int*)HP->data[index/2]) { HP->data[index] = HP->data[index/2]; HP->data[index/2] = data; index = i dc6c ndex/2; } else { HP->data[index] = data; HP->len++; return true; } } } HP->data[index] = data; printf("元素%d入堆了\n",*(int*)HP->data[index]); HP->len++; return true; } void* Heap_DeleteMax(HEAP_T HP,OPERATOR cmp,OPERATOR myfree,void* sxw) { void* res = HP->data[0]; int child = 0, i = 0; if(HP == NULL) { printf("堆是空的,初始化\n"); return NULL; } if(HP->len < 0) { printf("队列元素已经空了\n"); return NULL; } child = 1; //删除完要从根部开始调整,while内说明至少有儿子 while(child <= HP->len) { if(cmp!=NULL && myfree!=NULL) { if(cmp(HP->data[child+1],HP->data[child])) child++; if(cmp(HP->data[HP->len],HP->data[child])) { HP->data[child/2] = HP->data[HP->len]; /**不释放那个元素的单位,留到最后deleteall一起做处理,但是如果不释放,再入列的话,就会去比较了 所以还是要进行释放然后置NULL的 */ if(myfree!=NULL) { myfree(HP->data[HP->len]); } HP->data[HP->len] = NULL; break; } else { HP->data[child/2] = HP->data[child]; child = child *2; } } else { /**两个儿子时找最大的*/ if(HP->data[child+1]&&HP->data[child]) { if(*(int*)HP->data[child+1] > *(int*)HP->data[child]) child++; if(*((int*)(HP->data[HP->len])) > *((int*)(HP->data[child]))) { HP->data[(child-1)/2] = HP->data[HP->len]; HP->data[HP->len] = NULL; HP->len--; return res; } else { HP->data[(child-1)/2] = HP->data[child]; child = child*2+1; } } /**只有一个孩子说明到底了*/ else if(HP->data[child+1] == NULL || HP->data[child] == NULL) { HP->data[(child-1)/2] = HP->data[HP->len]; HP->data[HP->len] = NULL; HP->len--; return res; } } } //没有孩子后决定最后一个元素是否要填补 if((child-1) / 2 < HP->len ) { HP->data[(child)/2] = HP->data[HP->len]; if(myfree!=NULL) myfree(HP->data[HP->len]); } HP->data[HP->len] = NULL; HP->len--; return res; } void Heap_DeleteAll(HEAP_T HP,OPERATOR op,void* sxw) { if(HP==NULL) { printf("堆是无效的\n"); exit(0); } int index = Heap_GetLen(HP); if(op!=NULL) { for(;index>=0;index--) { op(HP->data[index],sxw); HP->data[index] = NULL; } } free(HP); HP = NULL; } int Heap_GetSize(HEAP_T HP) { if(HP == NULL) exit(0); else return HP->size; } int Heap_GetLen(HEAP_T HP) { if(HP == NULL) exit(0); else return HP->len; } //用于检验的 void Heap_Printf(HEAP_T HP) { int i = 0; for(i = 0;i <= Heap_GetLen(HP);i++) { printf("%d\t",*(int*)HP->data[i]); } printf("\n"); }
关于小堆的也很简单,只要理解了deletamax后,其他的increaseKe就很简单。
(二)堆排序
堆排序,其实就是借助于每次从根处得到最大的(小)的值,连续N次就可以排好了,为了不用临时数组,所以不借助ADT的实现,直接从无序的数组(无序堆序列)开始先建一个有序堆,然后开始排序。
第一步:把无序堆建成有序堆
从后面的非叶子节点开始(N/2,可以画出而得到)调整,使得有子节点的根大(或者小)
for(i= N/2;i>=0;i–)调整这么多非叶子结点
第二步:多次排序调整
交换第1各元素和最后一个元素,并减小的对的大小,此时根处的有序性被破坏,所以从根处调整。
实现:
一个很关键的函数,从堆A的某个位置index调整
void PercDown(int *A ,int index,int N) { int tmp = A[index]; int child = 2*index+1; while(child < N)//至少有一个子节点 { if(child< N && child+1 < N)//2个节点时 { if(A[child+1] > A[child]) child++; if(tmp < A[child]) { A[(child-1)/2] = A[child]; A[child] = tmp; child = 2*child + 1; } else break; } else if(child < N && child+1 >=N)//一个节点 { if(tmp < A[child]) { A[(child-1)/2] = A[child]; A[child] = tmp; break; } else break; } else//无节点 break; } //为了和之前的堆方法搭配所以就多一步可以直接A[(child-1)/2]=tmp; if((child-1)/2 < N) { A[(child-1)/2] = tmp; } }
建堆
void Heap_Bulid(int* A,int N) { int index = N/2; for(;index >= 0 ; index--) /**从N/2处开始更新每个非叶子节点*/ { PercDown(A,index,N); } }
排序
void Heap_Sort(int *A,int N) { Heap_Bulid( A, N); /**实际排序操作*/ for(int i=N;i>0;i--) { Swap(A,0,i-1); //从根处调整 PercDown(A,0,i-1); } }
测试代码可以用“排序算法(一)中的测试代码”。
用数组实现的优先队列不能支持合并操作,所以还有后面会和树,散列表那些一起补记回来。
相关文章推荐
- 优先队列和堆排序
- 堆排序,优先队列实现
- 七大内部排序算法总结(插入排序、希尔排序、冒泡排序、简单选择排序、快速排序、归并排序、堆排序)
- 堆排序(优先队列)
- 堆和堆的应用:堆排序和优先队列
- 堆、堆排序和优先队列
- 2011-03-04 CLRS Chapter6 Heapsort 堆排序 优先队列
- 优先队列和堆排序
- 堆,优先队列以及堆排序
- 《常见算法和数据结构》优先队列(3)——堆排序
- 堆、堆排序、优先队列
- 《算法导论》堆排序和优先队列
- 优先队列和堆排序
- 堆排序(优先队列)
- 优先队列和堆排序
- 第六章 堆排序 6.5 优先队列
- 七大内部排序算法总结(插入排序、希尔排序、冒泡排序、简单选择排序、快速排序、归并排序、堆排序)
- 源码系列:堆排序、优先队列
- 第6章 堆排序,d叉堆,优先队列
- 堆排序(优先队列)