1. 程式人生 > >排序問題(蠻力法,分治法)

排序問題(蠻力法,分治法)

蠻力法:

選擇排序法:

演算法思想:在剩餘序列中選出最小(或最大)的關鍵字,和剩餘序列的第一個關鍵字交換位置,依次選擇下去(每次掃描結束找出最小的一個元素依次放在前面的位置),直至使整個序列有序。

程式碼實習:

#include<iostream>
using namespace std;
void print(int a[], int n)

{  
    for(int j= 0; j<n; j++)
	{  
        cout<<a[j] <<"  ";  
    }  
    cout<<endl;  
}

void selectSort(int a[], int len)
{
	int minindex, temp;
	for(int i = 0; i<len-1;i++)
	{
	    minindex = i;
	    for(int j = i+1; j<len; j++)
		{
		    if(a[j]<a[minindex])
		    minindex = j;		
		}
		temp = a[i];
		a[i] = a[minindex];
		a[minindex] = temp;
	}
}

int main()
{  
    int a[10] = {8,1,9,7,2,4,5,6,10,3};  
    cout<<"初始序列:";  
    print(a,10);  
    selectSort(a,10);  
    cout<<"排序結果:";  
    print(a,10);  
    system("pause"); 
} 

演算法分析:

演算法中兩層迴圈的執行次數和初始序列沒有關係,第二層迴圈每一次都需要遍歷剩餘帶排序序列,故時間複雜度為O(n^2)。

氣泡排序法:

演算法思想:

        氣泡排序法每次是將相鄰兩個元素進行比較,將較大(小)的元素放在後面,這樣一次掃描結束後就將當前最大(小)的那個元素放在了列表的後面。

程式碼實現:

/*①傳統的氣泡排序法sort_Bubble:內外兩輪迴圈,前後兩個資料依次比較; 
②改進的氣泡排序法sort_Bubble2:同樣是內外兩次迴圈,但用一個標記來標
識當前這一輪是否發生資料交換,如果沒發生,說明排序實際上已經完成,可
以提前結束排序任務。*/
void swap(int*a , int* b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

void sort_Bubble(int arr[],int N)
{
    /// step用於統計比較次數
    int step = 0;
    for(int i=0;i<N;i++)
    {
        for(int j=0;j<N-i-1;j++)
        {
            step++;
            if(arr[j]>arr[j+1])
                swap(arr+j,arr+j+1);
        }
    }
    cout<<"sort_Bubble:"<<step<<endl;
}

void sort_Bubble2(int arr[],int N)
{
    /// step用於統計比較次數
    int step = 0;
    bool changeFlag = true;
    for(int i=0;i<N;i++)
    {
        if(changeFlag == false)
            break;
        changeFlag = false;
        for(int j=0;j<N-i-1;j++)
        {
            step++;
            if(arr[j]>arr[j+1])
            {
                swap(arr+j,arr+j+1);
                changeFlag = true;
            }
        }
    }
    cout<<"sort_Bubble2:"<<step<<endl;
}

int main()
{
    int tmpArray[10] = {0,-3,12,234,9,45,99,123,401,990};
    int tmpArray2[10] = {0,-3,12,234,9,45,99,123,401,990};
    for(int i=0;i<10;i++)
        cout<<tmpArray[i]<<"\t";
    cout<<"\n氣泡排序法:"<<endl;
    sort_Bubble(tmpArray,10);
    sort_Bubble2(tmpArray2,10);
    for(int i=0;i<10;i++)
        cout<<tmpArray[i]<<"\t";
    return 0;
}

演算法分析:

O(n^2).

分治法:

將一個難以解決的大問題,劃分成一些規模較小的子問題,從而得到原問題的解。

一般來說,分治法由以下三步組成:

1)劃分:把規模為n的原問題劃分為k個規模較小的子問題

2)求解子問題:各個子問題的解法與原問題的解法通常是相同的,可以用遞迴方法求解各個子問題,有時遞迴問題也可以用迴圈來實現。

3)合併:把各個子問題的解合併起來,合併的代價因情況的不同而有很大差異,分治演算法的效率很大程度上依賴於合併的實現。

歸併排序法:

演算法描述:

演算法實現:

#include<iostream>
using namespace std;
const int maxn=500000,INF=0x3f3f3f3f;
int L[maxn/2+2],R[maxn/2+2];
void merge(int a[],int n,int left,int mid,int right)
{
    int n1=mid-left,n2=right-mid;
    for(int i=0;i<n1;i++)
        L[i]=a[left+i];
    for(int i=0;i<n2;i++)
        R[i]=a[mid+i];
    L[n1]=R[n2]=INF;
    int i=0,j=0;
    for(int k=left;k<right;k++)
    {
        if(L[i]<=R[j])
            a[k]=L[i++];
        else
            a[k]=R[j++];
    }
}
void mergesort(int a[],int n,int left,int right)
{
    if(left+1<right)
    {
        int mid=(left+right)/2;
        mergesort(a,n,left,mid);
        mergesort(a,n,mid,right);
        merge(a,n,left,mid,right);
    }
}
int main()
{
    int a[maxn],n;
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>a[i];
    mergesort(a,n,0,n);
    for(int i=0;i<n;i++)
    {
        if(i)
            cout<<" ";
        cout<<a[i];
    }
    cout<<endl;
    return 0;
}

演算法分析:

       歸併排序演算法採用的是分治演算法,即把兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序的序列分成若干個子序列,每個子序列都是有序的,然後把有序子序列合併成整體有序序列,這個過程也稱為2-路歸併.一般來說,n個數據大致會分為logN層,每層執行merge的總複雜度為O(n), 所以總的複雜度為O(nlogn)。    

快速排序法:

演算法描述:

快速排序首先選一個軸值(pivot,也有叫基準的),將待排序記錄劃分成獨立的兩部分,左側的元素均小於軸值,右側的元素均大於或等於軸值,然後對這兩部分再重複,直到整個序列有序。過程是和二叉搜尋樹相似,就是一個遞迴的過程

演算法實現:

排序函式:

QuickSort(int arr[], int first, int end){
 if (first < end) {
   int pivot = OnceSort(arr,first,end);
   //已經有軸值了,再對軸值左右進行遞迴
   QuickSort(arr,first,pivot-1);
   QuickSort(arr,pivot+1,end);
 }
}

一次排序的函式:

int OnceSort(int arr[], int first, int end){
 int i = first,j = end;
 //當i<j即移動的點還沒到中間時迴圈
 while(i < j){
  //右邊區開始,保證i<j並且arr[i]小於或者等於arr[j]的時候就向左遍歷
  while(i < j && arr[i] <= arr[j]) --j;
  //這時候已經跳出迴圈,說明j>i 或者 arr[i]大於arr[j]了,如果i<j那就是arr[i]大於arr[j],那就交換
  if(i < j){
   int temp = arr[i];
   arr[i] = arr[j];
   arr[j] = temp;
  }
  //對另一邊執行同樣的操作
  while(i < j && arr[i] <= arr[j]) ++i;
  if(i < j){
   int temp = arr[i];
   arr[i] = arr[j];
   arr[j] = temp;
  }
 }
 //返回已經移動的一邊當做下次排序的軸值
 return i;
}

演算法分析:

快速排序時間複雜度的最好情況和平均情況一樣為O(nlog2 n),最壞情況下為O(n^2 ),這個看起來比前面兩種排序都要好,但是這是不穩定的演算法,並且空間複雜度高一點( O(nlog2 n),而且快速排序適用於元素多的情況。

發現一個超詳細的排序總結https://www.cnblogs.com/zyb428/p/5673738.html