前言
本篇文章分析了选择排序法、冒泡排序法和插入排序法的逻辑,给出了C语言实现代码及其复杂度分析。参考自书籍 Fundamentals of Data Structures In C。本篇文章代码均已测试通过,欢迎大家批评指正🙏
引入
在正式讲解排序法之前,我们先来对已排好序的数组搭建一个最基础的排序过程:首先搜索整个数组,比较相邻元素、如果左边的值大于右边的值,则相邻元素交换,遍历上述过程直至相邻元素总保持左边小于右边,实现过程如下:
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#define MAX_SIZE 101 // 初始化数组的最大长度
#define SWAP(x, y, t) ((t) = (x), (x) = (y), (y) = (t)) /* 宏定义可适用于任何数据类型,函数定义法见swap */
#define COMPARE(x, y) (((x) < (y)) ? -1 :((x) == (y)) ? 0 : 1) /* the other way is func: compare() */
void swap(int *x, int *y); /* change the location of two values */
int compare(int x, int y ); /* 判断两数关系 */
void sort(int [], int); /* 交换两数位置 */
void main()
{
int i, n, a, b, num;
int list[MAX_SIZE];
printf("Enter the number of numbers to generate: ");
scanf("%d", &n);
if(n < 1 || n > MAX_SIZE)
{
fprintf(stderr, "Improper value of n\n"); // write the data into the file
exit(1);
}
/* randomly generate numbers */
for(i = 0; i < n; i++)
{
list[i] = rand() % 1000;
printf("%d ", list[i]);
}
sort(list, n);
printf("\n Sorted array: \n");
for(i = 0; i < n; i++)
printf("%d ", list[i]);
printf("\n");
}
/* Actually it's a selection sorting */
void sort(int list[], int n)
{
int i, j, min, temp;
for(i = 0; i < n-1; i++)
{
min = i;
for(j = i+1; j < n; j++)
if(list[j] < list[min])
min = j;
SWAP(list[i], list[min], temp);
}
}
void swap(int *x, int *y)
{
int temp = *x;
*x = *y; // *x指向存储y所指向的地址
*y = temp;
}
/* compare x and y, return -1 for less than, 0 for equal, 1 for greater */
int compare(int x, int y)
{
if(x < y) return -1;
else if(x == y) return 0;
else return 1;
}
输出为:
一、选择排序法
选择排序算法(selection sort algorithm)基本思想:搜索整个列表,找到最小项的位置;如果该位置不是列表的第一个位置(即索引值为0),就会交换这两位置的值。(这实际上就是我们在引入中搭建的排序算法)
在使用选择排序法将数组元素按升序排序后,我们还可以查找某个元素是否在该数组中,顺序查找是最简单的思路却不是最佳的方法,在此程序中,我们使用二分查找将复杂度从O(n)降低至O(lgn)。
💻代码实现:
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#define MAX_SIZE 101
#define SWAP(x, y, t) ((t) = (x), (x) = (y), (y) = (t)) /* 宏定义可适用于任何数据类型,函数定义法见swap */
#define COMPARE(x, y) (((x) < (y)) ? -1 :((x) == (y)) ? 0 : 1) /* the other way is func: compare() */
void swap(int *x, int *y); /* change the location of two values */
int compare(int x, int y ); /* 判断两数关系 */
void sort(int [], int); /* selection sort */
int binarysearch(int list[], int num, int left, int right); /* 折半查找法基本思路 */
void main()
{
int i, n, a, b, num;
int list[MAX_SIZE];
printf("Enter the number of numbers to generate: ");
scanf("%d", &n);
if(n < 1 || n > MAX_SIZE)
{
fprintf(stderr, "Improper value of n\n"); // write the data into the file
exit(1);
}
/* randomly generate numbers */
printf("The target list: /n");
for(i = 0; i < n; i++)
{
list[i] = rand() % 1000;
printf("%d ", list[i]);
}
sort(list, n);
printf("\n Sorted array: \n");
for(i = 0; i < n; i++)
printf("%d ", list[i]);
printf("\n");
printf("Type the expected value: ");
scanf("%d", &num);
a = binarysearch(list, num, 0, n-1);
printf("Is 【%d】 in this random array?(【1】-> YES, 【-1】-> NO, 0 -> right in the middle)\n", num);
printf("The answer is 【%d】\n", a);
}
/* 取已排序好的数组内0~n-1的数查找,先取该范围内中间的数进行判断,若该数小于则右边界减一,若大于则左边界加一 */
int binarysearch(int list[], int num, int left, int right)
{
int middle;
while(left <= right)
{
middle = (right + left) / 2;
switch (COMPARE(list[middle], num))
{
case -1: left = middle + 1;
break;
case 0: return middle;
case 1: right = middle - 1;
}
}
return -1;
}
void sort(int list[], int n)
{
int i, j, min, temp;
for(i = 0; i < n-1; i++)
{
min = i;
for(j = i+1; j < n; j++)
if(list[j] < list[min])
min = j;
SWAP(list[i], list[min], temp);
}
}
void swap(int *x, int *y)
{
int temp = *x;
*x = *y; // *x指向存储y所指向的地址
*y = temp;
}
/* compare x and y, return -1 for less than, 0 for equal, 1 for greater */
int compare(int x, int y)
{
if(x < y) return -1;
else if(x == y) return 0;
else return 1;
}
👀输出如下:
💻折半查找的递归代码实现以及复杂度分析:
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<time.h>
#define MAX_SIZE 101
#define SWAP(x, y, t) ((t) = (x), (x) = (y), (y) = (t)) /* 宏定义可适用于任何数据类型,函数定义法见swap */
#define COMPARE(x, y) (((x) < (y)) ? -1 :((x) == (y)) ? 0 : 1) /* the other way is func: compare() */
int binarysearch(int list[], int num, int left, int right); /* 折半查找法基本思路 */
int binsearch(int list[], int num, int left, int right); /* 递归的折半查找法 */
void sort(int [], int); /* selection sort */
void swap(int *x, int *y); /* change the location of two values */
int binarysearch(int list[], int num, int left, int right); /* 折半查找法 */
int compare(int x, int y ); /* 判断两数关系 */
void main(void)
{
int i, n, a, b, num;
int list[MAX_SIZE];
clock_t start, stop1, stop2;
double duration;
printf("Enter the number of numbers to generate: ");
scanf("%d", &n);
if(n < 1 || n > MAX_SIZE)
{
fprintf(stderr, "Improper value of n\n"); // write the data into the file
exit(1);
}
/* randomly generate numbers */
printf("The target list: \n");
for(i = 0; i < n; i++)
{
list[i] = rand() % 1000;
printf("%d ", list[i]);
}
// 计时开始
start = clock();
// 排序
sort(list, n);
printf("\n Sorted array: \n");
for(i = 0; i < n; i++)
printf("%d ", list[i]);
printf("\n");
// 查找
printf("Type the expected value: ");
scanf("%d", &num);
printf("=============未使用递归==============\n");
a = binarysearch(list, num, 0, n-1);
// 非递归的二分查找计时结束
stop1 = clock();
printf("Is 【%d】 in this random array?(【>0】-> YES, 【-1】-> NO, 0 -> right in the middle)\n", num);
if (num <= 0) {
printf("The answer is 【%d】\n", a);
}
else {
printf("YES. It's at the position【%d】\n", a);
}
printf("=============使用递归===============\n");
b = binsearch(list, num, 0, n-1);
// 递归的二分查找计时结束
stop2 = clock();
printf("Is 【%d】 in this random array?(【>0】-> YES, 【-1】-> NO, 0 -> right in the middle)\n", num);
if (num <= 0) {
printf("The answer is 【%d】\n", b);
}
else {
printf("YES. It's at the position【%d】\n", b);
}
duration = ((double) (stop1-start)) / CLOCKS_PER_SEC;
printf("不使用递归的折半查找消耗时间:%f\n" ,duration);
duration = ((double) (stop2-stop1+start)) / CLOCKS_PER_SEC;
printf("使用递归的折半查找消耗时间:%f\n" ,duration);
}
void sort(int list[], int n)
{
int i, j, min, temp;
for(i = 0; i < n-1; i++)
{
min = i;
for(j = i+1; j < n; j++)
if(list[j] < list[min])
min = j;
SWAP(list[i], list[min], temp);
}
}
void swap(int *x, int *y)
{
int temp = *x;
*x = *y; // *x指向存储y所指向的地址
*y = temp;
}
/* compare x and y, return -1 for less than, 0 for equal, 1 for greater */
int compare(int x, int y)
{
if(x < y) return -1;
else if(x == y) return 0;
else return 1;
}
/* 取已排序好的数组内0~n-1的数查找,先取该范围内中间的数进行判断,若该数小于则右边界减一,若大于则左边界加一 */
int binarysearch(int list[], int num, int left, int right)
{
// 渐进复杂度为O(lgn)
int middle;
while(left <= right)
{
middle = (right + left) / 2;
switch (COMPARE(list[middle], num))
{
case -1: left = middle + 1;
break;
case 0: return middle;
case 1: right = middle - 1;
}
}
return -1;
}
/* 递归的折半查找,返回调用直至逼近值然后返回,若找到则返回1,未找到则返回-1 */
int binsearch(int list[], int num, int left, int right)
{
// 渐进复杂度为O(n^2)
int middle;
if(left <= right)
{
middle = (left + right) / 2;
switch (COMPARE(list[middle], num))
{
case -1: return
binsearch(list, num, middle+1, right);
case 0: return middle;
case 1: return
binsearch(list, num, left, middle-1);
}
}
return -1;
}
👀输出如下:
二、冒泡排序法
冒泡排序(bubble sort):从列表索引为0处开始,并比较每一对数据项,直到移动到列表的末尾。每当成堆的两项之间的顺序不正确时(左大右小),就会交换位置,整体上表现为将最大的项移动到列表末尾。
💻代码实现:
/* 冒泡排序法 */
#include<stdio.h>
int main()
{
int data[5];
printf("请输入五个数组元素:\n");
int i,j;
for(i = 0;i < 5;i ++)
{
scanf("%d",&data[i]);
}
for(i = 0;i < 5;i ++)
{
for(j = 0;j < 5 - 1 - i;j++)
{
if(data[j] > data[j+1]) // 这里默认采用升序来排列
{
int temp;
temp = data[j];
data[j] = data[j+1];
data[j+1] = temp;
}
}
}
printf("排序好的数组为:\n");
for(i = 0;i < 5;i ++)
{
printf("%d ",data[i]);
}
return 0;
}
// 冒泡排序法的渐进复杂度为O(n^2)
👀输出为:
三、插入排序法
插入排序(Insertion sort):在第i轮通过列表时,第i个项应该插入到列表的前i个项之中的正确排序位置。
💻代码实现:
/* 插入排序法 */
#include<stdio.h>
// 打印结果的函数
void print(int data[],int n)
{
int i;
for(i=0;i<n;i++)
{
printf("%d ",data[i]);
}
printf("\n");
}
// 选择排序
void insertSort( int data[] ,int n )
{
int i,j;
int t;
for(i=1;i<n;i++)
{
t = data[i];
j = i -1;
while(j>=0 && data[j]>t)
{
data[j+1]=data[j];
j--;
}
data[j+1] = t;
print(data,n); //显示排序的过程
}
}
int main()
{
// 原数组
int data[8]={5, 0, 2, 1, 3, 6, 7, 8};
// 应用插入排序法后的数组
insertSort(data ,8);
return 0;
}
// 插入排序法的复杂度为O(n^2)
👀输出为: