【算法入门】冒泡、选择、插入、希尔、快速排序 + 查找 C语言总结
今日学习:冒泡排序、选择排序、插入排序、希尔排序、快速排序,顺序查找、二分查找。
🔍文章大纲
- 1 冒泡排序
- 2 选择排序
- 3 直接插入排序
- 4 希尔排序
- 5 快速排序
- 6 查找算法(顺序查找、二分查找)
- 7 排序算法对比表
- 8 学习总结
1. 冒泡排序
原理:相邻元素两两比较,大的往后交换,每轮把最大值"冒泡"到数组尾部;增加交换标记做优化,提前结束有序数组。
复杂度 :最好O(n),最坏/平均O(n²);空间O(1);稳定排序
#include <stdio.h>
// 冒泡排序-优化版
void bubbleSort(int arr[], int n)
{
for(int i = 0; i < n - 1; i++)
{
int swapFlag = 0; //标记本轮是否发生交换
for(int j = 0; j < n - 1 - i; j++)
{
if(arr[j] > arr[j+1])
{
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
swapFlag = 1;
}
}
if(swapFlag == 0) break; //没有交换,数组已有序,直接退出
}
}
2. 选择排序
原理:划分有序区、无序区;每一轮在无序区找到最小值下标,和无序区第一个元素交换。
复杂度 :全部O(n²);空间O(1);不稳定排序
void selectSort(int arr[], int n)
{
for(int i = 0; i < n - 1; i++)
{
int minIdx = i;
//找无序区间最小值下标
for(int j = i + 1; j < n; j++)
{
if(arr[j] < arr[minIdx])
{
minIdx = j;
}
}
//交换
int temp = arr[i];
arr[i] = arr[minIdx];
arr[minIdx] = temp;
}
}
3. 直接插入排序
原理:把数组分为有序区、无序区;依次取出无序区元素,向前插入到有序区合适位置,类似整理扑克牌。
复杂度 :最好O(n),最坏/平均O(n²);空间O(1);稳定排序
void insertSort(int arr[], int n)
{
for(int i = 1; i < n; i++)
{
int temp = arr[i]; //待插入元素
int j;
//有序区元素向后挪
for(j = i - 1; j >= 0 && arr[j] > temp; j--)
{
arr[j+1] = arr[j];
}
arr[j+1] = temp;
}
}
4. 希尔排序(插入排序优化)
原理:分组插入排序。设置增量gap,按gap分组组内插入排序;不断缩小gap,gap=1就是普通插入排序。
复杂度 :平均O(nlogn),最坏O(n²);空间O(1);不稳定排序
void shellSort(int arr[], int n)
{
int gap = n / 2;
while(gap > 0)
{
//分组插入排序
for(int i = gap; i < n; i++)
{
int temp = arr[i];
int j;
for(j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
{
arr[j + gap] = arr[j];
}
arr[j + gap] = temp;
}
gap = gap / 2;
}
}
5. 快速排序(分治思想)
原理:选基准pivot,将数组划分:小于基准放左边,大于基准放右边;递归处理左右子区间。
复杂度 :平均O(nlogn),最坏O(n²);空间O(logn)递归栈;不稳定排序
//分区函数
int partition(int arr[], int left, int right)
{
int pivot = arr[left];
while(left < right)
{
while(left < right && arr[right] >= pivot) right--;
arr[left] = arr[right];
while(left < right && arr[left] <= pivot) left++;
arr[right] = arr[left];
}
arr[left] = pivot;
return left;
}
void quickSort(int arr[], int left, int right)
{
if(left >= right) return;
int mid = partition(arr, left, right);
quickSort(arr, left, mid - 1);
quickSort(arr, mid + 1, right);
}
6. 查找算法
6.1 顺序查找(线性查找)
数组不需要有序,逐个遍历对比
//找到返回下标,找不到返回-1
int seqSearch(int arr[], int n, int key)
{
for(int i = 0; i < n; i++)
{
if(arr[i] == key)
return i;
}
return -1;
}
6.2 二分查找(折半查找)
⚠️数组必须有序,每次折半缩小查找范围;时间复杂度O(logn)
//二分查找,有序数组,找到返回下标,找不到返回-1
int binarySearch(int arr[], int n, int key)
{
int left = 0;
int right = n - 1;
while(left <= right)
{
int mid = left + (right - left)/2; //防止溢出,等价于(left+right)/2
if(arr[mid] == key)
{
return mid;
}
else if(arr[mid] > key)
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
return -1;
}
7. 排序算法对比表
| 排序算法 | 平均时间 | 最坏时间 | 空间 | 稳定性 | 适用场景 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n²) | O(n²) | O(1) | 稳定 | 少量数据、基本有序 |
| 选择排序 | O(n²) | O(n²) | O(1) | 不稳定 | 少量数据 |
| 直接插入排序 | O(n²) | O(n²) | O(1) | 稳定 | 数据量小,大部分有序 |
| 希尔排序 | O(nlogn) | O(n²) | O(1) | 不稳定 | 中等规模数据 |
| 快速排序 | O(nlogn) | O(n²) | O(logn) | 不稳定 | 大数据,最常用 |
8. 学习总结
今天学习了5种经典排序与两种基础查找算法:
- 冒泡、选择、插入属于简单排序,时间复杂度O(n²),适合小规模数据,用来理解排序思想。
- 希尔排序是插入排序的优化,通过分组降低移动次数。
- 快速排序基于分治,实际工程使用最多,但要注意最坏情况。
- 查找:顺序查找不要求有序;二分查找效率很高,但前提必须数组有序。
- 稳定性概念:相等元素排序后相对位置不变就是稳定排序。
完整测试main函数(直接复制运行)
//测试main
int main()
{
int arr[] = {8,3,6,2,9,1,5};
int n = sizeof(arr)/sizeof(arr[0]);
//bubbleSort(arr,n);
//selectSort(arr,n);
//insertSort(arr,n);
//shellSort(arr,n);
//quickSort(arr,0,n-1);
for(int i=0;i<n;i++){
printf("%d ",arr[i]);
}
int pos = binarySearch(arr,n,6);
printf("\n查找6的下标:%d\n",pos);
return 0;
}