排序算法一:冒泡、选择、插入排序

本文详细介绍了冒泡排序、选择排序和插入排序的理论背景,以及它们的代码实现过程。重点强调了每种排序算法的时间复杂度,均为O(n^2)。
摘要由CSDN通过智能技术生成

目录

        冒泡排序

理论

代码实现

时间复杂度

选择排序

理论

代码实现

 时间复杂度

插入排序

理论分析

代码实现

时间复杂度


冒泡排序

理论

   冒泡,我们很容易联想到水煮沸,或者是鱼儿吐泡的情景,水泡会在水中上升到达水面。冒泡排序正如其名,即大的数一个个往上冒出来。过程是在一个数组中从左至右依次进行两个数的比较,大的就向前进,小的给大的挪出位置。我们用图来加深这个理解。

   如图即为一次冒泡的过程,数组nums有7个元素。可以看到第一次冒泡进行了6次比较(7-1),找出了数组中最大的元素。那我们可以由此推出:要确定7个元素的大小关系,我们要进行6次冒泡排序,每次冒泡要两两比较的次数都比上次少1(因为右边的数字位置都固定了),等最后确定了倒数第二大数字的位置时,全部元素的大小位置就被排好了,冒泡排序结束。所以我们推广一下:n个元素需要n-1次冒泡,第一次冒泡要比较n-1次,后续的冒泡次数为n-1-i(i=0,1,2.....起到累加1的作用)。所以冒泡需要两层循坏:外层控制冒泡次数,内层控制两两元素比较次数。 

代码实现

#include<stdio.h>
#include<stdlib.h>

#define L 7

//打印函数
void print(int* nums) {
	for (int i = 0; i < L; i++) {
		printf("%d ", nums[i]);
	}
	printf("\n");
}

int main() {
	int i, j;
	int* nums = (int*)malloc(sizeof(int) * L);//动态分配内存
	for (i = 0; i < L; i++) {
		scanf("%d", &nums[i]);
	}
	for (i = 0; i < 6; i++) {//外层循环控制冒泡次数
		for (j = 0; j < 6 - i; j++) {内层循环控制比较次数
			if (nums[j] > nums[j + 1]) {
				int temp = nums[j];
				nums[j] = nums[j + 1];
				nums[j + 1] = temp;
			}
		}
		print(nums);
	}
	return 0;
}

执行结果: 

  

时间复杂度

   我们看代码可以知道,双层循环,每一层循环都以n为基数进行操作,所以时间复杂度是O(n^2)。 

选择排序

理论

和冒泡排序十分相似,从过程上看冒泡是每一次冒泡在最右侧确定出一个较大的数,而选择排序是每一次选择在最左侧确定一个较小的数。换句话说,冒泡是从右到左确定,选择是从左到右确定

既然要从左到右确定数字位置,那么我们需要以最左侧未固定的数为基准,将数组进行扫描,选出数组中最小的数与基准数位置互换,然后基准数左移一位,以此类推。

如图所示,不难看出,7个元素的数组,经过6次选择(即选择出未固定的最小数)固定出了数组内的所有数位置。推广一下,n个元素的数组,经过了n-1次选择,能得出结果。而我们还不知道的是,每次选择中怎么比较呢?我们从基准数开始向后遍历数组,找出后面的最小数,再进行基准数与最小数的交换,这样来看,双层循环的步骤也就十分明了了。 

代码实现

#include<stdio.h>
#include<stdlib.h>

#define L 7

//打印函数
void print(int* nums) {
	for (int i = 0; i < L; i++) {
		printf("%d ", nums[i]);
	}
	printf("\n");
}

int main() {
	int i, j, min = 0;
	int* nums = (int*)malloc(sizeof(int) * L);//动态分配内存
	for (i = 0; i < L; i++) {
		scanf("%d", &nums[i]);
	}
	for (i = 0; i < 6; i++) {//设置基准
		min = i;//更新min(min代表最小值在数组中的索引)
		for (j = i + 1; j < L; j++) {//遍历数组后方
			if (nums[j] < nums[min]) {
				min = j;
			}
		}
        //交换基准与最小值的位置
		int temp = nums[min];
		nums[min] = nums[i];
		nums[i] = temp;
		print(nums);
	}
	return 0;
}

 时间复杂度

从代码看出双层循环每层循环仍然以n为基数,所以时间复杂度也为O(n^2)。

插入排序

理论分析

插入排序就是从前往后遍历,将小的数插在合适的位置(左边的数小于它,右边的数大于等于它)。从过程上分析,我们需要以一个数为基准,遍历它左边的数,来找到基准数合适的插入位置,然后将基准数左边的比它大的数都右移一位,给基准数挪出位置,在将基准书插入到这个位置中。所以我们要用到双指针 i 和 j 。i用来遍历数组中基准数前面的数,j用来指向基准数的位置(默然第一个数已经排好了顺序,从第二数开始)。我们用图来加深理解。

我们看图可以发现,基准值的合适位置其实就是比它大的数中最小数所在的位置。基准数挪出位置供左边比它大的那些数向右移,而反过来这些数右移又能为基准数挪出那个合适的位置。

代码实现

#include<stdio.h>
#include<stdlib.h>

#define L 7

void print(int nums[], int len)
{
	for (int i = 0; i < len; i++)
	{
		printf("%d ", nums[i]);
	}
	printf("\n");
}

void insertsort(int nums[], int len)
{
	for (int i = 1; i < len; i++)//从第二个元素开始定基准
	{
		for (int j = 0; j < i; j++)//遍历基准的前面的数
		{
			if (nums[i] < nums[j])//找到基准的合适位置(j的索引)
			{
				int temp = nums[i];//保存基准值
                //整体右移
				for (int k = i-1; k>=j; k--)
					nums[k + 1] = nums[k];
				nums[j] = temp;//将基准值插入合适的位置
			}
		}
		printArray(nums, len);
	}
}

int main()
{
	int* nums = (int*)malloc(sizeof(int) * L);//动态分配内存
	for (int i = 0; i < L; i++) {
		scanf("%d", &nums[i]);
	}
	insertsort(nums, L);
	return 0;
}

时间复杂度

插入排序若是递增序列,有n-1次插入,每次插入要比较 i 次(i是以n为基数的),所以时间复杂度为O(n^2),若是递减序列,有n-1次插入,每次插入要比较1次,然后进行i-1-k次右移操作(i和j均以n为基数),所以也是O(n^2)。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值