排序
刷完一遍王道後,發現差不多又忘記了。所以打算用一個月時間整理一下。先整理每一章的知識框架,然後時間充裕的話把書上的算法整理,主要以南郵數據結構的考試大綱爲主體,王道數據結構和陳慧南的數據結構(只作爲補充內容,看了排序的一部分,覺得不是很好耶)爲輔。
整理了五天(其實每天都是抽出一點時間,畢竟考研黨要學很多,哭泣。。。)
這個是網上搜的圖,懶得打表格了,其實框架裏面也有,哈哈哈哈
下面是王道和陳慧南數據結構書上的算法
1.排序使用的順序表和鏈表結構
(針對陳慧南那本教材)
//排序使用的順序表和鏈表結構
typedef int T;
typedef struct list{
int Size,MaxList;
T Elements[MaxSize];
}Lists;
typedef struct node{
T Element;
struct node *Link;
}Node;
typedef struct list{
Node *First;
int Size;
}List;
2.直接插入排序
//順序表的直接插入排序
void InsertSort(List *lst)
{
int i,j;
T x;
for(i=1;i<lst->Size;i++) //執行n-1趟
{
x=lst->Elements[i];//待插入元素存入臨時變量
for(j=i-1;j>=0 && x<lst->Elements[j];j--) //從後往前查找插入位置
lst->Elements[j+1]=lst->Elements[j];//元素後移,j指針前移
lst->Elements[j+1]=x;//待插入元素找到的插入位置
}
}
//單鏈表的直接插入排序算法
void InsertSort(List *lst)
{
Node *unsorted,*sorted,*p,*q;
//p表示表中與待插入的記錄比較的結點,q表示p的前驅結點
//sorted總是指向單鏈表中已經有序的部分子表的尾部,而指針unsotred指向sorted的後繼結點,
//即待插入的記錄結點
if(lst->First!=NULL) //空鏈表
{
sorted=lst->First;
while(sorted->Link!=NULL)
{
unsorted=sorted->Link; //至少一個結點
if(unsorted->Element<list->First->Element) //若待插入記錄小於第一個記錄
{
sorted->Link=unsorted->Link;//將待記錄從鏈表取下
unsorted->Link=lst->First;//將待插記錄插在鏈表的最前面
lst->First=unsorted;
}
else //若待插記錄大於等於第一個記錄
{
q=lst->First;p=q->Link;
while(unsorted->Element>p->Element)//搜索待插記錄的適當插入位置
q=p;p=p->Link;
}
if(unsorted==p) sorted=unsorted;//將待插記錄在有序子表末尾
else //將待插記錄插在結點*q之後
{
sorted->Link=unsorted->Link;
unsorted->Link=p;q->Link=unsorted;
}
}
}
}
}
//直接插入排序(王道)
void InsertSort(Elementype A[],int n)
{
int i,j;
for(i=2;i<=n;i++) //依次將A[2]~A[n]插入到前面已排序序列
if(A[i].key<A[i-1].key) //若A[i]的關鍵碼小於其前驅,需將A[i]插入有序表
{
A[O]=A[i];//複製爲哨兵,A[0]不存放元素
for(j=i-1;A[O].key<A[j].key;--j) //從後往前查找待插入位置
A[j+1]=A[j];
A[j+1]=A[0];//複製到插入位置
}
}
3.希爾排序
//希爾排序
void InsSort(List *lst,int h)
{
int i,j;T x;
for(i=h;i<lst->Size;i+=h)
{
x=lst->Elements[i];
for(j=i-h;j>=0 && x<lst->Elements[j];j-=h)//位置j的同組前一個位置爲j-h
lst->Elements[j+h]=lst->Elements[j];
lst->Elements[j+h]=x;
}
void ShellSort(List *lst)
{
int i,incr=lst->Size;
do{
incr=incr/3+1;
for(i=0;i<incr;i++) InsSort(lst,incr);//計算增量
}while(incr>1);
}
}
//希爾排序(王道)
void ShellSort(ElementType A[],int n)
{
//前後記錄位置的增量是dk,不是1
//A[0]只是存儲單元,不是哨兵,當j<=0時,插入位置已到
for(dk=n/2;dk>=1;dk=dk/2)//初始增量爲總長度的一半,之後依次除2且向下取整
for(i=dk+1;i<=n;i++)
if(A[i].key<A[i-dk].key)
{
//需將A[i]插入有序增量子表
A[O]=A[i];//暫存在A[0]
for(j=i-dk;j>0 && A[0].key<A[j].key;j-=dk)
//待插入關鍵字之前以dk爲增量的關鍵字只要比待插入關鍵字大的都往後移動dk位
A[j+dk]=A[0];//插入
}
}
4.折半插入排序(王道)
void InsertSort(ElemType A[],int n)
{
int i,j,low,high,mid;
for(i=2;i<=n;i++) //依次將A[2]~A[n]插入前面的已排序序列
{
A[0]=A[i];//將A[i]暫存到A[0]中
low=1;high=i-1;//設置折半查找的範圍
while(low<=high)//折半查找(默認遞增有序)
{
mid=(low+high)/2;//取中間值
if(A[mid].key>A[0].key) high=mid-1;//查找左半子表
else low=mid+1;//查找右半子表
}
for(j=i-1;j>=high+1;--j)
A[j+1]=A[j];//統一後移元素,空出插入位置
A[high+1]=A[0];//插入操作
}
}
5.冒泡排序
//冒泡排序(王道)
void BubbleSort(ElemType A[],int n)
{
//用冒泡排序法將序列中的元素按從小到大排列
for(int i=0;i<n-1;i++)
{
flag=false;//表示本趟冒泡是否發生交換的標誌
for(j=n-1;j>i;j--)//一趟冒泡過程
if(A[j-1].key>A[j].key)
{
//若爲逆序
swap(A[j-1],A[j]);//交換
flag=true;
}
if(flag==false)
return;//本趟遍歷後沒有發生交換,說明表已經有序
}
}
6.快速排序
//快速排序(王道)
void QuickSort(ElemType A[],int low,int high)
{
if(low<high) // 遞歸跳出的條件
{
//Partion()是劃分操作,將表劃分爲滿足上述條件的兩個子表
int pivotpos=Partition(A,low,high);//劃分
QuickSort(A,low,pivotpos-1);//依次對兩個子表進行遞歸排序
QuickSort(A,pivotpos+1,high);
}
}
//分治,假設每次總以當前表中第一個元素作爲樞軸值(基準)來對錶進行劃分,
//則必須將表中比樞軸大的元素向右移動,將比樞軸小的元素向左移動,使得一趟
//Partition()操作後,表中的元素被樞軸一分爲二。
int Partition(ElemType A[],int low,int high)
{
ElemType pivot=A[low];//將當前表中第一個元素設爲樞軸值,且對錶進行劃分
while(low<high){
//循環跳出條件
while(low<high && A[high]>=pivot) --high;
A[low]=A[high];//將比樞軸值小的元素移動到左端
while(low<high && A[low]<=pivot) ++low;
A[high]=A[low];//將比樞軸值大的元素移動到右端
}
A[low]=pivot;//樞軸元素存放到最終位置
return low;
}
7.簡單選擇排序
//簡單選擇排序(王道)
void SelectSort(ElemType A[],int n)
{
//對錶A做簡單選擇排序,A[]從0開始存放元素
for(i=0;i<n-1;i++)//一共進行n-1趟
{
min=j; //記錄最小元素位置
for(j=i+1;j<n;j++) //在A[i...n-1]中選擇最小的元素
if(A[j]<A[min]) min=j;//更新最小元素位置
if(min!=i) swap(A[i],A[min]);//與第i個位置交換
}
}
8.堆排序
//堆排序 (王道)
//建立大根堆(自下往上逐步調整爲大根堆
void BuildMaxHeap(ElemType A[],int len)
{
for(int i=len/2;i>0;i--)//從i=[n/2]~1,反覆調整
AdjustDown(A,i,len);
}
void AdjustDown(ElemType A[],int k,int len)
{
//函數AdjustDown將元素k向下進行調整
A[0]=A[k] ;//A[0]暫存
for(i=2*k;i<=len;i*=2){
//沿key較大的子結點向下篩選
if(i<len && A[i]<A[i+1])//如果右孩子大一些,就只要考慮和右孩子比較
i++;
if(A[0]>=A[i]) break;//篩選結束,如果這個元素結點值不小於它的結點值,則不需要交換
else{
A[k]=A[i];//將A[i]調整到雙親結點上
k=i;//修改k值,以便繼續向下篩選
}
}
A[k]=A[0];//將篩選結點的值放入最終的位置
}
//向上調整堆
void AdjustUp(ElemType A[],int n)
{
//參數k爲向上調整的結點,也爲堆的元素個數
A[0]=A[k];
int i=k/2;//若結點值大於雙親結點,則將雙親結點向下調,並繼續向上比較
while(i>0 && A[i]<A[0])//跳出循環
{
A[k]=A[i];//雙親結點下調
k=i;
i=k/2;//繼續向上比較
}
A[k]=A[0];//複製到最終位置
}
//堆排序算法
void HeapSort(ElemType A[],int len)
{
BuildMaxHeap(A,len);//初始建堆
for(i=len;i>1;i--)
{
//n-1趟的交換和建堆過程
Swap(A[i],A[1]);//輸出堆頂元素(和堆底元素交換)
AdjustDown(A,1,i-1);//整理,再把剩餘的i-1個元素整理成堆
}
}
9.歸併排序
//歸併排序(王道)
ElemType *B(ElemType *)malloc(n+1)*sizeof(ElemType);//輔助數組B
void Merge(ElemType A[],int low,int mid,int high){
//表A的兩段A[low...mid]和A[mid+1...high]
for(int k=low;k<=high;k++)
B[k]=A[k];//將A中所有元素複製到B中
for(i=low;j=mid+1,k=i;i<=mid && j<=high;j++) {
if(B[i]<=B[j])//比較B的左右兩段中的元素
A[k]=B[i++];//將較小值複製到A中
else
A[k]=B[j++];
}
while(i<=mid) A[k++]=B[i++];//若第一個表未檢測完,複製
while(j<=high) A[k++]=B[j++];//若第二個表未檢測完,複製
}
//合併
void MergeSort(ElemType A[],int low,int high)
{
if(low<high){
int mid=(low+high)/2;//從中間劃分兩個子序列
MergeSort(A,low,mid);//對左側子序列進行遞歸排序
MergeSort(A,mid+1,high);//對右側子序列進行遞歸排序
Merge(A,low,mid,high);//歸併
}
}
//鏈表上的合併排序
//1.合併函數
Node *Merge(Node *p,Node *q)
{
Node *rear,head;//head爲啞結點,rear爲指針變量
rear=&head;//rear指向head結點
while(p!=NULL && q!=NULL)
{
//合併兩個有序鏈表
if(p->Element<=q->Element)
{
rear->Link=p;rear=p;
p=p->Link;
}
else
{
rear->Link=q;rear=q;
q=q->Link;
}
}
if(p==NULL) rear->Link=q;//將一個鏈表的剩餘部分鏈至結果鏈表的尾部
else rear->Link=p;
return head.Link;//返回結果鏈表的起始結點地址
}
//2.分割函數,將一個鏈表分割成兩個長度基本相等的鏈表
//pos指針和mid開始向後移動,pos每次向後移動兩個結點,mid每次向後一定一個結點。
//等到pos移出最後一個結點成爲空指針,以mid指示的結點作爲前半部分子表的表尾。
//函數值最後返回後半結點表的頭指針
Node *Divide(Node *p)
{
Node *pos,*mid,*q;
if(mid=p)==NULL) return NULL;
pos=mid->Link;
while(pos!=NULL)
{
pos=pos->Link;
if(pos!=NULL)
{
mid=mid->Link;pos=pos->Link;
}
}
q=mid->Link;mid->Link=NULL;
return q;
}
//3.兩路合併排序
//首先調用函數Divide對鏈表對半分割成前後兩個子鏈表。分別對這兩個子鏈表實行合併排序。
//也就是進行兩次遞歸調用,將這兩個子鏈表分別排序成有序表。
//最後,調用Merge函數將這兩個有序子鏈表合併成一個有序鏈表,從而結束合併排序。
void RMSort(Node** sublst)
{
if(*sublst!=NULL && (*sublst)->Link!=NULL)
{
Node *second=Divide(*sublst);
RMSort(sublst);
RMSort(&second);
*sublst=Merge(*sublst,second);
}
}
void RMergeSort(List *lst)
{
RMSort(&lst->First);
}
小結哈哈哈