對冒泡、直接插入、快速、歸併四種排序方式的理解,以及實際應用時的感覺。

 

如下全部程序的運行環境均爲Code::Block,代碼在不一樣編譯器下運行,結果可能會有有所不一樣

如,如下定義變量的位置並不是全在程序開頭,在VC上,可能會報錯。改正方法:將程序裏定義變量的語句放在程序的開頭便可。

************************************************************************************************************************************************************************************************ios

話很少說,直接進入正題:c++

一):直接插入排序(插入排序有多種類型,此次講平時應用最多的)

代碼實現以下:(以正序輸出爲例,倒序輸出,僅需對代碼進行適當修改便可)程序員

#include<bits/stdc++.h>
#include<algorithm>
using namespace std;

int a[100055];

int main()
{
    int n,i;
    cin>>n;

    for(i=1;i<=n;i++)
    {
        cin>>a[i];
    }

    int head;
    head = 2;
    while(head<=n)
    {
        i = head;
        while(i>=2&&a[i]<a[i-1])
        {
            if(a[i]<a[i-1])//判斷是否進行交換
            {
                swap(a[i] ,a[i-1]);//調用swap函數
                i--;
            }
        }
        head++;//起索引做用,是循環的範圍加1
    }

    for(i=1;i<=n;i++)
    {
        cout<<a[i];
    }
    return 0;
}

上述代碼的運行結果爲:算法

 

首先,咱們來理一下上述代碼的思想,上述代碼實際上就是,每次while循環依次比較a[2]與a[1]  a[3]與a[2] 、a[2]與a[1] ; a[4]與a[3]、a[3]與a[2]、a[2]與a[1] ; ………大小,直到遍歷完數組全部的元素,也就完成了排序。數組

咱們能夠把上述代碼總結成一種模型,即,上述代碼能夠對任意一組數據的任意一段連續數據進行排序(僅需改變上述代碼的初始化條件以及循環結束條件便可實現),而這一思想的典型應用就是 「滾動的榜單」問題。函數

固然,上述代碼也能夠用for循環實現,但我的認爲,用while循環,更能體現其思想本質,尤爲是經過變量head,體現比較範圍的變換。且在實際應用時,我的感受,用while循環實現更加方便。學習

二):冒泡排序

代碼以下(以正序輸出爲例)ui

#include<bits/stdc++.h>
#include<algorithm>
using namespace std;

int a[55];

int main()
{
    int n,i;
    cin>>n;
    for(i=1;i<=n;i++)
    {
        cin>>a[i];
    }

    int head = 1;
    while(head<=n)
    {
        for(i=head+1;i<=n;i++)
        {
            if(a[i]<a[head])
            {
                swap(a[i] ,a[head]);
            }
        }
        head++;
    }

    for(i=1;i<=n;i++)
    {
        cout<<a[i];
    }
    return 0;
}

上述代碼的運行結果爲:spa

  

上述代碼的思想是:首先,用數組的第一個元素與剩下的元素比較,並將最小的元素調到第一位;而後,用數組的第二個元素與剩下的元素比較,並將最小的元素調到第二位;以此類推,當head等於n時,就完成了排序設計

上述算法,對於不搞競賽的同窗來講,應該是最經常使用的排序方法了。

上述代碼一樣能夠用for循環實現。

 

三):快速排序

具體代碼以下(以升序形式輸出)

#include<iostream>
#include<algorithm>
using namespace std;

void quicksort(int l ,int r);

int a[100055];

int main()
{
    int n,i;
    cin>>n;
    for(i=1;i<=n;i++)
    {
        cin>>a[i];
    }

    quicksort(1 ,n);

    for(i=1;i<=n;i++)
    {
        cout<<a[i]<<" ";
    }

    cout<<endl;
    return 0;
}

void quicksort(int l  ,int r)
{
    int i,j,m,mid;
    i = l;
    j = r;
   mid = (l+r)/2; m = a[mid];//將數組分紅兩部分 while(i<=j) {
/***********************************************/
//注意,這裏while裏面的判斷條件爲何不帶上 「=」?
//是爲了防止當出現數組中出現一段相等的數字是,該循環變爲死循環。以下面舉出的例子。 while(a[i]<m) i++; while(a[j]>m) j--;
/***********************************************/ if(i<=j) { swap(a[i] ,a[j]); i++; j--; } } if(j-l>=1)//中止調用函數自己的判斷條件 quicksort(l ,j);//經過調用函數自身,對分開的兩部分,分別進行排序
//同時,因爲i,j通過while循環,均越過了中值mid,故,新範圍是(l ,j),下面的(i,r)同理。 if(r-i>=1)//中止調用函數自己的判斷條件 quicksort(i ,r);//經過調用函數自身,對分開的兩部分,分別進行排序 }

上述代碼運行結果爲:

 

但當將while循環中的判斷條件帶上等號是,就會出現漏洞,可能使程序變爲死循環,如:

 

上述代碼實際上體現了二分的思想:首先隨機在數組中選一個數,將一個數組分紅隨機分紅兩部分(這裏我就用區間的中間位置對應的值,實際上,能夠用rand函數隨機生成一個位置,這樣其實更加合理),分別在左,右取大於或等於、小於或等於a[mid]的數,進行交換,直到i,j越過mid值時,一次循環結束。這樣不斷地進行循環,最終便可以實現排序。

 快速排序的精髓就在於靈活應用二分的思想,這也正是其運行效率高的緣由之一。

 

