1.序言
其实早在几年前,我就了解了冒泡排序的原理,但心中一直存在以下两个疑问:
1.如何将冒泡排序简单的两行原理转化为代码?
2.冒泡排序的代码如何写才算是优秀的?
看过很多冒泡排序的书籍和博客,有很多给出了示例,步骤,代码,有的甚至进行了时间复杂度的分析,但那终究不是自己的东西,虽说,现在依然有些问题解释的不太清楚,但我想尝试一下解释清楚,望大神们多多指点。
2.问题描述
输出:原序列的一个重排<a1*, a2*, a3*, ... , an*>,使得a1*<=a2*<=a3*<=...<=an*。
3.问题分析
4.问题解决前提条件
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.优化策略
造成没必要的操作主要原因是后面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