1.交換排序的基本思想是:兩兩比較待排序記錄的關鍵字,若反序即進行交換,直到沒有反序的記錄爲止。
static void swap(int *p,int *s )
{
int tmp;
tmp=*p;
*p=*s;
*s=tmp;
}
void exchange_sort(int *arr,int len)
{
for (int i=0;i<len-1;i++)
{
for (int j=i+1;j<len;j++)
{
if (arr[i]>arr[j])
{
swap(&arr[i],&arr[j]);
}
}
}
}
應用交換排序基本思想的主要排序方法有:冒泡排序(Bubble sort)和快速排序(Quick sort)。
</pre><pre name="code" class="cpp" style="color: rgb(85, 85, 85);">//冒泡排序
void bubble_sort(int *arr,int len)
{
for (int i=0;i<len-1;i++)
{
for (int j=0;j<len-i-1;j++)
{
if (arr[j]>arr[j+1])
{
swap(&arr[j],&arr[j+1]);
}
}
}
}
//改進版冒泡排序,當一趟比較後沒有交換(flag=false)即已經有序,不再進行,直接跳出
void bubble_sort_ex(int *arr,int len)
{
bool flag=true;
for (int i=0;i<len-1 && flag;i++)
{
flag=false;
for (int j=0;j<len-i-1;j++)
{
if (arr[j]>arr[j+1])
{
swap(&arr[j],&arr[j+1]);
flag=true;
}
}
}
}
快速排序是對冒泡排序的一種改進,基本思想:通過一趟排序將待排記錄分割爲獨立的兩部分,其中一部分記錄關鍵字均比另一部分記錄的關鍵字小,則可對這兩部分記錄繼續進行排序,已達到整個有序。
//快速排序
static int partition(int *arr,int left,int right)
{
int key=arr[left];
while (left<right)
{
while (left<right && arr[right]>=key)
{
right--;
}
arr[left]=arr[right];
while (left<right && arr[left]<=key)
{
left++;
}
arr[right]=arr[left];
}
arr[left]=key;
return left;
}
static void quick(int *arr,int left,int right)
{
if (left<right)
{
int pivo=partition(arr,left,right);
quick(arr,left,pivo-1);
quick(arr,pivo+1,right);
}
}
void quick_sort(int *arr,int len)
{
quick(arr,0,len-1);
}
2.選擇排序的基本思想是:每一趟在n-i-1個記錄中選取關鍵字最小的記錄作爲有序序列中第i個記錄。
最簡單的是簡單選擇排序,一趟簡單的選擇排序的操作爲:通過n-i次關鍵字間的比較,從n-i-1個記錄中選擇出關鍵字最小的記錄,並和第i個記錄交換。
通俗地講就是,對比數組中前一個元素跟後一個元素的大小,如果後面的元素比前面的元素小則用一個變量k來記住他的位置,接着第二次比較,前面"後一個元素"現變成了"前一個元素",繼續跟他的"後一個元素"進行比較如果後面的元素比他要小則用變量k記住它在數組中的位置(下標),等到循環結束的時候,我們應該找到了最小的那個數的下標了,然後進行判斷,如果這個元素的下標不是第一個元素的下標,就讓第一個元素跟他交換一下值,這樣就找到整個數組中最小的數了。然後找到數組中第二小的數,讓他跟數組中第二個元素交換一下值,以此類推。
//選擇排序
void select_sort(int *arr,int len)
{
int min=arr[0];
int min_index=0;
int i;
int j;
for (i=0;i<len-1;i++)
{
min=arr[i];
min_index=i;
for (j=i+1;j<len;j++)
{
if (min>arr[j])
{
min=arr[j];
min_index=j;
}
}
if(i!=min_index)//下標並沒有改變
{
swap(&arr[i],&arr[min_index]);
}
}
}
選擇排序的主要操作是進行關鍵字間的比較,因此,改進簡單選擇排序應從減少“比較”出發。基於選擇排序的思想,出現了堆排序。
堆是一個近似完全二叉樹的結構,並同時滿足堆性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。
爲使記錄序列按關鍵詞非遞減有序排列,則在堆排序的算法中先建一個“大根堆:,即先選得一個關鍵字爲最大的記錄並與序列最後一個記錄交換,然後對序列中前n-1個記錄進行篩選,重新調整爲大根堆,如此反覆直至排序結束。
//堆排序
static void heap_adjust(int *arr,int start,int len)
{
int tmp=arr[start];
int i;
while ((i=2*start+1)<len)
{
if (i+1<len && arr[i]<arr[i+1])
{
i++;
}
if (tmp>=arr[i])
{
break;
}
arr[start]=arr[i];
start=i;
}
arr[start]=tmp;
}
void heap_sort(int *arr,int len)
{
for (int start=len/2-1;start>=0;start--)
{
heap_adjust(arr,start,len);
}
for (int i=len;i>1;i--)
{
swap(&arr[0],&arr[i-1]);
heap_adjust(arr,0,i-1);
}
}
//插入排序
//void insert_sort(int *arr,int len)
//{
// int tmp;
// int j;
//
// for (int i=1;i<len;i++)
// {
// tmp=arr[i];
// for (j=0;j<i;j++)
// {
// if (arr[j]>=tmp)
// {
// break;
// }
// }
//
// for (int k=i-1;k>=j;k--)
// {
// arr[k+1]=arr[k];
// }
//
// arr[j]=tmp;
// }
//}
void insert_sort(int *arr,int len)
{
int tmp;
int j;
for (int i=1;i<len;i++)
{
tmp=arr[i];
for (j=i-1;j>=0;j--)
{
if (arr[j]<=tmp)
{
break;
}
arr[j+1]=arr[j];
}
arr[j+1]=tmp;
}
}
註釋掉的代碼符合人們的思維邏輯,但效率呢?從註釋掉的代碼到未註釋的代碼,想必你已經看出哪些地方進行了改進,未註釋的代碼纔是真正的直接插入排序。當然還有其他的插入排序,這裏再展示一種:
void half_insert_sort(int *arr,int len)
{
int tmp;
int j;
int left;
int right;
int mid;
for (int i=1;i<len;i++)
{
tmp=arr[i];
left=0;
right=i-1;
while (left<=right)
{
//mid=(left+right)/2;
mid=(right-left+1)/2+left;
if (tmp<arr[mid])
{
right=mid-1;
}
else
{
left=mid+1;
}
}
for (j=i-1;j>=right+1;j--)
{
arr[j+1]=arr[j];
}
arr[j+1]=tmp;
}
}
希爾排序又稱“縮小增量排序”,是基於插入排序的思想,但在時間效率上較前幾種排序有較大改進。它的基本思想是:先將整個待排記錄序列分割成若干子序列分別進行直接插入排序,待整個序列中的記錄“基本有序”時,再對全體記錄進行一次直接插入排序。
//希爾排序
static void shell(int *arr,int len,int gap)
{
int tmp;
int i;
int j;
for (i=gap;i<len;i++)
{
tmp=arr[i];
for (j=i-gap;j>=0;j-=gap)
{
if (arr[j]<tmp)
{
break;
}
arr[j+gap]=arr[j];
}
arr[j+gap]=tmp;
}
}
void shell_sort(int *arr,int len)
{
//for (int gap=len/2;gap>0;gap/=2)
//{
// shell(arr,len,gap);
//}
int gap_index[]={701,301,132,57,23,10,4,1};//特殊序列
int gap_len=sizeof(gap_index)/sizeof(gap_index[0]);
int i;
for (i=0;i<len;i++)
{
if (gap_index[i]<len)
{
break;
}
i++;
}
for (i;i<gap_len;i++)
{
shell(arr,len,gap_index[i]);
}
}
那麼,分幾組對希爾排序的效率有沒有影響呢?答案是肯定的,關於如何選擇增量,希爾排序效率更高,可以作爲思考。可在維基百科上查看有關希爾排序怎樣分組。
4.歸併排序是建立在歸併操作上的一種有效的排序算法,該算法是採用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲二路歸併。
//歸併排序
static void merge(int *arr,int len,int gap)
{
int *buff=(int *)malloc(sizeof(int)*len);
assert(buff!=NULL);
int k=0;
int low1=0;
int high1=low1+gap-1;
int low2=high1+1;
int high2=low2+gap-1<len ? low2+gap-1:len-1;//如果越界,進行拉回
while (low2<len)//歸併段2只要有,就需要繼續歸併
{
while (low1<=high1 && low2<=high2)
{
if (arr[low1]<=arr[low2])
{
buff[k++]=arr[low1++];
}
else
{
buff[k++]=arr[low2++];
}
}
while (low1<=high1)
{
buff[k++]=arr[low1++];
}
while (low2<=high2)
{
buff[k++]=arr[low2++];
}
low1=high2+1;
high1=low1+gap-1;
low2=high1+1;
high2=low2+gap-1<len ? low2+gap-1:len-1;//如果越界,進行拉回
}
while (low1<len)//沒有歸併段2,但有歸併段1
{
buff[k++]=arr[low1++];
}
for (int i=0;i<len;i++)
{
arr[i]=buff[i];
}
free(buff);
}
static void merge_ex(int *arr,int len,int gap)
{
int *buff=(int *)malloc(sizeof(int)*gap*2);
assert(buff!=NULL);
int *p=buff;
int m=0;
int low1=0;
int high1=low1+gap-1;
int low2=high1+1;
int high2=low2+gap-1<len ? low2+gap-1:len-1;
while (low1<len)
{
if (low2<len)
{
while (low1<=high1 && low2<=high2)
{
if (arr[low1]<=arr[low2])
{
*buff=arr[low1++];
buff++;
}
else
{
*buff=arr[low2++];
buff++;
}
}
}
while (low1<=high1)
{
*buff=arr[low1++];
buff++;
}
while (low2<=high2)
{
*buff=arr[low2++];
buff++;
}
buff=p;
for (int i=0;i<2*gap && m<len;i++)
{
arr[m]=*buff;
buff++;
m++;
}
buff=p;
low1=high2+1;
high1=low1+gap-1<len ? low1+gap-1:len-1;
low2=high1+1;
high2=low2+gap-1<len ? low2+gap-1:len-1;
}
free(buff);
}
void merge_sort(int *arr,int len)
{
for (int gap=1;gap<len;gap*=2)
{
//merge(arr,len,gap);
merge_ex(arr,len,gap);
//show(arr,len);
}
}