四):歸併排序

代碼以下:(以升序爲例)

#include<bits/stdc++.h>
using namespace std;
#define N 100055
void mergesort(int l, int r);
int a[N];
int larray[N],rarray[N],rn,ln,lposition,rposition;//larray[N],rarray[N]用於存儲拆分獲得的兩個數組;
                                                  //ln ,rn 用於記錄被分紅的左右數組分別包含的元素的個數;
                                                  //lposition ,rposition 用於記錄數組的下表所在的位置
int main()
{
   int n,i;
   cin>>n;
   for(i=1;i<=n;i++)
       cin>>a[i];
   mergesort(1,n);
   for(i=1;i<=n;i++)
        {
            cout<<a[i]<<" ";
        }
       cout<<endl;
       return 0;
}
void mergesort(int l,int r)
{
   int i;
   int mid=(l+r)/2;
        //將數組分爲兩部分
   if(l<mid)
    mergesort(l,mid);//反覆調用函數自己,實現排序
   if(mid+1<r)
    mergesort(mid+1,r);//反覆調用函數自己,實現排序
 
   ln=rn=0;
   for(i=l;i<=mid;i++)
         larray[++ln]=a[i];
   for(i=mid+1;i<=r;i++)
         rarray[++rn]=a[i];
 
   lposition=rposition=1;
         for(i=l;i<=r;i++)
        {
//******************************************************************************/
//這部分用於解決數組中最後一個元素歸屬問題(由於對於數組的追後一個元素,已經沒有其餘元素來和他進行大小比較了,那麼,也就不能利用下面的語句,經過比較兩個數組大小爲其中的元素分配位置。這時,
//必須單獨設計語句,爲最後的一個元素分配位置,也就有了下面的這兩個語句)
            if(lposition>ln)
              a[i]=rarray[rposition++];//lposition>ln的意思是,當前數組下標的位置已經超出了數組的有效長度,下同。
       else if(rposition>rn)
              a[i]=larray[lposition++];
//*****************************************************************************/
     else if(larray[lposition]<=rarray[rposition])//這兩句是爲數組中的元素分配位置,分配原則是數值小的先分配位置,下同。
               a[i]=larray[lposition++];
     else
        a[i]=rarray[rposition++];
    }
}

  上述代碼運行結果爲:

上述代碼的核心思想是:先將數組利用二分思想屢次進行二等分,而後在從獲得的各個小份數組出發,兩兩遵循在合併的同時按從小到大排序,最終,便可實現排序。

具體圖片以下:(下圖摘自百度百科 ,連接:https://baike.baidu.com/pic/歸併排序/1639015/0/c8177f3e6709c93d673b9ed49d3df8dcd00054c3

fr=lemma&ct=single#aid=0&pic=c8177f3e6709c93d673b9ed49d3df8dcd00054c3)

歸併排序在上述幾種排序方式中應該是最複雜的排序方式了,但其高效的運行效率,以及其包含的重要思想使得做爲一個合格的程序員,或者是搞算法競賽的同窗必須掌握。

到目前爲止,我的感受,對上述歸併排序算法的思想的應用最到位的當屬經典的  「 逆序數的對數」  問題。

 

五)二分法查找

除了介紹上述四種經常使用的排序方法外,我還想補充一下關於二分法查找的一下東西。(將一個數加入到一組數裏面後,找出其在這組數裏的位置)

先看代碼實現:(以正序輸入爲例)

#include<iostream>
using namespace std;
const int maxint = 1000000000;
int main()
{
    int a[100055];
    int n,m;
    cin>>n>>m;
    int i;
    for(i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    a[0] = -maxint;
    a[n+1] = maxint;//這個語句的做用就是爲了防止因輸入的數大於已知數據的最大值而形成程序不穩定
    int l,r,mid,mark;
    l = 0,r=n+1;
    while(l<=r)
    {
        mid = (l+r)/2;
        if(a[mid]>=m)
        {
            mark = mid;
            r = mid-1;
        }
        else
        {
            l = mid+1;
        }
    }
    cout<<mark<<endl;
    return 0;
}

  以上代碼運行結果爲:

若沒有上述語句,會:

結果是亂碼!!!

因此,咱們在設計二分法的時候,必定要考慮當輸入的值大於已知的最大值這種狀況,固然,解決這個問題的方法不止上面一種,就不一一羅列了。

 

但願以上內容能對各位有所幫組!!!

************************************************************************************************************************************************************************************************************************* 

再強調一次,以上代碼均爲     經過各類途徑學習+本身的理解      所得,如若侵權,請告知,覈實以後,必當即刪除!!!

同時本人爲大一菜鳥,上面的樣例均爲自主設置的,如如有誤,還請告知,必當即改正。

本人寫此文章主要目的是對知識點整理,但限於我的水平,如上述表述內容有些許錯誤,或不到位的地方,請各位大佬告知,在下必馬上更改!!!

歡迎大佬們評論,留言。

最後,碼字不易,要是感受還行的話,求求大佬們點個贊吧。

相關文章
相關標籤/搜索