排序

戴瑢溱

戴瑢溱

  • 楓資 教學長
  • 學術力 : Python、C++、網頁前端..
  • 興趣 : 在凌晨4點祝別人生日快樂
  • 專長 : 和別人猜拳都輸、轉盤都轉到我、錯過公車捷運社巴

仙人掌

Index

簡介

簡介 - 排序演算法是什麼 ?

  • 定義: 解決問題的一系列明確步驟(就像食譜)

  • 排序的目標: 將一組無序的資料(數字、字串、物件)按照規則(由小到大或由大到小)排列

  • 生活實例: 整理撲克牌、圖書館藏書分類

泡沫排序

選擇排序

合併 排序

合併排序

快速排序

基礎演算法

泡沫排序

泡沫排序的運作方式是重複走訪要排序的數列,一次比較兩個相鄰的元素。

  1. 比較相鄰的兩個元素。如果第一個比第二個大(升序排列),就交換它們。
  2. 對每一對相鄰元素做同樣的工作,從開始第一對到結尾的最後一對。這步完成後,最大的數會被換到最後面

  3. 針對所有的元素重複以上的步驟,除了最後一個已經排好的元素。

  4. 持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。

1

2

3

4

5

泡沫排序

1

2

3

4

5

泡沫排序

1

2

3

4

5

泡沫排序

1

2

3

4

5

泡沫排序

1

2

3

4

5

泡沫排序

1

2

3

4

5

泡沫排序

1

2

3

4

5

泡沫排序

1

2

3

4

5

泡沫排序

1

2

3

4

5

泡沫排序

1

2

3

4

5

泡沫排序

1

2

3

4

5

泡沫排序

1

2

3

4

5

泡沫排序

泡沫排序

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

void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n-1; i++) {
        for (int j = 0; j < n-i-1; j++) {
            if (arr[j] > arr[j+1]) {
                // 交換arr[j]和arr[j+1]
                int x = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = x;
            }
        }
    }
}

int main() {
    int arr[] = {5,4,3,2,1};
    int n = 5;
    cout << "原始數列:";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    bubbleSort(arr, n);
    
    cout << "排序後數列:";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
    
    return 0;
}

選擇排序

選擇排序從未排序的數字中,找到最小(或最大)的那一個,把它放到最前面

  1. 尋找最小值:在未排序的數列中,從頭到尾掃描一遍,找到最小的元素

  2. 交換位置:將這個最小元素與未排序部分的第一個元素交換位置

  3. 邊界移動:將該元素歸入「已排序部分」,未排序部分的起始位置向後移一位

  4. 重複執行:重複上述步驟,直到所有元素都排好序

1

2

3

4

5

選擇排序

1

2

3

4

5

選擇排序

1

2

3

4

5

選擇排序

1

2

3

4

5

選擇排序

1

2

3

4

5

選擇排序

1

2

3

4

5

選擇排序

選擇排序

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

int main(){
	int a[5]={4,3,5,2,1};
	int n=5;
    for(int i=0;i<n;i++){
        int min_index=i;//紀錄初始位置
        for(int j=i+1;j<n;j++){ //不用管已經找完的左邊
            if(a[j]<a[min_index])
                min_index=j;//尋找最小值
        }
        swap(a[i],a[min_index]);
    }
    for(int i=0; i<n; i++){
    	cout<<a[i]<<' ';
    }
}

插入排序

插入排序會將數列分成兩個部分:已排序部分未排序部分

  1. 從第二個元素開始:假設第一個元素已經排好了

  2. 取出標的:拿出未排序部分的第一個元素(稱為 key)

  3. 向左比較:將 key 與左側已排序部分的元素從右向左逐一比較

  4. 搬移與插入:如果左邊的元素比 key 大,就將該元素右移;直到找到比 key 小或等於的元素,再將 key 插入其右側空位

  5. 重複:直到所有元素都進入已排序部分

1

2

3

4

5

插入排序

1

2

3

4

5

