# 🔎 搜尋與排序 --- - **學習目標:** - 了解搜尋與排序的基本演算法 - 學會線性搜尋與二分搜尋 - 學會冒泡排序、插入排序、快速排序 - 能進行時間複雜度分析 (Big-O) --- ## 🔖 為什麼需要搜尋與排序? - **搜尋 (Search)**:在資料集合中找出指定元素 - **排序 (Sort)**:將資料按照一定順序排列 - 這些都是演算法的基礎,許多進階資料結構(例如樹、雜湊表)都依賴它們 --- ### [線性搜尋(Linear Search)]((https://www.cs.usfca.edu/~galles/visualization/Search.html)) - 從頭到尾逐一檢查 - 適合小型或未排序資料 ```cpp int linearSearch(int arr[], int n, int target) { for(int i=0; i<n; i++) if(arr[i] == target) return i; return -1; // 沒找到 } ``` - 時間複雜度: O(n) --- ### [二分搜尋(Binary Search)](https://www.cs.usfca.edu/~galles/visualization/Search.html) - 針對 已排序陣列 - 每次將搜尋區間砍半 → 高效率 - 時間複雜度: O(log n) ```cpp int binarySearch(int arr[], int n, int target) { int left = 0, right = n-1; while(left <= right) { int mid = (left + right) / 2; if(arr[mid] == target) return mid; else if(arr[mid] < target) left = mid + 1; else right = mid - 1; } return -1; } ``` --- ## [排序演算法](https://visualgo.net/en/sorting)介紹 - 冒泡排序(Bubble Sort) - 插入排序(Insertion Sort) - 快速排序(Quick Sort) - 合併排序 (merge Sort) - Heap Sort --- ### 冒泡排序(Bubble Sort) - 每回合比較相鄰兩元素,若逆序則交換 - 時間複雜度:O(n^2) ```cpp void bubbleSort(int arr[], int n) { for(int i=0; i<n-1; i++) { for(int j=0; j<n-1-i; j++) { if(arr[j] > arr[j+1]) swap(arr[j], arr[j+1]); } } } ``` --- ### 插入排序(Insertion Sort) - 每次將新元素插入到已排序序列的正確位置 - 適合小規模或接近有序的資料 - 時間複雜度:O(n^2) ```cpp void insertionSort(int arr[], int n) { for(int i=1; i<n; i++) { int key = arr[i]; int j = i-1; while(j >= 0 && arr[j] > key) { arr[j+1] = arr[j]; j--; } arr[j+1] = key; } } ``` --- ### 快速排序(Quick Sort) - 分治法(Divide and Conquer) - 選擇基準(pivot),將陣列分為左右兩部分再遞迴排序 - 平均時間複雜度:O(n log n) - 最差情況:O(n^2) ```cpp int partition(int arr[], int low, int high) { int pivot = arr[high]; int i = low - 1; for(int j=low; j<high; j++) { if(arr[j] < pivot) { i++; swap(arr[i], arr[j]); } } swap(arr[i+1], arr[high]); return i+1; } void quickSort(int arr[], int low, int high) { if(low < high) { int pi = partition(arr, low, high); quickSort(arr, low, pi-1); quickSort(arr, pi+1, high); } } ``` --- ### 合併排序(Merge Sort) - 分治法(Divide and Conquer) - 將陣列不斷分割,再合併排序好的子陣列 - 時間複雜度:**最佳 / 平均 / 最差 = O(n log n)** - 穩定排序(不會改變相同元素的相對位置) ```cpp void merge(int arr[], int l, int m, int r) { int n1 = m - l + 1, n2 = r - m; vector<int> L(n1), R(n2); for(int i=0; i<n1; i++) L[i] = arr[l+i]; for(int j=0; j<n2; j++) R[j] = arr[m+1+j]; int i=0, j=0, k=l; while(i<n1 && j<n2) { if(L[i] <= R[j]) arr[k++] = L[i++]; else arr[k++] = R[j++]; } while(i<n1) arr[k++] = L[i++]; while(j<n2) arr[k++] = R[j++]; } void mergeSort(int arr[], int l, int r) { if(l < r) { int m = l + (r-l)/2; mergeSort(arr, l, m); mergeSort(arr, m+1, r); merge(arr, l, m, r); } } ``` --- ### 📊 時間複雜度分析 (Big-O) | 演算法 | 最佳情況 | 平均情況 | 最差情況 | | -------- |:---------- |:---------- | ---------- | | 線性搜尋 | O(1) | O(n) | O(n) | | 二分搜尋 | O(1) | O(log n) | O(log n) | | 冒泡排序 | O(n) | O(n^2) | O(n^2) | | 插入排序 | O(n) | O(n^2) | O(n^2) | | 快速排序 | O(n log n) | O(n log n) | O(n^2) | | 合併排序 | O(n log n) | O(n log n) | O(n log n) | --- ## Asymptotic Notation --- ### Θ-Notation, asymptotic tight bound ![image](https://hackmd.io/_uploads/H1oV427oxg.png) ---- - For insertion sort, the worst case is `O(n)`, best case is `linear time`. - i.e., Θ(n) is not appropriate to describe the time complexity of the insertion sort. --- ### O-Notation, asymptotic upper bound ![image](https://hackmd.io/_uploads/SyKR4hXoeg.png) ---- - O(n) best describes time required for insertion sort - What about binary search? --- ## Thinking 1. quicksort 2. mergesort --- ## 🛠️ 課堂練習 (Lab) - 練習一: - 實作一個 linearSearch 與 binarySearch,比較在 10000 筆資料下的速度差異。 --- ## 💡 本週作業 - 用 linked list 撰寫 mergesort. - Explain the time complexity of the above mergesort.
{"title":"🔎 搜尋與排序","breaks":true,"description":"C++課程教材|線性搜尋、二分搜尋、排序演算法與 Big-O 分析","contributors":"[{\"id\":\"01487228-6720-47a9-875f-2f01b5d455ad\",\"add\":1001,\"del\":554,\"latestUpdatedAt\":1759030511027}]"}
    109 views