冒泡排序(Bubble Sort)
需求:实现int类型数组从小到大排序
代码(C实现):
#include <stdio.h>
#include <stdlib.h>
/***************************************************************************
*Function Name:bubble_sort
*Function argt:
* 1.int *parray:a point which point to an array;
* 2.int num :a var(int), size of array, in term of element;
*Return Value:void
*Description :sort an array, from small to large.
*Author :test1280
*History :2017/04/13
**************************************************************************/
void bubble_sort(int *parray, int num)
{
int tmp;
int i, j;
for (i=0; i<num-1; i++)
{
for (j=0; j<num-1-i; j++)
{
if (parray[j]>parray[j+1])
{
tmp = parray[j];
parray[j] = parray[j+1];
parray[j+1] = tmp;
}
}
}
}
int main()
{
int array[10] = {9, 5, 12, 36, 52, 1, -8, 14, -7, 0};
bubble_sort(array, 10);
for (int i=0; i<10; i++)
{
printf("%d\n", array[i]);
}
return 0;
}
所谓冒泡排序,就是待排序序列中每两个元素进行比较,如果满足一定条件,则进行元素之间的相互交换。
上述例子中:
待排序的元素共有10个;
第0次外循环:每两个元素进行比较,从0位置开始,比较9次,即可将最大的元素交换到最后一个位置9处;
第1次外循环:每两个元素进行比较,从0位置开始,比较8次,即可将最大的元素交换到位置8处;
第2次外循环:每两个元素进行比较,从0位置开始,比较7次,即可将最大的元素交换到位置7处;
……
共需要外循环num-1次,因为最后一个不需要比较,一定是最小的。
第i次外循环中,内循环共有num-i-1次。
上述例子的排序示意:
original:9 5 12 36 52 1 -8 14 -7 0
(以下为每次两个元素交换
后的示意图,红色标记是哪两个元素交换)
外循环第0次:
内循环第0次交换:
5 9 12 36 52 1 -8 14 -7 0
内循环第1次交换:
5 9 12 36
1 52 -8 14 -7 0
内循环第2次交换:
5 9 12 36 1
-8 52 14 -7 0
内循环第3次交换:
5 9 12 36 1 -8
14 52 -7 0
内循环第4次交换:
5 9 12 36 1 -8 14
-7 52 0
内循环第5次交换:
5 9 12 36 1 -8 14 -7
0 52
外循环第0次结束。
可以看到,52这个最大的数字从左到右,一点点地浮出水面,就像泡泡一样,得名冒泡排序。
start 1
5 9 12 1 36 -8 14 -7 0 52
5 9 12 1 -8 36 14 -7 0 52
5 9 12 1 -8 14 36 -7 0 52
5 9 12 1 -8 14 -7 36 0 52
5 9 12 1 -8 14 -7 0 36 52
5 9 12 1 -8 36 14 -7 0 52
5 9 12 1 -8 14 36 -7 0 52
5 9 12 1 -8 14 -7 36 0 52
5 9 12 1 -8 14 -7 0 36 52
end 1
start 2
5 9 1 12 -8 14 -7 0 36 52
5 9 1 -8 12 14 -7 0 36 52
5 9 1 -8 12 -7 14 0 36 52
5 9 1 -8 12 -7 0 14 36 52
end 2
start 3
5 1 9 -8 12 -7 0 14 36 52
5 1 -8 9 12 -7 0 14 36 52
5 1 -8 9 -7 12 0 14 36 52
5 1 -8 9 -7 0 12 14 36 52
end 3
start 4
1 5 -8 9 -7 0 12 14 36 52
1 -8 5 9 -7 0 12 14 36 52
1 -8 5 -7 9 0 12 14 36 52
1 -8 5 -7 0 9 12 14 36 52
end 4
start 5
-8 1 5 -7 0 9 12 14 36 52
-8 1 -7 5 0 9 12 14 36 52
-8 1 -7 0 5 9 12 14 36 52
end 5
start 6
-8 -7 1 0 5 9 12 14 36 52
-8 -7 0 1 5 9 12 14 36 52
end 6
start 7
end 7
start 8
end 8
5 9 1 12 -8 14 -7 0 36 52
5 9 1 -8 12 14 -7 0 36 52
5 9 1 -8 12 -7 14 0 36 52
5 9 1 -8 12 -7 0 14 36 52
end 2
start 3
5 1 9 -8 12 -7 0 14 36 52
5 1 -8 9 12 -7 0 14 36 52
5 1 -8 9 -7 12 0 14 36 52
5 1 -8 9 -7 0 12 14 36 52
end 3
start 4
1 5 -8 9 -7 0 12 14 36 52
1 -8 5 9 -7 0 12 14 36 52
1 -8 5 -7 9 0 12 14 36 52
1 -8 5 -7 0 9 12 14 36 52
end 4
start 5
-8 1 5 -7 0 9 12 14 36 52
-8 1 -7 5 0 9 12 14 36 52
-8 1 -7 0 5 9 12 14 36 52
end 5
start 6
-8 -7 1 0 5 9 12 14 36 52
-8 -7 0 1 5 9 12 14 36 52
end 6
start 7
end 7
start 8
end 8
start标记的是外循环第i次,从0开始;
start与end之间的是在此次循环中,进行了多少次内循环,输出为内循环交换后的结果。
若想要以从大到小的顺序排序,可以:
void bubble_sort(int *parray, int num)
{
int tmp;
int i, j;
for (i=0; i<num-1; i++)
{
printf("start %d\n", i);
for (j=0; j<num-1-i; j++)
{
if (parray[j]<parray[j+1])
{
tmp = parray[j];
parray[j] = parray[j+1];
parray[j+1] = tmp;
}
}
printf("end %d\n", i);
}
}
仅仅修改一下比较的条件即可:if (parray[j]<parray[j+1])
思考:
是否可以从后向前进行冒泡呢?
假设现在的需求还是从小到大,只不过要求从后进行冒泡。
我们可以每次外循环时总是将本轮中最小的放置到本轮的最前位置,代码修改如下:
void bubble_sort(int *parray, int num)
{
int tmp;
int i, j;
for (i=0; i<num-1; i++)
{
for (j=num-1; j>i; j--)
{
if (parray[j]<parray[j-1])
{
tmp = parray[j];
parray[j] = parray[j-1];
parray[j-1] = tmp;
}
}
}
}
每次交换后序列:
9 5 12 36 52 1 -8 -7 14 0
9 5 12 36 52 -8 1 -7 14 0
9 5 12 36 -8 52 1 -7 14 0
9 5 12 -8 36 52 1 -7 14 0
9 5 -8 12 36 52 1 -7 14 0
9 -8 5 12 36 52 1 -7 14 0
-8 9 5 12 36 52 1 -7 14 0
-8 9 5 12 36 52 1 -7 0 14
-8 9 5 12 36 52 -7 1 0 14
-8 9 5 12 36 -7 52 1 0 14
-8 9 5 12 -7 36 52 1 0 14
-8 9 5 -7 12 36 52 1 0 14
-8 9 -7 5 12 36 52 1 0 14
-8 -7 9 5 12 36 52 1 0 14
-8 -7 9 5 12 36 52 0 1 14
-8 -7 9 5 12 36 0 52 1 14
-8 -7 9 5 12 0 36 52 1 14
-8 -7 9 5 0 12 36 52 1 14
-8 -7 9 0 5 12 36 52 1 14
-8 -7 0 9 5 12 36 52 1 14
-8 -7 0 9 5 12 36 1 52 14
-8 -7 0 9 5 12 1 36 52 14
-8 -7 0 9 5 1 12 36 52 14
-8 -7 0 9 1 5 12 36 52 14
-8 -7 0 1 9 5 12 36 52 14
-8 -7 0 1 9 5 12 36 14 52
-8 -7 0 1 9 5 12 14 36 52
-8 -7 0 1 5 9 12 14 36 52
若想从后向前冒泡,从大到小排序,只需要针对上面的比较条件改成:if (parray[j]>parray[j-1])即可:
void bubble_sort(int *parray, int num)
{
int tmp;
int i, j;
for (i=0; i<num-1; i++)
{
for (j=num-1; j>i; j--)
{
if (parray[j]>parray[j-1])
{
tmp = parray[j];
parray[j] = parray[j-1];
parray[j-1] = tmp;
}
}
}
}
1.从小到大排序,从前向后冒泡;
2.从大到小排序,从前到后冒泡;
3.从小到大排序,从后向前冒泡;
4.从大到小排序,从后向前冒泡。
1和2只是条件换了下;3和4也是条件换了下。
冒泡排序最关键的是边界值,大家有点晕的时候可以在纸上画一画,立刻就清晰了。
再思考,有没有优化版的冒泡呢?
假设现在有一个待排序数组:0, 2, 1, 3, 4, 5, 6, 7, 8, 9
如果按照我们上述的冒泡算法,执行如下:
#include <stdio.h>
#include <stdlib.h>
void print_array(int *parray, int num)
{
int i;
for (i=0; i<num; i++)
{
printf("%d ", parray[i]);
}
printf("\n");
}
void bubble_sort(int *parray, int num)
{
int tmp;
int i, j;
for (i=0; i<num-1; i++)
{
printf("...start:%d\n", i);
for (j=0; j<num-1-i; j++)
{
if (parray[j]>parray[j+1])
{
tmp = parray[j];
parray[j] = parray[j+1];
parray[j+1] = tmp;
}
print_array(parray, 10);
}
printf("...end:%d\n", i);
}
}
int main()
{
int array[10] = {0, 2, 1, 3, 4, 5, 6, 7, 8, 9};
printf("ori:\n");
print_array(array, 10);
bubble_sort(array, 10);
for (int i=0; i<10; i++)
{
printf("%d\n", array[i]);
}
return 0;
[test1280@localhost sort]$ ./main
ori:
0 2 1 3 4 5 6 7 8 9
...start:0
0 2 1 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
...end:0
...start:1
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
...end:1
...start:2
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
...end:2
...start:3
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
...end:3
...start:4
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
...end:4
...start:5
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
...end:5
...start:6
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
...end:6
...start:7
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
...end:7
...start:8
0 1 2 3 4 5 6 7 8 9
...end:8
0
1
2
3
4
5
6
7
8
9
很明显后面的循环都是不必要的,我们可以增加一个标识来记录一下:
#include <stdio.h>
#include <stdlib.h>
void print_array(int *parray, int num)
{
int i;
for (i=0; i<num; i++)
{
printf("%d ", parray[i]);
}
printf("\n");
}
void bubble_sort(int *parray, int num)
{
int tmp;
int i, j;
int flag = 1;
for (i=0; i<num-1; i++)
{
flag = 0;
printf("...start:%d\n", i);
for (j=0; j<num-1-i; j++)
{
if (parray[j]>parray[j+1])
{
tmp = parray[j];
parray[j] = parray[j+1];
parray[j+1] = tmp;
flag = 1;
}
print_array(parray, 10);
}
printf("...end:%d\n", i);
if (flag == 0)
break;
}
}
int main()
{
int array[10] = {0, 2, 1, 3, 4, 5, 6, 7, 8, 9};
printf("ori:\n");
print_array(array, 10);
bubble_sort(array, 10);
for (int i=0; i<10; i++)
{
printf("%d\n", array[i]);
}
return 0;
}
[test1280@localhost sort]$ ./main
ori:
0 2 1 3 4 5 6 7 8 9
...start:0
0 2 1 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
...end:0
...start:1
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
...end:1
0
1
2
3
4
5
6
7
8
9
[test1280@localhost sort]$
相对最开始的冒泡排序,效率提高了一些。