程式設計師必知必會的十大排序算法

2021-02-07 菜鳥教程
緒論

身為程式設計師,十大排序是所有合格程式設計師所必備和掌握的,並且熱門的算法比如快排、歸併排序還可能問得比較細緻,對算法性能和複雜度的掌握都有要求。本文將帶你捋一捋常見的十大排序算法,讓你輕輕鬆鬆掌握!

首先對於排序來說,大多數人對排序的概念停留在冒泡排序或者JDK中的Arrays.sort(),手寫各種排序對很多人來說都是一種奢望,更別說十大排序算法了。

對於排序的分類,可以根據不同的維度比如複雜度、內外部、比較非比較等維度來分類。我們正常講的十大排序算法是內部排序,我們更多是基於「比較和非比較」這個維度將它們分為兩大類:

「非比較類的有桶排序、基數排序、計數排序」。也有很多人將排序歸納為8大排序,那就是因為基數排序、計數排序是建立在桶排序之上或者是一種特殊的桶排序,但是基數排序和計數排序有它特有的特徵,所以在這裡就將它們分別列出來。比較類排序也有更細緻的分法,有基於交換的、基於插入的、基於選擇的、基於歸併的,更細緻的可以看下面的腦圖。腦圖
交換類冒泡排序

冒泡排序,又稱起泡排序,它是一種基於交換的排序典型,也是快排思想的基礎,冒泡排序是一種穩定排序算法,時間複雜度為O(n^2)。基本思想是:「循環遍歷多次,每次從前往後把大元素往後調,每次確定一個最大(最小)元素,多次後達到排序序列。」(或者從後向前把小元素往前調)。

具體思想為(把大元素往後調):

從第一個元素開始往後遍歷,每到一個位置判斷是否比後面的元素大,如果比後面元素大,那麼就交換兩者大小,然後繼續向後,這樣的話進行一輪之後就可以保證「最大的那個數被交換到最末的位置」。第二次同樣從開始起向後判斷著前進,如果當前位置比後面一個位置更大,那麼就和他後面的那個數交換。但是有點需要注意的是,這次並不需要判斷到最後,只需要判斷到倒數第二個位置就行(因為第一次我們已經確定最大的在倒數第一,這次的目的是確定倒數第二)。同理,後面的遍歷長度每次減一,直到第一個元素,使得整個元素有序。

例如2 5 3 1 4排序過程如下:

實現代碼為:

public void  maopaosort(int[] a) {
  // TODO Auto-generated method stub
  for(int i=a.length-1;i>=0;i--)
  {
    for(int j=0;j<i;j++)
    {
      if(a[j]>a[j+1])
      {
        int team=a[j];
        a[j]=a[j+1];
        a[j+1]=team;
      }
    }
  }
}

快速排序

快速排序是對冒泡排序的一種改進,採用遞歸分治的方法進行求解。而快排相比冒泡是一種不穩定排序,時間複雜度最壞是O(n^2),平均時間複雜度為O(nlogn),最好情況的時間複雜度為O(nlogn)。

對於快排來說,「基本思想」是這樣的:

快排需要將序列變成兩個部分,就是「序列左邊全部小於一個數」「序列右邊全部大於一個數」,然後利用遞歸的思想再將左序列當成一個完整的序列再進行排序,同樣把序列的右側也當成一個完整的序列進行排序。其中這個數在這個序列中是可以隨機取的,可以取最左邊,可以取最右邊,當然也可以取隨機數。但是「通常」不優化情況下,我們取最左邊的那個數。

實現代碼為:

public void quicksort(int [] a,int left,int right)
{
  int low=left;
  int high=right;
  //下面兩句的順序一定不能混,否則會產生數組越界!!!very important!!!
  if(low>high)//作為判斷是否截止條件
    return;
  int k=a[low];//額外空間k,取最左側的一個作為衡量,最後要求左側都比它小,右側都比它大。
  while(low<high)//這一輪要求把左側小於a[low],右側大於a[low]。
  {
    while(low<high&&a[high]>=k)//右側找到第一個小於k的停止
    {
      high--;
    }
    //這樣就找到第一個比它小的了
    a[low]=a[high];//放到low位置
    while(low<high&&a[low]<=k)//在low往右找到第一個大於k的,放到右側a[high]位置
    {
      low++;
    }
    a[high]=a[low];   
  }
  a[low]=k;//賦值然後左右遞歸分治求之
  quicksort(a, left, low-1);
  quicksort(a, low+1, right);  
}

