赞
踩
引言:
创造排序算法的先驱们真的是让人佩服,设计的太巧妙了。今天来讲一下冒泡排序,既是加深理解,也是一种分享。
思想:
冒泡排序是这样的,假如我当前有一个数组[x1,x2,x3,...,xn],这n个数是无序的,那么我要对该数组进行排序的话,假如要将该数组进行升序,我每一次都能从前往后(或从后往前)确定一个元素xi的具体位置,排在元素xi之前的元素都要比它小,它之后的元素都比它大,具体是先确定正数第一个位置还是倒数第一个位置,改变循环条件即可。这样讲可能有点抽象,那我们举个例子,逐步讲解冒泡排序的思想:
声明一个包含有8个元素的数组a[7],数组下标从0开始,所以这里为7时代表的第八个元素:
2 | 9 | 3 | 4 | 6 | 8 | 7 | 5 |
目标:
对该数组a[8]进行升序排序,即小的排在前面,大的排在后面。
解决思路:
先假设a[i]为我当前这一轮得出来的最小元素值的存储位置,a[j]和a[j-1]为当前两个元素作比较。
比如我当前开始遍历第一轮,确定一个最小值,并把它存在数组的第一个位置,i=0,即a[0]这个位置。
如何确定最小值?我从该数组的最后一个位置开始往前遍历作两两比较,遍历到当前存储最小值位置的下一个位置,第一轮比较具体到我的数组则是,a[7]与a[6]比较一次,a[6]与a[5]比较一次,a[5]与a[4]比较一次,...,a[1]与a[0]比较一次,在进行a[1]与a[0]比较的时候,如果a[1]小于a[0],则交换他们两个位置,否则不交换,此时a[0]存储的就是这一轮两两比较中的最小值;即我把j=7遍历到j=1;
因为a[0]已经存储了当前的最小值,所以下一轮的最小值应该存储在a[1]中,即i=1;下一轮开始:我依然从该数组的最后一个位置开始往前遍历作两两比较,只不过这一轮我只从后往前遍历到a[2],也就是a[7]与a[6]比较一次,a[6]与a[5]比较一次,a[5]与a[4]比较一次,...,a[2]与a[1]比较一次,不需要再进行a[1]与a[0]比较,因为a[0]已经存储了上一轮整个数组的最小值,现在的a[1]存储第二小值。
下一轮。。。,对于我声明的数组,循环遍历直到i=6即可,也是就数组长度减1的地方为最后一轮,因为当i=[6]的时候,我后面只剩一个元素i=7,这两个元素比较一次就可以确定这两个元素的最终位置了。
把思路演示出来看一下,记着目标是升序数组:
元素组a[7]:
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] |
2 | 9 | 3 | 4 | 6 | 8 | 7 | 5 |
(1)、当i=0,j=7时,a[7]=5,a[6]=7,a[7]<a[6],交换位置,a[6]=5,a[7]=7。
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] |
2 | 9 | 3 | 4 | 6 | 8 | 5 | 7 |
(2)、当i=0,j=6时,a[6]=5,a[5]=8,a[6]<a[5],交换位置,a[6]=8,a[5]=5。
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] |
2 | 9 | 3 | 4 | 6 | 5 | 8 | 7 |
(3)、当i=0,j=5时,a[5]=5,a[4]=6,a[5]<a[4],交换位置,a[5]=6,a[4]=5。
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] |
2 | 9 | 3 | 4 | 5 | 6 | 8 | 7 |
(4)、当i=0,j=4时,a[4]=5,a[3]=4,a[4]>a[3],不交换位置。
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] |
2 | 9 | 3 | 4 | 5 | 6 | 8 | 7 |
(5)、当i=0,j=3时,a[3]=4,a[2]=3,a[3]>a[2],不交换位置。
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] |
2 | 9 | 3 | 4 | 5 | 6 | 8 | 7 |
(6)、当i=0,j=2时,a[2]=3,a[1]=9,a[2]<a[1],交换位置,a[2]=9,a[1]=3。
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] |
2 | 3 | 9 | 4 | 5 | 6 | 8 | 7 |
(7)、当i=0,j=1时,a[1]=3,a[0]=2,a[2]>a[1],不交换位置。
a[0] | a[1] | a[2] | a[3] | a[4] | a[5] | a[6] | a[7] |
2 | 3 | 9 | 4 | 5 | 6 | 8 | 7 |
以上就是一轮完整的遍历。接下来就是(i=1,j=7,6,5,4,3,2),(i=2,j=7,6,5,4,3),(i=3,j=7,6,5,4),(i=4,j=7,6,5),(i=5,j=7,6),(i=6,j=7),大家可以手推导加深理解。
第二部分:用数据结构来实现冒泡排序:
声明一个结构体,结构体中声明一个数组,再声明一个当前数数组长度,其中数组最大存储空间可以用关键字define来定义一个常量。
- #include <iostream>
- #define MaxSize 10
- using namespace std;
- typedef struct
- {
- int data[MaxSize];
- int length;
- }Sqlist;
由于在遍历数组的过程中需要进行两个数组的交换,所以可以定义一个函数,用于实现数组中的两个数的交换。
- void swap(Sqlist *L, int j)
- {
- int temp = L->data[j];
- L->data[j] = L->data[j-1];
- L->data[j-1] = temp;
- }
将排序过程单独定义成一个函数,这个函数是实现排序的核心。
- void maopao(Sqlist *L)
- {
- // i是从前往后遍历,遍历到倒数第二个位置
- for(int i=0;i<L->length-1;i++)
- {
- // flag用于标志,当前一轮如果没有进行位置交换,说明已经排好序,不需要继续遍历比较,结束函数
- bool flag = false;
- // j是从后往前遍历,从最后一个元素开始,比较j和j-1元素的大小,每一轮都遍历到i+1这个位置,因为i之前说的元素都已经有序
- for(int j=L->length-1;j>i;j--)
- {
- if(L->data[j] < L->data[j-1])
- {
- // 交换两个元素的位置,调用上面声明的swap函数
- swap(L, j);
- flag=true;
- }
- }
- if(flag==false)
- {
- return;
- }
- }
- return;
- }
主函数用于定义结构体数组中的元素以及长度,然后调用排序函数。
- int main()
- {
- Sqlist *L;
- L->length=8;
- L->data[0] = 2;
- L->data[1] = 9;
- L->data[2] = 3;
- L->data[3] = 4;
- L->data[4] = 6;
- L->data[5] = 8;
- L->data[6] = 7;
- L->data[7] = 5;
- maopao(L);
- for(int i = 0; i<L->length; i++)
- {
- cout<<"L->data["<<i<<"]="<<L->data[i]<<endl;
- }
-
- return 0;
- }
运行结果如图所示:
完整代码如下:
- #include <iostream>
- #define MaxSize 10
- using namespace std;
- typedef struct
- {
- int data[MaxSize];
- int length;
- }Sqlist;
- void swap(Sqlist *L, int j)
- {
- int temp = L->data[j];
- L->data[j] = L->data[j-1];
- L->data[j-1] = temp;
- }
- void swap1(Sqlist *L, int i, int j)
- {
- int temp;
- temp = L->data[i];
- L->data[i] = L->data[j];
- L->data[j] = temp;
- }
- // 冒泡排序
- void maopao(Sqlist *L)
- {
- for(int i=0;i<L->length-1;i++)
- {
- bool flag = false;
- for(int j=L->length-1;j>i;j--)
- {
- if(L->data[j] < L->data[j-1])
- {
- swap(L, j);
- flag=true;
- }
- }
- if(flag==false)
- {
- return;
- }
- }
- return;
- }
- int main()
- {
- Sqlist *L;
- L->length=8;
- L->data[0] = 2;
- L->data[1] = 9;
- L->data[2] = 3;
- L->data[3] = 4;
- L->data[4] = 6;
- L->data[5] = 8;
- L->data[6] = 7;
- L->data[7] = 5;
- maopao(L);
- for(int i = 0; i<L->length; i++)
- {
- cout<<"L->data["<<i<<"]="<<L->data[i]<<endl;
- }
-
- return 0;
- }
总结:冒泡排序是一个比较基础的排序,其运行时间复杂度最好为数组有序的情况,比较n-1次即可,最坏为数组反序的情况,比较n(n-1)/2,即时间复杂度为n的平方。
书写不易,谢谢点赞加收藏。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。