堆排序定義
n個關鍵字序列Kl,K2,…,Kn稱為堆,若且唯若該序列滿足如下性質(簡稱為堆性質):
(1) ki≤K2i且ki≤K2i+1 或(2)Ki≥K2i且ki≥K2i+1(1≤i≤ )
若將此序列所儲存的向量R[1..n]看做是一棵完全二叉樹的儲存結構,則堆實質上是滿足如下性質的完全二叉樹:樹中任一非葉結點的關鍵字均不大於(或不小於)其左右孩子(若存在)結點的關鍵字。
(用大根)堆排序的基本思想
① 先將初始檔案R[1..n]建成一個大根堆,此堆為初始的無序區
② 再將關鍵字最大的記錄R[1](即堆頂)和無序區的最後一個記錄R[n]交換,由此得到新的無序區R[1..n-1]和有序區R[n],且滿足R[1..n-1].keys≤R[n].key
③ 由於交換後新的根R[1]可能違反堆性質,故應將當前無序區R[1..n-1]調整為堆。然後再次將R[1..n-1]中關鍵字最大的記錄R[1]和該區間的最後一個記錄R[n-1]交換,由此得到新的無序區R[1..n-2]和有序區R[n-1..n],且仍滿足關係R[1..n-2].keys≤R[n-1..n].keys,同樣要將R[1..n-2]調整為堆。
……
直到無序區只有一個元素為止。
(大根)堆排序演算法的基本操作:
①
初始化操作:將R[1..n]構造為初始堆;
② 每一趟排序的基本操作:將當前無序區的堆頂記錄R[1]和該區間的最後一個記錄交換,然後將新的無序區調整為堆(亦稱重建堆)。
注意:
①只需做n-1趟排序,選出較大的n-1個關鍵字即可以使得檔案遞增有序。
②用小根堆排序與利用大根堆類似,只不過其排序結果是遞減有序的。堆排序和直接選擇排序相反:在任何時刻,堆排序中無序區總是在有序區之前,且有序區是在原向量的尾部由後往前逐步擴大至整個向量為止。
堆排序的演算法:
// 堆排序.cpp : 定義控制台應用程式的進入點。#include "stdafx.h"#include <iostream>using namespace std;//交換a與bvoid swap(int &a,int &b){int temp=a;a=b;b=temp;}//堆調整函數(此處為建立大根堆)void HeapAdjust(int *a,int s,int m){//已知a[s]到a[m]的記錄除了a[S]之外均滿足堆的定義,//本函數調整a[s],使a[s]到a[m]成為一個大頂堆int temp=a[s];for(int i=2*s+1;i<=m;i=2*i+1){ if(i<m && a[i]<a[i+1]) i+=1; if(temp>=a[i]) break; else { a[s]=a[i]; s=i; }}a[s]=temp;}//堆排序函數void HeadSort(int *a,int len){ //最開始調整資料建立一個大頂堆 for(int i=(len-1)/2;i>=0;i--) { HeapAdjust(a,i,len-1); } for(int j=len-1;j>0;j--) { swap(a[0],a[j]);//將堆頂記錄和當前未經排序子系列a[0]...a[j]中最後一個記錄a[j]交換 HeapAdjust(a,0,j-1);//將a[0]...a[j-1]重新調整為一個大頂堆 }}void printArray(int *a,int len){for(int i=0;i<len;i++)cout<<a[i]<<" ";cout<<endl;}int _tmain(int argc, _TCHAR* argv[]){int a[]={49,38,65,97,76,13,27,49};int len=sizeof(a)/sizeof(int);printArray(a,len);HeadSort(a,len);printArray(a,len);system("PAUSE");return 0;}
本文參考資料結構(c語言版 嚴蔚敏) P279-P283