插入類排序直接插入排序

直接插入排序在所有排序算法中的是最簡單的排序方式之一。和我們上學時候 從前往後、按高矮順序排序一樣,那麼一堆高低無序的人群中,從第一個開始,如果前面有比自己高的,就直接插入到合適的位置。「一直到隊伍的最後一個完成插入」,整個隊列才能滿足有序。

直接插入排序遍歷比較時間複雜度是每次O(n),交換的時間複雜度每次也是O(n),那麼n次總共的時間複雜度就是O(n^2)。有人會問折半(二分)插入能否優化成O(nlogn),答案是不能的。因為二分只能減少查找複雜度每次為O(logn),而插入的時間複雜度每次為O(n)級別,這樣總的時間複雜度級別還是O(n^2)。

插入排序的具體步驟:

選取當前位置(當前位置前面已經有序),目標就是將當前位置數據插入到前面合適位置。

實現代碼為:

public void insertsort (int a[])
{
  int team=0;
  for(int i=1;i<a.length;i++)
  {
    System.out.println(Arrays.toString(a));
    team=a[i];
    for(int j=i-1;j>=0;j--)
    {

      if(a[j]>team)
      {
        a[j+1]=a[j];
        a[j]=team; 
      } 
      else {
        break;
      }
    }
  } 
}

希爾排序

直接插入排序因為是O(n^2),在數據量很大或者數據移動位次太多會導致效率太低。很多排序都會想辦法拆分序列,然後組合,希爾排序就是以一種特殊的方式進行預處理,考慮到了「數據量和有序性」兩個方面維度來設計算法,使得序列前後之間小的儘量在前面,大的儘量在後面,進行若干次的分組別計算,最後一組即是一趟完整的直接插入排序。

對於一個長串,希爾首先將序列分割(非線性分割),是「按照某個數模」(取餘,這個類似報數1、2、3、4,1、2、3、4這樣形式)進行分割,先「各組分別進行直接插入排序」,這樣「很小的數在後面」,可以通過「較少的次數移動到相對靠前」的位置,然後慢慢合併變長,再稍稍移動。

因為每次這樣插入都會使得序列變得更加有序,稍微有序序列執行直接插入排序成本並不高。所以這樣能夠在合併到最終的時候,基本小的在前,大的在後,代價越來越小。這樣希爾排序相比插入排序還是能節省不少時間的。

實現代碼為:

public void shellsort (int a[])
{
  int d=a.length;
  int team=0;//臨時變量
  for(;d>=1;d/=2)//共分成d組
    for(int i=d;i<a.length;i++)//到那個元素就看這個元素在的那個組即可
    {
      team=a[i];
      for(int j=i-d;j>=0;j-=d)
      {    
        if(a[j]>team)
        {
          a[j+d]=a[j];
          a[j]=team; 
        }
        else {
          break;
        }
      }
    } 
}

選擇類排序簡單選擇排序

簡單選擇排序(Selection sort)是一種簡單直觀的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到「已排序序列的末尾」。以此類推,直到所有元素均排序完畢。

實現代碼為:

