1. 程式人生 > >【自考】排序算法-插入、交換、選擇、歸並排序

【自考】排序算法-插入、交換、選擇、歸並排序

排序算法 ans ica args sub dex 劃分 data 下標



碎碎念:

? ? ? ? ?記得當初第一年的時候、接觸算法、有那麽兩個視頻、跳舞的、講的是冒泡排序跟選擇排序、當時看了好多遍最終懂了、這次多了一些算法、學起來也還好吧、咱是有基礎的人、找到了曾經的視頻、有的就發了、沒找到的就沒法、事實上算法並不難、繞繞就明確了、先別看代碼- -


思維導圖

技術分享圖片



插入排序

從頭到尾巴、從第二個開始、向左進行插入、這裏說的插入是指作比較、直到比較出比自己小的就插入到他的前面。

樣例

? ? ? ? ?1 7 4 8 6 5

? ? ? ? ? 插入排序

? ? ? ? ?[1]7 4 8 6 5

? ? ? ? ?[1 7]?4 8 6 5? ?

??? ? ? ?[1?4?7]??8 6 5??

? ? ? ? ?[1?4?7?8 ]?6 5??

? ? ? ? ?[1?4 6?7?8 ]?5?

? ? ? ? ?[1?4?5?6?7?8?]

?特性

? ? ? ? 算法是穩定的、時間復雜度為O(n2)、空間復雜度為O(1)、須要一個輔助的空間變量

代碼(java)

<span style="font-size:18px;">// insert_sort
    publicint[] insert_sort(int[] arr) {
       int[] intArr = arr;
       int i, j, key;
       for (j = 1; j < intArr.length; j++) {
           key = intArr[j];
           i = j - 1;
           while (i >= 0 && intArr[i] > key) {
              intArr[j] = intArr[i];
              i--;             
           }
           intArr[i + 1] = key;
       }
       return intArr;
    }</span>


小編:咳咳咳、這次也自稱小編了、事實上插入排序真的沒啥- -、就是一個個往有序序列裏面插入新的數值。

視頻(插入)



交換排序

? ? ? ? 交換排序事實上就是兩個值相互比較的思想、假設後面的值比前面的小、就交換位置(從小到大排序)、交換排序一般有冒泡排序、高速排序兩種。

冒泡排序

? ? ? ? ?第一次聽說這個算法就想到了兒時家裏養的金魚、經常看它吐泡泡、(盡管不久它就不在了)、可是思想非常明顯、那就是氣泡在水裏會上浮、所謂的冒泡排序就是從頭到位、依次兩個值做比較、假設後面的值比前面小、兩個值就交換、每次都能夠交換出一個’最大值‘,就好像大泡泡上浮一樣。

樣例(相鄰的兩個數交換、每循環一次交換就能找出一個最大的)

技術分享圖片


特性:時間復雜度為O(n2)是穩定的算法、可是數據大的時候不建議使用。


代碼

<span style="font-size:18px;">void BubbleSort3(int a[], int n)
{
	int j, k;
	int flag;
	
	flag = n;
	while (flag > 0)
	{
		k = flag;
		flag = 0;
		for (j = 1; j < k; j++)
			if (a[j - 1] > a[j])
			{
				Swap(a[j - 1], a[j]);
				flag = j;
			}
	}
}</span>

視頻(冒泡)





高速排序


? ? ? ? ? ? ? 事實上是冒泡排序的一種改進、取一個鍵值、然後與其它值相比較、比鍵值大的放後面鍵值小的放前面(交換)、循環一次的效果就是比鍵值大的都在前面了、比鍵值小的都在後面了、然後再在前面那半取一個鍵值、循環上邊步驟、不斷劃分、直到排序完畢。


樣例:

技術分享圖片技術分享圖片

技術分享圖片


特性:時間復雜度為O(nlog2n)、不穩定!平均時間最佳。最壞情況近似O(n2)


代碼

<span style="font-size:18px;">#include<iostream>  
using namespace std;  
void quickSort(int a[],int,int);  
int main()  
{  
    int array[]={34,65,12,43,67,5,78,10,3,70},k;  
    int len=sizeof(array)/sizeof(int);  
    cout<<"The orginal arrayare:"<<endl;  
    for(k=0;k<len;k++)  
        cout<<array[k]<<",";  
    cout<<endl;  
    quickSort(array,0,len-1);  
    cout<<"The sorted arrayare:"<<endl;  
    for(k=0;k<len;k++)  
        cout<<array[k]<<",";  
    cout<<endl;  
    system("pause");  
    return 0;  
}  
  
