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



Index
簡介
簡介 - 排序演算法是什麼 ?
-
定義: 解決問題的一系列明確步驟(就像食譜)
-
排序的目標: 將一組無序的資料(數字、字串、物件)按照規則(由小到大或由大到小)排列
-
生活實例: 整理撲克牌、圖書館藏書分類
泡沫排序
選擇排序
合併 排序
合併排序
快速排序
基礎演算法
泡沫排序
泡沫排序的運作方式是重複走訪要排序的數列,一次比較兩個相鄰的元素。
- 比較相鄰的兩個元素。如果第一個比第二個大(升序排列),就交換它們。
-
對每一對相鄰元素做同樣的工作,從開始第一對到結尾的最後一對。這步完成後,最大的數會被換到最後面。
-
針對所有的元素重複以上的步驟,除了最後一個已經排好的元素。
-
持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
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
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]<<' ';
}
}插入排序
插入排序會將數列分成兩個部分:已排序部分和未排序部分。
-
從第二個元素開始:假設第一個元素已經排好了
-
取出標的:拿出未排序部分的第一個元素(稱為 key)
-
向左比較:將 key 與左側已排序部分的元素從右向左逐一比較
-
搬移與插入:如果左邊的元素比 key 大,就將該元素右移;直到找到比 key 小或等於的元素,再將 key 插入其右側空位
-
重複:直到所有元素都進入已排序部分
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),然後把數列切成兩半。
-
挑選基準 :從數列中挑選一個元素作為「基準」
-
分割 :重新排列數列。所有比基準小的元素擺在它左邊,比基準大的擺在它右邊。這個動作完成後,基準點就已經站在它最終正確的位置上
-
遞迴 :對左邊和右邊的子數列分別重複上述步驟,直到整個數列排好
快速排序
快速排序的精髓在於選定一個基準點 (Pivot),然後把數列切成兩半。
-
挑選基準 :從數列中挑選一個元素作為「基準」
-
分割 :重新排列數列。所有比基準小的元素擺在它左邊,比基準大的擺在它右邊。這個動作完成後,基準點就已經站在它最終正確的位置上
-
遞迴 :對左邊和右邊的子數列分別重複上述步驟,直到整個數列排好
快速排序
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 個元素(這時它自然就是排好序的)
-
合併 :將兩個排好序的子數列合併成一個新的、排好序的數列。這也是最關鍵的一步
合併排序
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;
}
排序
By ariel tai
排序
- 99