插入排序

1

2

3

4

5

插入排序

1

2

3

4

5

插入排序

1

2

3

4

5

插入排序

插入排序

插入排序

#include <iostream>
using namespace std;
int main() {
  int a[5]={3,2,5,1,4},insert;
  for(int i=1;i<5;i++){
    insert=a[i];
    for(int j=i-1;j>=0;j--){
      if(insert<a[j]){
        swap(a[j+1],a[j]);
      }
      else break;    
    }
  }
   for(int i=0;i<5;i++){
    cout << a[i] << " ";
  }
}

進階演算法

快速排序

快速排序的精髓在於選定一個基準點 (Pivot),然後把數列切成兩半。

  1. 挑選基準 :從數列中挑選一個元素作為「基準」

  2. 分割 :重新排列數列。所有比基準小的元素擺在它左邊,比基準大的擺在它右邊。這個動作完成後,基準點就已經站在它最終正確的位置上

  3. 遞迴 :對左邊和右邊的子數列分別重複上述步驟,直到整個數列排好

快速排序

快速排序的精髓在於選定一個基準點 (Pivot),然後把數列切成兩半。

  1. 挑選基準 :從數列中挑選一個元素作為「基準」

  2. 分割 :重新排列數列。所有比基準小的元素擺在它左邊,比基準大的擺在它右邊。這個動作完成後,基準點就已經站在它最終正確的位置上

  3. 遞迴 :對左邊和右邊的子數列分別重複上述步驟,直到整個數列排好

快速排序

1

2

3

4

5

1

2

3

4

5

pivot

快速排序

1

2

3

4

5

>4

<4

快速排序

快速排序

1

2

3

4

5

>4

<4

快速排序

1

2

3

4

5

>4

<4

快速排序

1

2

3

4

5

>4

<4

快速排序

1

2

3

4

先看左半邊

快速排序

先看左半邊

1

2

3

4

pivot

快速排序

先看左半邊

2

3

4

pivot

4

3

2

1

快速排序

先看左半邊

3

4

pivot

4

3

2

1

快速排序

1

2

3

4

繼續看左半邊

快速排序

1

2

3

4

pivot

左邊的元素為0或1時,停止執行

快速排序

1

2

3

4

pivot

左邊的元素為0或1時,停止執行

快速排序

1

2

3

4

5

排序完畢

尚未排序

所以再來看右邊

快速排序

4

5

pivot

左邊的元素為0或1時,停止執行

快速排序

4

5

合併

1

2

3

快速排序

#include <iostream>
using namespace std;

// 快速排序函式
void QuickSort(int arr[],int n,int result[],int& index) {
    if (n <= 0) {
        return; // 若陣列長度小於等於 0,直接返回
    }

    int pivot=arr[0]; //以第一個元素作為pivot
    int leftCount=0, rightCount=0;
    int left[n],right[n]; // 暫存左邊和右邊的元素

    for(int i=1;i<n;i++) { // 將陣列元素分配到左邊或右邊
        if(arr[i]<pivot){
            left[leftCount++]=arr[i];
        }
        else{
            right[rightCount++]=arr[i];
        }
    }

    // 遞迴排序左邊的陣列
    QuickSort(left,leftCount,result,index);
    
    // 插入pivot到結果陣列
    result[index++]=pivot;

    // 遞迴排序右邊的陣列
    QuickSort(right,rightCount,result,index);
}

int main(){
    int data[]={4,3,5,1,2};
    int n=sizeof(data)/sizeof(data[0]); //計算陣列長度

    int result[n]; //儲存排序結果
    int index=0; //結果陣列的索引

    cout<<"Before sorting:";
    for(int i=0;i<n;i++){
        cout<<data[i]<<" ";
    }
    cout<<endl;
    
    QuickSort(data,n,result,index);

    cout<<"After sorting:";
    for(int i=0;i<n;i++){
        cout<<result[i]<<" ";
    }
    cout<<endl;
    return 0;
}