void quickSort(int s[], int l, int r)  
{  
    if (l< r)  
    {        
        int i = l, j = r, x = s[l];  
        while (i < j)  
        {  
            while(i < j && s[j]>= x) // 從右向左找第一個小於x的數  
                j--;   
            if(i < j)  
                s[i++] = s[j];  
            while(i < j && s[i]< x) // 從左向右找第一個大於等於x的數  
                i++;   
            if(i < j)  
                s[j--] = s[i];  
        }  
        s[i] = x;  
        quickSort(s, l, i - 1); // 遞歸調用  
        quickSort(s, i + 1, r);  
    }  
}  </span>



視頻(高速)



選擇排序



直接選擇排序

? ? ? ? ? 這個真的簡單的不想說了= = 、有一行數、每次都選擇一個最小的出來、如圖

技術分享圖片

初始狀態 [ 8 3 2 1 7 4 6 5 ] 8 -- 1 第一次 [ 1 3 2 8 7 4 6 5 ] 3 -- 2 第二次 [ 1 2 3 8 7 4 6 5 ] 3 -- 3 第三次 [ 1 2 3 8 7 4 6 5 ] 8 -- 4 第四次 [ 1 2 3 4 7 8 6 5 ] 7 -- 5 第五次 [ 1 2 3 4 5 8 6 7 ] 8 -- 6 第六次 [ 1 2 3 4 5 6 8 7 ] 8 -- 7 第七次 [ 1 2 3 4 5 6 7 8 ] 排序完畢 代碼

<span style="font-size:18px;">//為了使用Random類,須要增加例如以下這行:
import java.util.*;
/**
 * 直接選擇排序算法的Java實現。<br/>
 *   1:從a[0]-a[N-1]中選出最小的數據,然後與a[0]交換位置<br/>
 *   2:從a[1]-a[N-1]中選出最小的數據,然後與a[1]交換位置(第1步結束後a[0]就是N個數的最小值)<br/>
 *   3:從a[2]-a[N-1]中選出最小的數據。然後與a[2]交換位置(第2步結束後a[1]就是N-1個數的最小值)<br/>
 *   以此類推。N-1次排序後,待排數據就已經依照從小到大的順序排列了。<br/>
 * 此代碼作為課件提供給學生參考,在學完數組、循環、推斷後練習。<br/>
 * @author luo_wenqiang@126點com
 * @version 1.0.0
 */
