排序算法
冒泡排序
冒泡排序的工作原理
- 冒泡排序的核心思想是逐步将未排序部分中的最大(或最小)元素移动到数组的一端。
- 在每一轮比较中,相邻的元素两两进行比较,如果顺序不正确(如当前元素比下一个元素大),则交换它们。
- 经过一轮比较,最大的元素会被移动到未排序部分的末尾,接下来对剩余未排序的部分重复这一过程。
冒泡排序的代码实现
//冒泡排序
void bubble(int arr[],int size){
int i,j;
for(i = size - 1;i > 0;i--){
for(j = 0;j < i;j++){
//交换
if(arr[j] > arr[j+1]){
int tmp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tmp;
}
}
}
}
代码说明
-
外层循环:
- 控制排序的轮数。每一轮会将当前未排序部分中的最大元素移动到末尾。
-
内层循环:
- 两两比较相邻的元素,并根据需要进行交换。(通过 size - 1 将外层循环轮数和内层循环论述联系起来)
插入排序
插入排序的工作原理
- 插入排序将数组分为已排序和未排序两部分。
- 初始时,已排序部分只有第一个元素,剩余的元素在未排序部分。
- 然后依次将未排序部分的元素插入到已排序部分的适当位置,以此构建出整个有序序列。
插入排序的代码实现
//插入排序
void insert(int arr[],int size){
int i,j;
int key;//存储要比较的数
//从第二个数开始比较
for(i = 1;i < size;i++){
key = arr[i];
for(j = i-1;j >= 0;j--){
//如果前面的元素比key大,前面的元素后移
if(arr[j] > key){
arr[j+1] = arr[j];
}else{
break;
}
}
//当循环结束后,j+1即为key要插入的位置
arr[j+1] = key;
}
}
代码说明
-
外层循环:
- 从第二个元素开始,逐个元素进行插入操作。
i
是当前未排序部分的第一个元素。
- 从第二个元素开始,逐个元素进行插入操作。
-
内层循环:
- 比较当前元素
key
与已排序部分的元素arr[j]
,如果已排序部分的元素大于key
,则将arr[j]
向右移动,为key
腾出插入位置。
- 比较当前元素
-
插入操作:
- 内层循环结束后,将
key
插入到正确的位置arr[j + 1]
。
- 内层循环结束后,将
选择排序
选择排序的工作原理
- 初始状态:数组分为已排序部分和未排序部分。初始时,已排序部分为空,整个数组都属于未排序部分。
- 每一轮操作:从未排序部分中找到最小的元素,并将其与未排序部分的第一个元素交换位置。此时,已排序部分的长度增加一,未排序部分的长度减少一。
- 重复操作:不断重复上述操作,直到未排序部分为空,整个数组就变得有序。
选择排序的代码实现
//选择排序
void select(int arr[], int size){
int i,j;
int min;
for(i = 0;i < size - 1;i++){
min = i;// 假设当前元素为最小值,记录当前元素下标
for(j = i + 1;j < size;j++){
if(arr[min] > arr[j]){
min = j;// 找到更小的元素,更新最小值的索引
}
}
// 将找到的最小元素与当前元素交换
int tmp = arr[i];
arr[i] = arr[min];
arr[min] = tmp;
}
}
代码说明
-
外层循环 (
for (i = 0; i < size - 1; i++)
):- 这个循环控制着每一轮的选择排序。每一轮确定一个元素的最终位置,也就是说,每一轮都会将当前未排序部分中的最小元素放置到已排序部分的末尾。
i
的范围是从0
到size - 2
。这是因为当只剩下一个元素时,它已经是未排序部分中最小的,无需再进行排序。
-
初始化最小值索引 (
min = i
):- 在每一轮开始时,假设当前未排序部分的第一个元素(即
arr[i]
)是最小值。因此,min
被初始化为i
。
- 在每一轮开始时,假设当前未排序部分的第一个元素(即
-
内层循环 (
for (j = i + 1; j < size; j++)
):- 这个循环用于遍历当前未排序部分的其他元素,找到实际的最小值。
- 从
j = i + 1
开始,比较arr[j]
和arr[min]
。如果arr[j]
小于arr[min]
,那么arr[j]
是新的最小值,并更新min
为j
。
-
交换元素 (
int tmp = arr[i]; arr[i] = arr[min]; arr[min] = tmp;
):- 在内层循环结束后,
min
索引处保存的是当前未排序部分的最小元素。 - 将这个最小元素与当前未排序部分的第一个元素(即
arr[i]
)进行交换,从而将该最小元素放到正确的位置。
- 在内层循环结束后,
快速排序
快速排序的工作原理
-
选择基准:
- 从数组中选择一个元素作为基准。通常可以选择第一个元素、最后一个元素、中间的元素,或者随机选择。
-
划分操作:
- 将数组分成两个部分:一部分包含小于或等于基准的元素,另一部分包含大于或等于基准的元素。此时,基准元素位于其最终位置上。
-
递归排序:
- 递归地对划分后的两部分进行快速排序。
快速排序的代码实现(包含main.c)
//快速排序
void quick(int arr[],int start,int end){
int i = start;//记录左下标
int j = end;//记录右下标
int mid = arr[start];// 选择基准元素
int tmp;//临时变量用于交换元素
while(i < j){ //i和j相等时把元素分成大于和小于基准元素的两部分
//从头往后找,比基准小就继续
while(arr[i] < mid){
i++;
}
//循环结束,i的位置大于等于基准元素
//从后往前找,比基准大就继续
while(arr[j] > mid){
j--;
}
//循环结束,j的位置小于等于基准元素
//交换位置
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
if(start < j){
quick(arr,start,j-1);
}
if(end > j){
quick(arr,j+1,end);
}
}
int main(){
int num[] = {40,20,40,60,80,10,50,90,30};
//bubble(num,9);
//insert(num,9);
//select(num,9);
quick(num,0,8);
for(int i = 0; i < 9;i++){
printf("%d ",num[i]);
}
printf("\n");
return 0;
}
代码说明
请看代码注释。
值得改进的地方
处理相等元素时可能出现死循环:
- 如果数组中存在与基准元素相同的多个元素,
i
和j
会出现在相同位置停留的情况(例如给给定一个数字序列为{2,3,2},),从而导致死循环或排序不正确。
查找算法
顺序查找
顺序查找的工作原理
- 初始化:从数据结构的第一个元素开始。
- 逐个比较:将每个元素与要查找的目标元素进行比较。
- 找到目标:如果找到目标元素,则返回该元素的位置。
- 遍历结束:如果遍历结束仍未找到目标元素,则返回一个表示未找到的标志(通常是
-1
)。
顺序查找的代码实现
//顺序查找
int line_find(int arr[],int size,int data){
//依次遍历
for(int i = 0;i < size;i++){
if(arr[i] == data){
return i;
}
}
return -1;
}
折半查找(适用于有序队列)
折半查找的工作原理
- 初始条件:数组必须是有序的(升序或降序均可)。
- 确定中间元素:每次查找时,将数组的中间元素与目标元素进行比较。
- 缩小范围:
- 如果中间元素等于目标元素,则查找成功,返回中间元素的索引。
- 如果中间元素大于目标元素,则目标元素只可能在左半部分,接下来只需在左半部分继续查找。
- 如果中间元素小于目标元素,则目标元素只可能在右半部分,接下来只需在右半部分继续查找。
- 递归或迭代:重复上述步骤,直到找到目标元素或查找范围为空
折半查找的代码实现(包含main.c)
//查找算法
#include<stdio.h>
//顺序查找
int line_find(int arr[],int size,int data){
//依次遍历
for(int i = 0;i < size;i++){
if(arr[i] == data){
return i;
}
}
return -1;
}
//折半查找
int half_find(int arr[],int size,int data){
int left = 0;//查找范围的左边界
int right = size - 1;//查找范围的右边界
while(left <= right){
int mid = (left + right) / 2;
if(data > arr[mid]){
//向右找
left = mid + 1;
}else if(data < arr[mid]){
//向左找
right = mid -1;
}else{
//找到了
return mid;
}
}
//没找到
return -1;
}
int main(){
int num[] = {1,2,3,4,5,6,7,8,9};
//int res = line_find(num,7,9);
int res = half_find(num,10,8);
printf("下标是:%d\n",res);
return 0;
}