冒泡排序

1.序言


其实早在几年前,我就了解了冒泡排序的原理,但心中一直存在以下两个疑问:

1.如何将冒泡排序简单的两行原理转化为代码?

2.冒泡排序的代码如何写才算是优秀的?

看过很多冒泡排序的书籍和博客,有很多给出了示例,步骤,代码,有的甚至进行了时间复杂度的分析,但那终究不是自己的东西,虽说,现在依然有些问题解释的不太清楚,但我想尝试一下解释清楚,望大神们多多指点。


2.问题描述


输入:n个数的序列<a1, a2, a3, ... , an>。

输出:原序列的一个重排<a1*, a2*, a3*, ... , an*>,使得a1*<=a2*<=a3*<=...<=an*。


3.问题分析


每一次从数列中选取最大(/最小)的数,然后把它放在最后(/最前)面,重复执行此操作,直到所有数据有序。

4.问题解决前提条件


a.此处我们选择使用标准C语言。

b.此处我们选择原理为:每一次从数列中选取最大的数,然后把它放在最后面,重复执行此操作,直到所有数据有序。当然,后面我会给出每一次从数列中选取最小的数,然后把它放在最前面,重复执行此操作,直到所有数据有序。

c.此处假设我们需要排序的n个数均为整型。


5.问题解决步骤:


a.解决n个数的序列的存储问题:

首先,我们要思考如何表示n个数的序列?标准C里边有数组或指针;

其次,如何选择数组或指针?我选择指针,因为指针可以表示任意长度的序列;

再者,如果选择指针,如何确定指针所代表的数据长度?此问题,我查找了一些文章,没有发现好的办法,所以我决定,再传入指针的同时,传入数据长度;

最后,序列用 int *p表示,长度用 int size表示。

b.解决每一次的问题:

首先,每一次代表啥?我思考很长时间,应该是代表本次需要选取最大数的数列范围;

其次,如何表示本次的数列范围?结合5-a解决了数列存储的问题,本次的数列范围可以通过指针指向的位置表示;

最后,考虑选取最大数,每次需把最大数交换到数列最后,所以设置指针位置 int i 从(size - 1)---> 0 变化。

c.解决从数列中选取最大数问题:

首先,如何选取最大数?当然是通过两两比较得到较大的那一个,再用较大的那一个与下一个数相比,直到所有的数都比较过一遍,就能得到最大数。

其次,两两比较的比较值是什么?应该是5-b中每一次的数列范围中的每一个数,结合5-a数列的存储,并且由于是寻找最大数,所以设置指针位置int j 从 (0 ---> i)(是否包含临界点,具体情况具体分析),代表比较值。

再者,两两比较的被比较值(即每次比较完的较大值)是什么?目前考虑有三种实现方式,分别如下:

1)选取固定位置本次选取为*(p + i),两两比较后,将较大的值换到位置i。

2)记录当前比较获得的最大值位置,用int max表示。

3)将比较值相邻的位置作为被比较值,将最大值向后交换即比较值为位置j,被比较值为位置j + 1。 

d.解决两个数据交换的问题:

此问题属于C语言基础,稍后会直接给出代码。

至此,我们已经将问题分析的原理尽数转化成了代码的逻辑,下面我们将给出代码,经过我们以上的讨论,只有在解决5-c时,有三种方案,因此,我们会在下一部分,分两部分给出代码,通用的与特殊的。


6.代码


a.通用代码片段:

#include<stdio.h>

void bubbleSort(int *p, int size);
void swap(int *a, int *b);
void printfArray(int *p, int size);

int main() {
	
	int a[8] = {49,38,65,97,76,13,27,30};
	int size = sizeof(a) / sizeof(a[0]);
	printfArray(&a[0], size);

	bubbleSort(&a[0], size);


	return 0;
}

void bubbleSort(int *p, int size) {
}

void swap(int *a, int *b) {
	int c = *a;
	*a = *b;
	*b = c;
}

void printfArray(int *p, int size) {
	for (int i = 0; i < size; ++i)
	{
		printf("%d ", *(p + i));
	}
	printf("\n");
}
b.特殊代码片段:

1)代码片段:

//选取固定位置本次选取为*(p + i),两两比较后,将较大的值换到位置i
void bubbleSort(int *p, int size) {
	//i代表每次寻找数列中最大值的边界
	for (int i = size - 1; i > 0; --i)
	{
		for (int j = 0; j < i; ++j)
		{
			if (*(p + j) > *(p + i))
			{
				swap((p + j), (p + i));
			}
		}
		
		printfArray(p, size);
	}
}
运行结果:

2)代码片段:

//记录当前比较获得的最大值位置,用int max表示
void bubbleSort(int *p, int size) {
	//i代表每次寻找数列中最大值的边界
	for (int i = size - 1; i > 0; --i)
	{
		int max = i;
		for (int j = 0; j < i; ++j)
		{
			if (*(p + j) > *(p + max))
			{
				max = j;
			}

		}
		swap((p + max), (p + i));
		
		printfArray(p, size);
	}
}
运行结果:

3)代码片段:

//将比较值相邻的位置作为被比较值,将最大值向后交换即比较值为位置j,被比较值为位置j + 1
void bubbleSort(int *p, int size) {
	//i代表每次寻找数列中最大值的边界
	for (int i = size - 1; i > 0; --i)
	{
		//j代表本次寻找数列最大值的游标值
		for (int j = 0; j < i; ++j)
		{
			if (*(p + j) > *(p + j + 1))
			{
				swap((p + j), (p + j + 1));
			}
		}
		
		printfArray(p, size);
	}

}
运行结果:


7.优劣分析(此部分纯属个人观点)


a. 1) 存在不足之处,就是本来位于后面的较大数被交换到前面。

示例: 49 38 65 97 76 13 27 80

第一次:49 38 65 80 76 13 27 97

b. 2)是1)的一种优化,减少了交换的次数。

c. 3)就解决了1)中存在的不足。


8.优化策略


如果有一个无序数列{2, 1, 3, 4, 5, 6, 7, 8, 9, 10},从第一次循环交换后的操作,可以说都是没必要的。

造成没必要的操作主要原因是后面8个数的顺序是有序的,所以,我们可以设置一个标记变量,标记数列中的数是否在循环结束前就已经有序了。

代码片段如下:

//将比较值相邻的位置作为被比较值,将最大值向后交换即比较值为位置j,被比较值为位置j + 1
void bubbleSort(int *p, int size) {
	int flag = 1;
	//i代表每次寻找数列中最大值的边界
	for (int i = size - 1; i > 0 && flag; --i)
	{
		flag = 0;
		//j代表本次寻找数列最大值的游标值
		for (int j = 0; j < i; ++j)
		{
			if (*(p + j) > *(p + j + 1))
			{
				swap((p + j), (p + j + 1));
				flag = 1;
			}
		}
		
		printfArray(p, size);
	}

}

优化前结果:

优化后结果:


9.时间复杂度和空间复杂度


参见http://blog.csdn.net/yuzhihui_no1/article/details/44339711 分析。


10.参考


a.http://blog.csdn.net/cbs612537/article/details/8294960

b.http://blog.csdn.net/cbs612537/article/details/8513723

c.http://blog.csdn.net/yuzhihui_no1/article/details/44339711

d.http://blog.csdn.net/matrix_laboratory/article/details/9304043









  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值