public class 直接選擇排序
{
    public static void main(String[] args) 
    {
        //聲明一個數組
        int[] array = new int[10];
        //為這個數組隨機填入整型數字
        Random random = new Random();
        for (int i = 0; i < array.length ; i++)
        {
            array[i] = random.nextInt(500);
        }
  
        System.out.print("原始數組  :");
        System.out.println(Arrays.toString(array));
        /****************************************
         以下開始正式的“直接選擇排序”算法
         直接選擇排序的關鍵:
            1:從a[0]-a[N-1]中選出最小的數據,然後與a[0]交換位置
            2:從a[1]-a[N-1]中選出最小的數據,然後與a[1]交換位置(第1步結束後a[0]就是N個數的最小值)
            3:從a[2]-a[N-1]中選出最小的數據,然後與a[2]交換位置(第2步結束後a[1]就是N-1個數的最小值)
            以此類推。N-1次排序後。待排數據就已經依照從小到大的順序排列了。

****************************************/ //N個數組元素,就須要循環N輪 for(int i = 0; i < array.length; i++){ //最小數的索引,該索引每次都依據外層循環的計數器來認為初始值。

int minIndex = i; for (int j = i; j < (array.length); j++) { //依據最小數的索引,推斷當前這個數是否小於最小數。

//假設小於。則把當前數的索引作為最小數的索引。

//否則不處理。 if(array[minIndex] > array[j]){ minIndex = j; } //直到循環完畢的時候。minIndex肯定就是當前這輪循環中。最小的那個。 } //System.out.print(i + "輪。最小數" + array[minIndex] + "。"); //System.out.print("原索引" + minIndex + ",新索引" + i); //得到最小數的索引後。把該索引相應的值放到最左邊,而且把最左邊的值放到索引所在的位置. //最左邊的值 int temp = array[i]; //把最小數索引相應的值放到最左邊 array[i] = array[minIndex]; //把原來最左邊相應的值放到最小數索引所在的位置 array[minIndex] = temp; System.out.println(String.format("%2s",(i + 1)) + "輪排序後:" + Arrays.toString(array)); } } }</span>


我次………………復制百度的代碼真不少 = =?




堆排序

簡單的了解下堆、堆分最大堆和最小堆、最大堆跟比也子節點大、最小堆相反

最大堆和最小堆

技術分享圖片

假設從小到大用堆排序、每次都把最小堆的根輸出、然後用堆的最後一層最右邊的葉子節點補上去、然後進行堆排序(從節點數除以2的根開始排序)、選出最小根、輸出、依次這樣直到堆的數都輸出完。

這裏最重點的事實上是堆排序

推薦一篇博客吧

http://blog.csdn.net/genios/article/details/8157031



歸並

二路歸並排序

把每兩個數分成一組、相比較、假設順序不正確就交換順出、循環一次之後、把兩個分組合成一個分組、這時候這個分組有四個數、然後進行排序、依此循環、最總合並成一個組、就是按從大到小排序的了、發張圖吧= =說的不太清。

技術分享圖片

代碼

package algorithm;
 
public class MergeSort {
// private static long sum = 0;
/**
 * <pre>
 * 二路歸並
 * 原理:將兩個有序表合並和一個有序表
 * </pre>
 * 
 * @param a
 * @param s
 * 第一個有序表的起始下標
 * @param m
 * 第二個有序表的起始下標
 * @param t
 * 第二個有序表的結束小標
 * 
 */
private static void merge(int[] a, int s, int m, int t) {
int[] tmp = new int[t - s + 1];
int i = s, j = m, k = 0;
while (i < m && j <= t) {
if (a[i] <= a[j]) {
tmp[k] = a[i];
k++;
i++;
} else {
tmp[k] = a[j];
j++;
k++;
}
}
while (i < m) {
tmp[k] = a[i];
i++;
k++;
}
 
while (j <= t) {
tmp[k] = a[j];
j++;
k++;
}
System.arraycopy(tmp, 0, a, s, tmp.length);
}
 
/**
 * 
 * @param a
 * @param s
 * @param len
 * 每次歸並的有序集合的長度
 */
public static void mergeSort(int[] a, int s, int len) {
int size = a.length;
int mid = size / (len << 1);
int c = size & ((len << 1) - 1);
// -------歸並到僅僅剩一個有序集合的時候結束算法-------//
if (mid == 0)
return;
// ------進行一趟歸並排序-------//
for (int i = 0; i < mid; ++i) {
s = i * 2 * len;
merge(a, s, s + len, (len << 1) + s - 1);
}
// -------將剩下的數和倒數一個有序集合歸並-------//
if (c != 0)
merge(a, size - c - 2 * len, size - c, size - 1);
// -------遞歸運行下一趟歸並排序------//
mergeSort(a, 0, 2 * len);
}
 
public static void main(String[] args) {
int[] a = new int[] { 4, 3, 6, 1, 2, 5 };
mergeSort(a, 0, 1);
for (int i = 0; i < a.length; ++i) {
System.out.print(a[i] + " ");
}
}
}



算法復雜度與特性總結

排序算法

時間

特性

插入排序(插入)

O(n2)

記錄少適用、記錄多不適用

冒泡排序(交換)

O(n2)

穩定!

高速排序(交換)

O(nlog2n)

不穩定!

平均時間最佳!

最壞情況近似

O(n2)

直接選擇(選擇)

O(n2)

簡單、easy實現、不適合N較大的情況

堆排序(選擇)

O(n log2n)

不適用排序記錄較少、適合較多記錄

有序序列合並(歸並)

O(n-h+1)

?

二路歸並排序(歸並)

O(n log2n)

N較大時、歸並排序時間性能優於堆排序、可是所需儲存量較大

?

?

?



總結:

? ? ? ? 這些算法預計以後用都是別人封裝好的、拿來直接用即可、可是用東西、要知道他的特性、數多的時候用哪個?數少的時候用哪個、什麽時候用、事實上算法也就那麽回事、不要想太復雜、考試的話、時間復雜度主要考考、空間復雜度非常少考、特性要考的、會出給一列數、讓你用算法排序、把步驟寫出來。



————————————高大上的算法————————————-


———————chenchen————————



【自考】排序算法-插入、交換、選擇、歸並排序