--------------------------------------------------------------------------------------------------------------------------------------------------------------
2018年8月30日补充:
看了普林斯顿算法中关于堆的实现,感觉更加形象生动,补充如下:
//下面的结点往上游,游到一个合适的位置 void swim(int k) { while(k > 1 && less(k/2,k)) { exch(k/2,k); k/=2; } } //上面的结点往下沉 沉到一个合适的位置 void sink(int k) { while(2*k <= N) { int j=2*k; if(j<N && less(j,j+1)) j++; //选出两个儿子中的较大值 if(!less(k,j)) break; //若找到了合适的位置就跳出 exch(k,j); k = j; } } //删除最大元素 Key delMax() { Key max = pq[1]; //从根结点得到最大的元素 exch(1,N--); //将其和最后一个结点交换 pq[N+1] = NULL; //防止对象游离 sink(1); //恢复堆的有序性 return max; } void stack_sort(int a[], int N) { for(int k=N/2; k>=1; k--) { sink(a,k,N); //将初始的数组排成堆 } while(N > 1) { exch(a,1,N--); //将max排到最后面 sink(a,1,N); //重新恢复有序性 } }
堆排序是我们所知的唯一能够同时最优地利用空间和时间的方法------在最坏的情况下也能保证使用2NlgN次比较和恒定的额外空间。
//最大堆 typedef struct Heap* MaxHeap; struct Heap { ElementType *Elements; //存储数组 int Size; //当前堆中元素个数 int Capacity; //堆的容量 }; //创建一个最大堆 MaxHeap CreateMaxHeap(int MaxSize) { MaxHeap H = malloc(sizeof(struct Heap)); H->Elements = malloc(sizeof(ElementType) * MaxSize); H->Size = 0; H->Capacity = MaxSize; H->Elements[0] = MaxData; //哨兵 return H; } // int isFull(MaxHeap H) { return (H->Size == H->Capacity); } //在堆中插入一个元素 int Insert(MaxHeap H,ElementType X) { if(isFull(H)) { printf("space error"); return 0; } i = ++H->Size; for(;H->Elements[i/2] < X;i/=2) H->Elements[i] = H->Elements[i/2]; H->Elements[i] = X; return 1; } // int isEmpty(MaxHeap H) { return (H->Size == 0); } //在最大堆中删除最大的元素,即根节点 ElementType DeleteMax(MaxHeap H) { int Parent, Child; ElementType MaxItem; if(isEmpty(H)) { printf("the tree is empty!") return 0; } MaxItem = H->Elements[1]; ElementType Tmp = H->Elements[Size--]; //所要替代根节点的元素 //现在给它找一个合适的位置 for(Parent = 1; Parent * 2 <= H->Size/*判断是否存在左子结点*/; Parent = Child) { Child = 2*Parent; if((Child != H->Size)&&(H->Elements[Child] < H->Elements[Child+1])) Child++; //判断左右两个结点哪个大,并使Child指向他 if(temp >= H->Elements[Child]) break; //如果找到了合适的位置就跳出 else H->Elements[Parent] = H->Elements[Child]; //没有找到,即左右两个节点中存在比Tmp大的,则将大的子结点向上移动 } H->Elements[Parent] = Tmp; return MaxItem } //建造最大堆 //PercDown:在H中以H->Elements[p]为根节点的子堆 将其排成最大堆 void PercDown(MaxHeap H, int p) { int Parent,Child; ElementType X; X = H->Elements[p]; for(Parent = p; Parent*2 <= H->Size; Parent = Child) { Child = Parent*2; if((Child != H->Size)&&(H->Elements[Child] < H->Elements[Child+1])) Child++; //判断左右两个结点哪个大,并使Child指向他 if(X >= H->Elements[Child]) break; //如果找到了合适的位置就跳出 else H->Elements[Parent] = H->Elements[Child]; //没有找到,即左右两个节点中存在比Tmp大的,则将大的子结点向上移动 } H->Elements[Parent] = X; } //建造最大堆 void BuildHeap(MaxHeap H) { int i; for(i = H->Size/2; i>0; i--) { PercDown(H,i); } } //堆排序 //注意,堆排序中没有了哨兵 void Swap(ElementType *a, ElementType *b) { ElementType t = *a; *a = *b; *b = t; } void PercDown(ElementType a[], int p, int N) { int Parent,Child; ElementType X; X = a[p]; for(Parent = p; (Parent*2+1)<N;Parent = Child) { Child = Parent*2 + 1; if(((Child+1)!=N) && a[Child] < a[Child+1]) Child++; if(X >= a[Child]) break; else a[Parent] = a[Child]; } a[Parent] = X; } void HeapSort(ElementType a[], int N) { int i; for(i = N/2-1;i>=0;i--) { PercDown(a,i,N); } for(i=N-1;i>0;i--) { Swap(&a[0],&a[i]); PercDown(a,0,i); } }
复杂度分析:O(nlogn):第一个for循环,初始化堆,为O(n),第二个for循环为O(nlogn);
稳定性分析:不稳定。