数据结构-经典排序算法:冒泡排序-白话文详解和c/c++代码实现

引言:

创造排序算法的先驱们真的是让人佩服,设计的太巧妙了。今天来讲一下冒泡排序,既是加深理解,也是一种分享。

思想:

冒泡排序是这样的,假如我当前有一个数组[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),大家可以手推导加深理解。

第二部分:用数据结构来实现冒泡排序:

  1. 声明一个结构体,结构体中声明一个数组,再声明一个当前数数组长度,其中数组最大存储空间可以用关键字define来定义一个常量。

#include <iostream>
#define MaxSize 10
using namespace std;
typedef struct
{
    int data[MaxSize];
    int length;
}Sqlist;
  1. 由于在遍历数组的过程中需要进行两个数组的交换,所以可以定义一个函数,用于实现数组中的两个数的交换。

void swap(Sqlist *L, int j)
{
    int temp = L->data[j];
    L->data[j] = L->data[j-1];
    L->data[j-1] = temp;
}
  1. 将排序过程单独定义成一个函数,这个函数是实现排序的核心。

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;
}
  1. 主函数用于定义结构体数组中的元素以及长度,然后调用排序函数。

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;
}
  1. 运行结果如图所示:

完整代码如下:

#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的平方。

书写不易,谢谢点赞加收藏。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值