數據結構也到達了終點,最後的考試也步步緊逼。最後的時間裏,有必要合理分配空閒時間,制定好學習計劃,在考前充分複習,儘自己最大努力。這最後一章,介紹了與查找技術不同的排序技術,但同樣的都是一些優秀的思想方法,思維上有些難度,不去深入理解,很容易出現細節方面問題。
一,排序的基本概念
1,穩定性的概念:同一數據重複出現,相對位置不發生變化則稱穩定。
2,時間性能取決於比較次數+移動次數。
3,各類算法存儲結構,順序存儲數組下標從1開始,0下標做輔助空間。
二,插入類的排序
1,直接插入排序,穩定
在插第i個記錄時前i-1個已排好序
for (i=2; i<=n; i++) {
r[0]=r[i]; j=i-1;
while (r[0]<r[j]) { //r[0]做監視哨
r[j+1]=r[j];
j=j-1;
}
r[j+1]=r[0];
}
2,希爾排序(縮小增量排序),不穩定
將整個待排序記錄分割成若干個子序列,在子序列內分別進行直接插入排序,待整個序列中的記錄基本有序時,對全體記錄進行直接插入排序。初始增量d=n/2(d個子序列同步進行)
for (d=n/2; d>=1; d=d/2){
for (i=d+1; i<=n; i++) {
r[0]=r[i];
j=i-d;
while (j>0 && r[0]<r[j])
{
r[j+d]=r[j]; //記錄後移d個位置
j=j-d; //比較統一子序列的前一個記錄
}
r[j+d]=r[0];
}
}
三,交換排序
1,相鄰比序(冒泡),穩定
改進原版冒泡提高性能,減少比較次數,記錄最後一次交換位置,下次交換到此結束,無交換時退出。
exchange=n;
while (exchange)
{
bound=exchange;
exchange=0;
for (j=1; j<bound; j++)
if (r[j]>r[j+1]) {
r[j]←→r[j+1];
exchange=j; //記錄最後交換的位置
}
}
2,快速排序
首先選一個軸值(即比較的基準),通過一趟排序將待排序記錄分割成獨立的兩部分,前一部分記錄的關鍵碼均小於或等於軸值,後一部分記錄的關鍵碼均大於或等於軸值,然後分別對這兩部分重複上述方法,直到整個序列有序。可用二叉樹描述,深度表示遞歸次數。
void QuickSort (int r[ ], int first, int end )
{
if (first < end) {
pivotpos = Partition (r, first, end ); //一次劃分
//對前一個子序列進行快速排序
QuickSort (r, first, pivotpos-1);
//對後一個子序列進行快速排序
QuickSort (r, pivotpos+1, end );
}
}
int Partition(int r[ ], int first, int end)
{
i=first; j=end; //初始化
r[0]=r[i];
while (i<j)
{
while (i<j && r[0]<= r[j]) j--; //右側掃描
if (i<j) {
r[i]=r[j]; i++; //將較小記錄交換到前面
}
while (i<j && r[i]<= r[0]) i++; //左側掃描
if (i<j) {
r[j]=r[i]; j--; //將較大記錄交換到後面
}
}
r[i]=r[0];
retutn i; //i爲軸值記錄的最終位置
}
四,選擇排序,無最好最壞情況之分。
1,簡單選擇排序,不穩定,時間複雜度永遠是n方
基本思想:第i 趟在n-i+1(i=1,2,…,n-1)個記錄中選取關鍵碼最小的記錄作爲有序序列中的第i個記錄。
for ( i=1; i<n; i++)
{
index=i; //index記錄有序數組的最後位置
for (j=i+1; j<=n; j++)
if (r[j]<r[index]) index=j;
if (index!=i) r[i]<==>r[index];
}
2,堆排序,不穩定
堆是具有下列性質的完全二叉樹:每個結點的值都小於或等於其左右孩子結點的值(稱爲小根堆),或每個結點的值都大於或等於其左右孩子結點的值(稱爲大根堆)。
要得到升序的序列則要構造大根堆
#include<bits/stdc++.h>
using namespace std;
int judge(int a[],int i,int j)
{
int k=i;
int l=2*k;
int t=a[k];
while(l<=j)
{
if(l<j&&a[l]<a[l+1])
l++;
if(t>a[l]) break;
else
{
a[k]=a[l];
k=l;
l=2*k;
}
}
a[k]=t;
}
void h(int a[],int n){
for(int j=n/2;j>=1;j--)
judge(a,j,n);
for(int i=1;i<n;i++)
{
//swap(a[1],a[n-i+1]);
int f=a[1];
a[1]=a[n-i+1];
a[n-i+1]=f;
judge(a,1,n-i);
}
}
int main()
{
int n,a[9999];
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i];
h(a,n);
for(int i=1;i<=n;i++)
cout<<a[i]<<" ";
}
五,歸併排序(分治的思想)
將n個待排序的記錄的序列看成n個長度爲一的有序序列。
1,二路歸併排序
歸併排序的主要操作是歸併,其主要思想是:將若干有序序列逐步歸併,最終得到一個有序序列。 無法原地進行引入相同大小的數組存中間結果
#include<bits/stdc++.h>
using namespace std;
void judge(int a[],int b[],int s,int m,int t)
{
int i=s;
int j=m+1;
int k=s;
while(i<=m&&j<=t)
{
if(a[i]<=a[j]) b[k++]=a[i++];
else
b[k++]=a[j++];
}
if(i<=m)
while(i<=m)
{
b[k++]=a[i++];
}
else
while(j<=t)
b[k++]=a[j++];
}
void mp(int a[],int b[],int n,int h)
{
int i=1;
while(i<=n-2*h+1)
{
judge(a,b,i,i+h-1,i+2*h-1);
i+=2*h;
}
if(i<n-h+1) judge(a,b,i,i+h-1,n);
else for(int k=i; k<=n; k++)
b[k]=a[k];
}
void msort(int a[],int b[],int n)
{
int h=1;
while(h<n)
{
mp(a,b,n,h);
h=2*h;
mp(b,a,n,h);
h=2*h;
}
}
int main()
{
int n,a[9999],b[9999];
cin>>n;
for(int i=1; i<=n; i++)
cin>>a[i];
msort(a,b,n);
for(int i=1; i<=n; i++)
cout<<a[i]<<" ";
cout<<endl;
for(int i=1; i<=n; i++)
cout<<b[i]<<" ";
}
六,分配排序
基於分配和收集,不經過比較
1,桶式排序,穩定
int main(){
Node *s,*first; head *list;
int m;cin>>m;
list=new head[m];
for (int i=0;i<=m;i++){
list[i].first=NULL; list[i].rear=NULL;}
int n;
cin>>n;
first=NULL;
for( i=0;i<n;i++)
{ s=new Node; cin>>s->data; s->next=first; first=s; }
distribute(first,n,list);
collect(list,first,m);
Node *p=first;
while(p){
cout<<p->data<<"\t"; p=p->next; }
cout<<endl;
return 0;
}
2,基數排序,穩定
低位優先,尾插法
void distribute(Node *first, int n, head *list,int d){
Node *p,*q; p=first; int data, s,t;
while(p) {
data=p->data;
s=pow(10,d);t=s/10;data=data%s;data=data/t;
q=p->next;
if( list[data].first)
{list[data].rear->next=p; list[data].rear=p;}
else
list[data].first=list[data].rear=p;
list[data].rear->next=NULL;
p=q;
}
}
void collect( head *list, Node *&first,int m){
int i=0,j;
while(list[i].first==NULL) i++;
if(i>m) return;
first=list[i].first;
while(i<=m) {
j=i+1;
while(list[j].first==NULL) j++;
if(j>m) return;
list[i].rear->next=list[j].first;
i=j;
}
}
cin>>d;
for(i=1;i<=d;i++) //處理每一“位”(個位、十位...)
{
distribute(first,n,list ,i);
collect(list,first,m);
for (int i=0;i<=m;i++)
{
list[i].first=NULL;
list[i].rear=NULL;
}
}