public void selectSort(int[] arr) {
  for (int i = 0; i < arr.length - 1; i++) {
    int min = i; // 最小位置
    for (int j = i + 1; j < arr.length; j++) {
      if (arr[j] < arr[min]) {
        min = j; // 更換最小位置
      }
    }
    if (min != i) {
      swap(arr, i, min); // 與第i個位置進行交換
    }
  }
}
private void swap(int[] arr, int i, int j) {
  int temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

堆排序

對於堆排序,首先是建立在堆的基礎上,堆是一棵完全二叉樹,還要先認識下大根堆和小根堆,完全二叉樹中所有節點均大於(或小於)它的孩子節點,所以這裡就分為兩種情況:

如果所有節點「大於」孩子節點值,那麼這個堆叫做「大根堆」,堆的最大值在根節點。如果所有節點「小於」孩子節點值,那麼這個堆叫做「小根堆」,堆的最小值在根節點。


堆排序首先就是「建堆」,然後再是調整。對於二叉樹(數組表示),我們從下往上進行調整,從「第一個非葉子節點」開始向前調整,調整的規則如下:

建堆是一個O(n)的時間複雜度過程,建堆完成後就需要進行刪除頭排序。給定數組建堆(creatHeap):

①從第一個非葉子節點開始判斷交換下移(shiftDown),使得當前節點和子孩子能夠保持堆的性質;

②對普通節點替換可能沒問題,對如果交換打破子孩子堆結構性質,那麼就要重新下移(shiftDown)被交換的節點,一直到停止。

堆構造完成,取第一個堆頂元素為最小(最大),剩下左右孩子依然滿足堆的性質,但是缺個堆頂元素,如果把孩子調上來,可能會調動太多並且可能破壞堆結構。

①所以索性把最後一個元素放到第一位,這樣只需要判斷交換下移(shiftDown),不過需要注意此時整個堆的大小已經發生了變化,我們在邏輯上不會使用被拋棄的位置,所以在設計函數的時候需要附帶一個堆大小的參數。

②重複以上操作,一直到堆中所有元素都被取得停止。

而堆算法複雜度的分析上,之前建堆時間複雜度是O(n),而每次刪除堆頂然後需要向下交換,每個個數最壞為logn個,這樣總的時間複雜度為O(n)+O(nlogn)=O(nlogn)。

實現代碼為:

static void swap(int arr[],int m,int n)
{
  int team=arr[m];
  arr[m]=arr[n];
  arr[n]=team;
}
//下移交換 把當前節點有效變換成一個堆(小根)
static void shiftDown(int arr[],int index,int len)//0 號位置不用
{
  int leftchild=index*2+1;//左孩子
  int rightchild=index*2+2;//右孩子
  if(leftchild>=len)
    return;
  else if(rightchild<len&&arr[rightchild]<arr[index]&&arr[rightchild]<arr[leftchild])//右孩子在範圍內並且應該交換
  {
    swap(arr, index, rightchild);//交換節點值
    shiftDown(arr, rightchild, len);//可能會對孩子節點的堆有影響,向下重構
  }
  else if(arr[leftchild]<arr[index])//交換左孩子
  {
    swap(arr, index, leftchild);
    shiftDown(arr, leftchild, len);
  }
}
//將數組創建成堆
static void creatHeap(int arr[])
{
  for(int i=arr.length/2;i>=0;i--)
  {
    shiftDown(arr, i,arr.length);
  }
}
static void heapSort(int arr[])
{
  System.out.println("原始數組為         :"+Arrays.toString(arr));
  int val[]=new int[arr.length]; //臨時儲存結果
  //step1建堆
  creatHeap(arr);
  System.out.println("建堆後的序列為  :"+Arrays.toString(arr));
  //step2 進行n次取值建堆,每次取堆頂元素放到val數組中,最終結果即為一個遞增排序的序列
  for(int i=0;i<arr.length;i++)
  {
    val[i]=arr[0];//將堆頂放入結果中
    arr[0]=arr[arr.length-1-i];//刪除堆頂元素,將末尾元素放到堆頂
    shiftDown(arr, 0, arr.length-i);//將這個堆調整為合法的小根堆,注意(邏輯上的)長度有變化
  }
  //數值克隆複製
  for(int i=0;i<arr.length;i++)
  {
    arr[i]=val[i];
  }
  System.out.println("堆排序後的序列為:"+Arrays.toString(arr));

}

歸併類排序

在歸併類排序中,一般只講歸併排序,但是歸併排序也分二路歸併、多路歸併,這裡就講較多的二路歸併排序,且用遞歸方式實現。

歸併排序

歸併和快排都是「基於分治算法」的,分治算法其實應用挺多的,很多分治會用到遞歸,但事實上「分治和遞歸是兩碼事」。分治就是分而治之,可以採用遞歸實現,也可以自己遍歷實現非遞歸方式。歸併排序就是先將問題分解成代價較小的子問題,子問題再採取代價較小的合併方式完成一個排序。

歸併的思想是這樣的:

第一次:整串先進行劃分成一個一個單獨的,第一次是將序列中(1 2 3 4 5 6---)兩兩歸併成有序,歸併成(xx xx xx xx----)這樣局部有序的序列。第二次就是兩兩歸併成若干四個(1 2 3 4 5 6 7 8 ----)「每個小局部是有序的」。就這樣一直到最後這個串串只剩一個,然而這個耗費的總次數為logn。每次操作的時間複雜的又是O(n)。所以總共的時間複雜度為O(nlogn).

合併為一個O(n)的過程:

實現代碼為:

private static void mergesort(int[] array, int left, int right) {
  int mid=(left+right)/2;
  if(left<right)
  {
    mergesort(array, left, mid);
    mergesort(array, mid+1, right);
    merge(array, left,mid, right);
  }
}

private static void merge(int[] array, int l, int mid, int r) {
  int lindex=l;int rindex=mid+1;
  int team[]=new int[r-l+1];
  int teamindex=0;
  while (lindex<=mid&&rindex<=r) {//先左右比較合併
    if(array[lindex]<=array[rindex])
    {
      team[teamindex++]=array[lindex++];
    }
    else {    
      team[teamindex++]=array[rindex++];
    }
  }
  while(lindex<=mid)//當一個越界後剩餘按序列添加即可
  {
    team[teamindex++]=array[lindex++];

  }
  while(rindex<=r)
  {
    team[teamindex++]=array[rindex++];
  } 
  for(int i=0;i<teamindex;i++)
  {
    array[l+i]=team[i];
  }

}

桶類排序桶排序

桶排序是一種用空間換取時間的排序,桶排序重要的是它的思想,而不是具體實現,時間複雜度最好可能是線性O(n),桶排序不是基於比較的排序,而是一種分配式的。桶排序從字面的意思上看:

桶:每個桶有容量,桶是有一定容積的容器,所以每個桶中可能有多個元素。桶:從整體來看,整個排序更希望桶能夠更勻稱,即既不溢出(太多)又不太少。

桶排序的思想為:「將待排序的序列分到若干個桶中,每個桶內的元素再進行個別排序。」 當然桶排序選擇的方案跟具體的數據有關係,桶排序是一個比較廣泛的概念,計數排序是一種特殊的桶排序,基數排序也是建立在桶排序的基礎上。在數據分布均勻且每個桶元素趨近一個時間複雜度能達到O(n),但是如果數據範圍較大且相對集中就不太適合使用桶排序。

實現一個簡單桶排序:

import java.util.ArrayList;
import java.util.List;
//微信公眾號:bigsai
public class bucketSort {
 public static void main(String[] args) {
  int a[]= {1,8,7,44,42,46,38,34,33,17,15,16,27,28,24};
  List[] buckets=new ArrayList[5];
  for(int i=0;i<buckets.length;i++)//初始化
  {
   buckets[i]=new ArrayList<Integer>();
  }
  for(int i=0;i<a.length;i++)//將待排序序列放入對應桶中
  {
   int index=a[i]/10;//對應的桶號
   buckets[index].add(a[i]);
  }
  for(int i=0;i<buckets.length;i++)//每個桶內進行排序(使用系統自帶快排)
  {
   buckets[i].sort(null);
   for(int j=0;j<buckets[i].size();j++)//順便列印輸出
   {
    System.out.print(buckets[i].get(j)+" ");
   }
  } 
 }
}

計數排序

計數排序是一種特殊的桶排序,每個桶的大小為1,每個桶不再用List表示,而通常用一個值來計數。

「設計具體算法的時候」,先找到最小值min,再找最大值max。然後創建這個區間大小的數組,從min的位置開始計數,這樣就可以最大程度地壓縮空間,提高空間的使用效率。

簡單實現代碼為:



public static void countSort(int a[])
{
  int min=Integer.MAX_VALUE;int max=Integer.MIN_VALUE;
  for(int i=0;i<a.length;i++)//找到max和min
  {
    if(a[i]<min) 
      min=a[i];
    if(a[i]>max)
      max=a[i];
  }
  int count[]=new int[max-min+1];//對元素進行計數
  for(int i=0;i<a.length;i++)
  {
    count[a[i]-min]++;
  }
  //排序取值
  int index=0;
  for(int i=0;i<count.length;i++)
  {
    while (count[i]-->0) {
      a[index++]=i+min;//有min才是真正值
    }
  }
}

基數排序

基數排序是一種很容易理解但是比較難實現(優化)的算法。基數排序也稱為卡片排序,基數排序的原理就是多次利用計數排序(計數排序是一種特殊的桶排序),但是和前面的普通桶排序和計數排序有所區別的是,「基數排序並不是將一個整體分配到一個桶中」,而是將自身拆分成一個個組成的元素,每個元素分別順序分配放入桶中、順序收集,當從前往後或者從後往前每個位置都進行過這樣順序的分配、收集後,就獲得了一個有序的數列。

如果是數字類型排序,那麼這個桶只需要裝0-9大小的數字,但是如果是字符類型,那麼就需要注意ASCII的範圍。

所以遇到這種情況,我們基數排序思想很簡單,就拿 934,241,3366,4399這幾個數字進行基數排序的一趟過程來看,第一次會根據個位進行分配、收集:

分配和收集都是有序的,第二次會根據十位進行分配、收集,此次是在第一次個位分配、收集基礎上進行的,所以所有數字單看個位十位是有序的。

而第三次就是對百位進行分配收集,此次完成之後百位及其以下是有序的。

而最後一次進行處理的時候,千位有的數字需要補零,這次完畢後,千位及以後都有序,即整個序列排序完成。

簡單實現代碼為:

static void radixSort(int[] arr)//int 類型 從右往左
{
  List<Integer>bucket[]=new ArrayList[10];
  for(int i=0;i<10;i++)
  {
    bucket[i]=new ArrayList<Integer>();
  }
  //找到最大值
  int max=0;//假設都是正數
  for(int i=0;i<arr.length;i++)
  {
    if(arr[i]>max)
      max=arr[i];
  }
  int divideNum=1;//1 10 100 100……用來求對應位的數字
  while (max>0) {//max 和num 控制
    for(int num:arr)
    {
      bucket[(num/divideNum)%10].add(num);//分配 將對應位置的數字放到對應bucket中
    }
    divideNum*=10;
    max/=10;
    int idx=0;
    //收集 重新撿起數據
    for(List<Integer>list:bucket)
    {
      for(int num:list)
      {
        arr[idx++]=num;
      }
      list.clear();//收集完需要清空留下次繼續使用
    }
  }
}

當然,基數排序的知識點不只這些,還有字符串等長、不等長、一維數組優化等各種實現需要學習。

結語

本文把十大排序就這麼瀟灑地過了一遍,我想大家都應該有所領悟了吧!對於算法總結,避免不必要的勞動力,我分享下面這個表格給大家:

(PS:表格顯示不全,可左右滑動)

排序算法平均時間複雜度最好最壞空間複雜度穩定性冒泡排序O(n^2)O(n)O(n^2)O(1)穩定快速排序O(nlogn)O(nlogn)O(n^2)O(logn)不穩定插入排序O(n^2)O(n)O(n^2)O(1)穩定希爾排序O(n^1.3)O(n)O(nlog2n)O(1)不穩定選擇排序O(n^2)O(n^2)O(n^2)O(1)不穩定堆排序O(nlogn)O(nlogn)O(nlogn)O(1)不穩定歸併排序O(nlogn)O(nlogn)O(nlogn)O(n)穩定桶排序O(n+k)O(n+k)O(n+k)O(n+k)穩定計數排序O(n+k)O(n+k)O(n+k)O(k)穩定基數排序O(n*k)O(n*k)O(n*k)O(n+k)穩定

相關焦點

  • 程式設計師必知必會的 10 個排序算法
    ,修煉編程內功)作者:bigsai / bigsai (本文來自作者投稿)緒論身為程式設計師,十大排序是是所有合格程式設計師所必備和掌握的,並且熱門的算法比如快排、歸併排序還可能問的比較細緻,對算法性能和複雜度的掌握有要求。
  • 八十七、Python|十大排序算法系列(上篇)
    只要我在每篇博文中寫得自己體會,修煉身心;在每天的不斷重複學習中,耐住寂寞,練就真功,不畏艱難,奮勇前行,不忘初心,砥礪前行,人生定會有所收穫,不留遺憾 (作者:Runsen )作者介紹:Runsen目前大三下學期,專業化學工程與工藝,大學沉迷日語,Python, Java和一系列數據分析軟體。導致翹課嚴重,專業排名中下。.
  • JS家的排序算法 十大經典算法排序總結
    我相信以下的代碼裡一定會有某些bug或錯誤或語法不規範等問題是我自己無法發現的,所以敬請各位大神能夠指出錯誤,因為只有在不斷改錯的道路上我才能取得長久的進步。✦ ✦ ✦十大經典算法排序總結對比一張圖概括:
  • 讓程式設計師抓狂的排序算法教學視頻
    羅馬尼亞人民熱愛跳舞,即使編程課也不放過,位於Tirgu Mures地區的Sapientia大學就製作了一系列用民族舞蹈形式表現的各種計算機排序算法的工作原理,包括冒泡排序、快速排序、希爾排序、插入排序等等。
  • 十大經典排序算法之希爾排序算法
    希爾排序之前我們講過冒泡排序、選擇排序、插入排序,它們的時間複雜度都是 今天我們要講的希爾排序雖然也是插入排序的一種,但是它是插入排序的一個高效變形,脫離了 主要思想希爾排序的思想簡單點說就是有跨度的插入排序,這個跨度會逐漸變小,直到變為 1,變為 1 的時候也就是我們之前講的簡單插入排序了。規範點的描述就是,選擇一組遞減的整數作為增量序列,最小的增量必須為 1。
  • 十大經典排序算法(動圖+代碼)
    下個月又將舉行信息奧賽了,一起來複習十大經典排序算法。
  • 程式設計師必須知道的10大基礎實用算法及其講解(4)
    程式設計師必須知道的10大基礎實用算法及其講解(4) 做為程式設計師,以下著十大10大基礎實用算法及其講解是必須知道的。 作者:來源:36大數據|2015-03-06 10:10 算法四:二分查找算法
  • JavaScript中的簡單排序算法
    英文 | https://medium.com/javascript-in-plain-english/simple-sorting-algorithms-in-javascript-57d512ceaf5d排序是程式設計師處理數據處理時最常見的問題之一
  • 程式設計師為什麼要學算法?
    程式設計師對算法通常懷有複雜情感,算法很重要是共識,但是否每個程式設計師都必須學算法是主要的分歧點。
  • 排序算法問題:穩定排序與不穩定排序
    作為在面試中經常被考到的考點,每一個程式設計師都應該知道,本文總結了常見算法的穩定性,值得一看,希望能對大家有所幫助。基數排序就是這樣,先按低位排序,逐次按高位排序,低位相同的元素其順序再高位也相同時是不會改變的。另外,如果排序算法穩定,對基於比較的排序算法而言,元素交換的次數可能會少一些(個人感覺,沒有證實)。回到主題,現在分析一下常見的排序算法的穩定性,每個都給出簡單的理由。
  • 統治世界的十大排序算法!
    0.1 算法分類十種常見排序算法可以分為兩大類:比較類排序:通過比較來決定元素間的相對次序,由於其時間複雜度不能突破O(nlogn),因此也稱為非線性時間比較類排序。冒泡排序(Bubble Sort)也是一種簡單直觀的排序算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因為越小的元素會經由交換慢慢「浮」到數列的頂端。
  • 程式設計師必備的幾種常見排序算法和搜索算法總結
    本文轉載自【微信公眾號:趣談前端,ID:beautifulFront】經微信公眾號授權轉載,如需轉載與原文作者聯繫前言最近為了鞏固一下自己的算法基礎,又把算法書裡的基本算法刷了一遍, 特地總結一下前端工程師需要了解的排序算法和搜索算法知識,雖然還有很多高深算法需要了解
  • JavaScript 數據結構與算法之美 - 十大經典排序算法匯總(上)
    筆者寫的 JavaScript 數據結構與算法之美系列用的語言是 JavaScript ,旨在入門數據結構與算法和方便以後複習。文中包含了十大經典排序算法的思想、代碼實現、一些例子、複雜度分析、動畫、還有算法可視化工具。
  • 輕鬆搞定Java冒泡排序算法以及算法優化
    作為Java程式設計師,簡單的算法,必須要掌握的。尤其初級開發人員在面試過程或者筆試都會有相應算法題,今天我們講解冒泡排序算法是如何實現的以及優化方法。何為冒泡排序冒泡排序的基本思路:通過對待排序系列從前向後,依次比較相鄰元素的值,若發現逆序就交換,意思就是使較大的元素從前向後移,好比水低下的氣泡一樣逐漸向上冒泡,一個道理的。冒泡排序優缺點優點:比較簡單、空間複雜度較低、是穩定的一種排序。
  • 十大經典排序算法(下)
    (Heapsort)是指利用堆這種數據結構所設計的一種排序算法。作為一種線性時間複雜度的排序,計數排序要求輸入的數據必須是有確定範圍的整數。計數排序(Counting sort)是一種穩定的排序算法。計數排序使用一個額外的數組C,其中第i個元素是待排序數組A中值等於i的元素的個數。然後根據數組C來將A中的元素排到正確的位置。它只能對整數進行排序。
  • 十大經典排序算法最強總結
    1、冒泡排序(Bubble Sort)冒泡排序是一種簡單的排序算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果它們的順序錯誤就把它們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因為越小的元素會經由交換慢慢浮到數列的頂端。1.1 算法描述1.2 動圖演示
  • 坊間傳說的世界十大經典算法
    文章來自CSDN,原作者應該是叫July,我們引用自http://blog.csdn.net/wangkechuang/article/details/7896355以下是正文部分:如果,一定要投票選出你最看重的十大算法,你會作何選擇列?
  • 十大經典排序算法大梳理 (動圖+代碼)
    >重要部分,面試必問,系統地學習很有必要!這步做完後,最後的元素會是最大的數。持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。歸併排序是建立在歸併操作上的一種有效的排序算法。計數排序基於一個假設,待排序數列的所有數均為整數,且出現在(0,k)的區間之內。如果 k(待排數組的最大值) 過大則會引起較大的空間複雜度,一般是用來排序 0 到 100 之間的數字的最好的算法,但是它不適合按字母順序排序人名。計數排序不是比較排序,排序的速度快於任何比較排序算法。
  • 躲在被窩偷看10W字:作業系統+程式設計師必知硬核知識大全,愛了
    前言現在很普遍的一個現象就是,已經做了開發的程式設計師會有的顧慮就是:以後會有出路嗎?掙得薪資高嗎?工作好找嗎?.
  • 經典排序算法全攻略
    這次跳槽季的面試中有沒有被算法難倒呢?不久前,一位程式設計師就因為算法問題被面試官吐槽了:清華就這水平?咳,清華的水平小編不敢妄作評論,不過算法是真心重要!就連李開復老師都曾經說過:「算法遠遠比日新月異語言重要得多。算法是本質,是『萬變不離其宗』的東西」。算法不僅作為理論基礎,微軟造作系統的研發更新、谷歌搜索、百度地圖引導等都需要強大的算法在背後支撐。