這幾天好好學習了一下排序算法 查看了各種書籍和網絡資源 挑着簡單易懂的教程好好學習了一下 基本上算是過了一遍 之後幾天 對排序算法好好地總結一下 整理一下,先把要整理的算法列舉一下:
1,選擇排序
對於一個a[0,n]的數組,依次遍歷數組,每次選出最大或最小的一個數
void selectSort(int *a,int size)
{
int min;
for(int i=0;i<size;i++)
{
min=i; //將當前下標定義爲最小值下標
for(int j=i+1;j<size;j++)
if(a[min]>a[j])
min=j; // 記錄當前最小下標
if(i!=min)
swap(a[i],a[min]); // 如果i不是最小下標 則交換
}
print(a,size);
}
2,冒泡排序
依次比較相鄰的兩個數,將小數放在前面,大數放在後面。即在第一趟:首先比較第1個和第2個數,將小數放前,大數放後。然後比較第2個數和第3個數,將小數放前,大數放後,如此繼續,直至比較最後兩個數,將小數放前,大數放後。至此第一趟結束,將最大的數放到了最後。在第二趟:仍從第一對數開始比較(因爲可能由於第2個數和第3個數的交換,使得第1個數不再小於第2個數),將小數放前,大數放後,一直比較到倒數第二個數(倒數第一的位置上已經是最大的),第二趟結束,在倒數第二的位置上得到一個新的最大數(其實在整個數列中是第二大的數)。如此下去,重複以上過程,直至最終完成排序。
void BubbleSort(int *a,int size)
{
for(int i=0;i<size-1;i++)
for(int j=0;j<size-1-i;j++)
{
if(a[j]>a[j+1])
swap(a[j],a[j+1]);
}
print(a,size);
}
優化:使用標誌的冒泡排序, 因爲當一次遍歷時 沒有發生任何的交換 事實上證明數組排序已經完成 函數可以退出
void BubbleSortWithFlag(int *a,int size)
{
bool flag;
for(int i=0;i<size-1;i++)
{
flag=true;
for(int j=0;j<size-1-i;j++)
{
if(a[j]>a[j+1])
{
swap(a[j],a[j+1]);
flag=false;
}
}
if(flag==true)
{
print(a,size);
return;
}
}
}
雞尾酒排序 :冒泡排序的變形 (轉)雞尾酒排序等於冒泡排序的輕微變形,不同的地方在於從低到高然後從高到低,而冒泡排序則僅從低到高去比較序列裏的每個元素。他可以得到比冒泡排序稍微好一點的效能,原因是冒泡排序只從一個方向進行比對(由低到高),每次循環只移動一個項目。以序列(2,3,4,5,1)爲例,雞尾酒排序只需要訪問兩次(升序降序各一次 )次序列就可以完成排序,但如果使用冒泡排序則需要四次。
(1)先對數組從左到右進行冒泡排序(升序),則最大的元素去到最右端;
(2)再對數組從右到左進行冒泡排序(降序),則最小的元素去到最左端。以此類推,依次改變冒泡的方向,並不斷縮小未排序元素的範圍。
void CockSort(int *a,int size)
{
int low=0;
int up=size-1;
int index=0;
while(low<up)
{
for(int i=low;i<up;i++)
if(a[i]>a[i+1])
{
swap(a[i],a[i+1]);
index=i;
}
up=index;
for(int i=up;i>low;i--)
if(a[i]<a[i-1])
{
swap(a[i],a[i-1]);
index=i;
}
low=index;
}
print(a,size);
}
3,插入排序 (直接,折半,路插入,表插入)
依次取出便利每一項 將每一項temp=a[j]和其之前的數進行比較,如果發現了比自己大的數 就將其放在當前j的位置 同時j--,繼續用temp和之前的數進行比較,直到沒有比自己大的數時結束。
//直接插入排序
void insertSort(int *a,int size)
{
if(size<2)
return;
int temp;
int j;
for(int i=1;i<size;i++)
{
temp=a[i];
for( j=i;j>0&&temp<a[j-1];j--)
{
a[j]=a[j-1];
}
a[j]=temp;
}
print(a,size);
}
折半插入排序:
折半插入排序(binary
insertion sort)是對插入排序算法的一種改進,由於排序算法過程中,就是不斷的依次將元素插入前面已排好序的序列中。由於前半部分爲已排好序的數列,這樣我們不用按順序依次尋找插入點,可以採用折半查找的方法來加快尋找插入點的速度。
折半插入排序算法的具體操作爲:在將一個新元素插入已排好序的數組的過程中,尋找插入點時,將待插入區域的首元素設置爲a[low],末元素設置爲a[high],則輪比較時將待插入元素與a[m],其中m=(low+high)/2相比較,如果比參考元素小,則選擇a[low]到a[m-1]爲新的插入區域(即high=m-1),否則選擇a[m+1]到a[high]爲新的插入區域(即low=m+1),如此直至low<=high不成立,即將此位置之後所有元素後移一位,並將新元素插入a[high+1]。
額 網上的方法 不是從數組a[1]開始 就是結果不正確 試了老半天 總算對了
void binaryInsertSort(int *a,int size)
{
int low,high,m,temp,j;
for (int i=1;i<size;++i){
temp=a[i];
low=0;
high=i-1;
/* if(a[i]>a[i-1]) //這句話可以稍微優化一下下速度 不影響結果
continue;*/
while (low<=high){
m=(low+high)/2;
if (temp<=a[m])
high=m-1; //當程序跳出時 high指向的其實並不是 >=temp的值的位置 而是>=temp的值的左邊的位置 所以....
else
low=m+1;
}
for( j=i;j>high+1;--j) //所以 這裏的high要加一哇
a[j]=a[j-1];
a[j]=temp;
}
print(a,size);
}
路插入 和 表插入 過幾天再說.......4,希爾排序
希爾算法思想:將整個無序序列分割成若干小的子序列分別進行插入排序。
如 一個長度爲13的數組
81 94 11 96 12 35 17 95 28 58 41 75 15
先以13/2=5爲gap進行比較 即對(81 35 41)(94 17 75)(11 95 15)分別進行排序 即對數組進行一次間距爲5的插入排序
之後以3 和 1爲間距 對數組進行插入排序
void shellSort(int *a,int size) // 代碼取自 數據結構於問題求解(c++版)
{
for(int gap=size/2;gap>0;gap=gap==2?1:static_cast<int>(gap/2.2)) // 這裏計算出當前的 間距
{
for(int i=gap;i<size;i++) // 這裏跟插入排序的代碼一樣 不過不在是每次與前一位比較 而是與前gap位進行比較
{
int temp=a[i];
int j;
for(j=i;j>=gap&&temp<a[j-1];j-=gap)
{
a[j]=a[j-gap];
}
a[j]=temp;
}
}
print(a,size);
}
5,歸併排序
歸併操作(merge),也叫歸併算法,指的是將兩個已經排序的序列合併成一個序列的操作。歸併排序算法依賴歸併操作。
歸併操作的過程如下:
- 申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列
- 設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置
- 比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置
- 重複步驟3直到某一指針達到序列尾
- 將另一序列剩下的所有元素直接複製到合併序列尾
void mergeSort(int* a,int size,int* temp,int left,int right)
{
if(left<right)
{
int mid=(left+right)/2;
mergeSort(a,size,temp,left,mid);
mergeSort(a,size,temp,mid+1,right);
merge(a,size,temp,left,mid+1,right);
}
}
void merge(int *a,int size,int *temp,int leftPos ,int rightPos,int rightEnd)
{
int leftEnd=rightPos-1;
int tempPos=leftPos;
int num=rightEnd-leftPos+1;
while(leftPos<=leftEnd && rightPos<=rightEnd)
if(a[leftPos]<=a[rightPos])
temp[tempPos++]=a[leftPos++];
else
temp[tempPos++]=a[rightPos++];
while(leftPos<=leftEnd)
{
temp[tempPos++]=a[leftPos++];
}
while(rightPos<=rightEnd)
{
temp[tempPos++]=a[rightPos++];
}
for(int i=0;i<num;i++,rightEnd--)
{
a[rightEnd]=temp[rightEnd];
}
}
6,快速排序
一趟快速排序的算法是:
1)設置兩個變量I、J,排序開始的時候:I=0,J=N-1;5)重複第3、4、5步,直到 I=J; (3,4步是在程序中沒找到時候j=j-1,i=i+1,直至找到爲止。找到並交換的時候i, j指針位置不
變。另外當i=j這過程一定正好是i+或j-完成的最後另循環結束。)
int partition(int *a,int low,int high)
<//這裏從兩端開始 沒有考慮抽取支點的過程 支點可以選取第一個 中間點 和最後一個點 選取中間大的值 交換到數組的最後面
{
int pivot=a[high];
int i,j;
for( i=low-1,j=high;;){
while(a[++i]<pivot &&i<high){}; //從前往後 找大於支點的
while(a[--j]>pivot &&j>low){}; //從前往前 找小於支點的
if(i<j){
swap(a[i],a[j]); //若找到 則交換
}
else
break;
}
swap(a[i],a[high]); // 將支點交換到小於與大於的分解處
return i;
}
void qucikSort(int*a,int low,int high)
{
if(low<high)
{
int mid=partition(a,low,high);
qucikSort(a,low,mid-1); //遞歸進行快排
qucikSort(a,mid+1,high);
}
}
7,堆排序
取自算法導論
int Left(int i) // 這裏取左右和父節點 是以 頂節點index爲0來考慮的
{
return i*2+1;
}
int Right(int i)
{
return i*2+2;
}
int Parent(int i)
{
return (i-1)/2;
}
void max_heapify(int *a,int heapSize,int i)
// 是對最大堆進行操作的重要子程序 用於保持堆的性質
{
int left=Left(i);
int right=Right(i);
int largest;
if(left<heapSize&&a[left]>a[i])
largest=left;
else
largest=i;
if(right<heapSize&&a[right]>a[largest])
largest=right;
if(largest!=i) //如果該父節點不是最大
{
swap(a[i],a[largest]); // 則將較大的子節點 與父節點交換 因爲交換可能破壞之後的結構
max_heapify(a,heapSize,largest); // 故 繼續對交換的那個子節點做一次最大堆操作
}
}
void build_max_heap(int *a,int size) //構建最大堆 (堆的大小 小於等於 數組的大小 )
{
int heapSize=size;
for(int i=size/2-1;i>=0;i--)
{
max_heapify(a,heapSize,i);
}
}
void heapSort(int* a,int size)
{
build_max_heap(a,size);
int heapSize=size;
for(int i=size-1;i>=1;i--)
{
swap(a[0],a[i]); // 講頂節點與最後一個交換 最後一個節點即存的最大值
heapSize--; //最大堆的節點個數減一 不考慮 最後點
max_heapify(a,heapSize,0); // 交換後 重新保持最大堆的性質
}
}
8,計數排序
void countingSort(int *a,int size,int k)
{
int *c=new int[k+1];
int *b=new int[size];
for(int i=0;i<=k;i++)
{
c[i]=0;
}
for(int j=0;j<size;j++)
{
b[j]=0;
c[a[j]]++;
}
for(int t=1;t<k+1;t++)
{
c[t]=c[t]+c[t-1];
}
for(int t=size-1;t>=0;t--)
{
b[c[a[t]]]=a[t];
c[a[t]]--;
}
//int pos=0; // 上面是算法導論的標準方法 下面的方式我覺得直觀而且快一些
//for(int i=0;i<=k;i++)
//{
// for(int t=0;t<c[i];t++)
// b[pos++]=i;
//
//}
print(b,size);
}
9,桶排序
class Node{
public: int key;
public: Node* next;
};
void bucketSort(int a[],int size,int bucketSize)
{
//Node **bucketNode1=(Node**)malloc(bucketSize*sizeof(Node *));
Node ** bucketTable=new Node*[bucketSize];
for(int i=0;i<bucketSize;i++)
{
//bucketNode1[i]=(Node *)malloc(sizeof(Node));
Node* node=new Node();
node->key=0;
node->next=NULL;
bucketTable[i]=node;
}
for(int j=0;j<size;j++)
{
Node* node=new Node();
node->key=a[j];
node->next=NULL;
int index=a[j]/10;
Node *buketNode=bucketTable[index]; // 找到自己應當所在的桶
if(buketNode->key==0)
{
buketNode->next=node;
bucketTable[index]->key++;
}else{
while(buketNode->next!=NULL && buketNode->next->key<=node->key) // 在該桶中的鏈表中尋找自己的位置
buketNode=buketNode->next;
node->next=buketNode->next; // 鏈表的插入操作
buketNode->next=node;
bucketTable[index]->key++;
}
int pos=0;
for(int t=0;t<bucketSize;t++)
{
for(Node *p=bucketTable[t]->next;p!=NULL;p=p->next)
{
a[pos++]=p->key;
}
}
}
}
10,基數排序
11,位圖排序
今後幾天 會詳細總結。。。。。