合併排序

合併排序同樣採用「分而治之」 的策略,但它的邏輯與快速排序剛好相反

  1. 切割 :把目前的數列從中間切開,變成左右兩個子數列

  2. 遞迴:持續把子數列切半,直到每個子數列只剩下 1 個元素(這時它自然就是排好序的)

  3. 合併 :將兩個排好序的子數列合併成一個新的、排好序的數列。這也是最關鍵的一步

合併排序

1

2

3

4

5

6

合併排序

1

2

3

4

5

6

合併排序

1

2

3

4

5

6

合併排序

分割程式碼

void MergeSort(int Array[], int front, int end) {
    if (front < end) {
        //重複直到每個分組只剩下一個值
        int mid = front + (end - front) / 2; // 計算中間值
        MergeSort(Array, front, mid);
        MergeSort(Array, mid + 1, end);//遞迴對左右兩部分進行排序
        Merge(Array, front, mid, end);//合併到原始陣列中
    }
}

合併排序

讀取陣列程式碼

void Merge(int Array[], int front, int mid, int end) {
    // 將陣列分成兩個子陣列,並複製到 LeftSubArray 和 RightSubArray
    int n1 = mid - front + 1; // 計算左子陣列的大小
    int n2 = end - mid; // 計算右子陣列的大小
    int LeftSubArray[n1], RightSubArray[n2];
    // 將元素複製到左子陣列
    for (int i = 0; i < n1; i++)
        LeftSubArray[i] = Array[front + i];
    // 將元素複製到右子陣列
    for (int j = 0; j < n2; j++)
        RightSubArray[j] = Array[mid + 1 + j];
    }

合併排序

1

2

3

4

5

6

合併排序

1

2

3

4

5

6

合併排序

1

2

3

4

5

6

合併排序

1

2

3

4

5

6

合併排序

1

2

3

4

5

6

合併排序

1

2

3

4

5

6

合併排序

1

2

3

4

5

6

合併排序

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

// 合併兩個已排序的子陣列
void Merge(int Array[], int front, int mid, int end) {
    // 計算左右子陣列的大小
    int n1 = mid - front + 1;
    int n2 = end - mid;

    // 創建左右子陣列
    int LeftSubArray[n1], RightSubArray[n2];

    // 複製元素到左子陣列
    for (int i = 0; i < n1; i++)
        LeftSubArray[i] = Array[front + i];
    
    // 複製元素到右子陣列
    for (int j = 0; j < n2; j++)
        RightSubArray[j] = Array[mid + 1 + j];

    int i = 0, j = 0, k = front;
    // 合併左右子陣列到原始陣列中
    while (i < n1 && j < n2) {
        if (LeftSubArray[i] <= RightSubArray[j]) {
            Array[k] = LeftSubArray[i];
            i++;
        } 
        else {
            Array[k] = RightSubArray[j];
            j++;
        }
        k++;
    }

    // 將左子陣列中剩餘的元素放入原始陣列
    while (i < n1) {
        Array[k] = LeftSubArray[i];
        i++;
        k++;
    }

    // 將右子陣列中剩餘的元素放入原始陣列
    while (j < n2) {
        Array[k] = RightSubArray[j];
        j++;
        k++;
    }
}

// 遞迴排序
void MergeSort(int Array[], int front, int end) {
    if (front < end) {
        // 計算中間索引,並遞迴地對左右兩部分進行排序和合併
        int mid = front + (end - front) / 2;
        MergeSort(Array, front, mid);
        MergeSort(Array, mid + 1, end);
        Merge(Array, front, mid, end);
    }
}

int main() {
    int Array[6];
    cout << "Enter 6 elements: ";
    for (int i = 0; i < 6; ++i)
        cin >> Array[i];

    MergeSort(Array, 0, 5);

    cout << "Sorted array: ";
    for (int i = 0; i < 6; ++i)
        cout << Array[i] << " ";
    cout << endl;

    return 0;
}
Made with Slides.com