冒泡排序 - 数据结构 - 算法


超全经典排序大汇总


算法思想

从前往后(或从后往前)两两比较相邻元素的值,若为逆序则交换它们,直到序列比较完为止,称这样的过程为一趟冒泡排序,进行n-1趟冒泡排序即可完成排序。

注:
  1. 每一趟排序都可以使一个元素移动到最终位置,已确定最终位置的元素在之后的处理中无需再对比
  2. 如果某一趟排序过程中未发生交换,则算法可提前结束

时间复杂度

  1. 最好 — O ( n ) O(n) O(n) 有序序列
  2. 最坏 — O ( n 2 ) O(n^2) O(n2) 逆序序列
  3. 平均 — O ( n 1.3 ) O(n^ {1.3}) O(n1.3)

演示动画

冒泡.gif

空间复杂度

使用临时变量 ---- O ( 1 ) O(1) O(1)

稳定性

稳定

适用性

  1. 顺序表
  2. 链表

算法特点

  1. 稳定排序
  2. 可用于链式存储和顺序存储
  3. 移动记录的次数比较多,算法平均时间性能比直接插入排序差。当初始序列无序,n 较大时,此算法不宜采用

核心代码

//冒泡排序 
void BubbleSort(int a[], int n){
	for(int i = 0; i < n - 1; i ++){// 控制趟数, n-1趟 
		bool flag = false;//标记每一趟是否有元素发生交换 
		for(int j = n - 1; j > i; j --){//从后往前依次枚举 
			if(a[j] < a[j - 1]){
			swap(a[j], a[j - 1]);
			flag = true;//发生交换 
			} 
		}
		if(flag == false) return;//本趟未发生交换,即所有元素都已经有序 
	}
}

优化历程

1.朴素写法

//升序排序
void bubble_sort(int a[], int len){//枚举趟, len为数组长度
    for(int i = 0; i < len; i ++){//枚举比较元素
        for(int j = 0; j < len - i - 1; j ++){
            if(a[j] > a[j + 1]){//逆序,交换
                int t = a[j];
                a[j] = a[j + 1];
                a[j + 1] = t;
            }
        }
    }
}

2.一次优化

设置一个标志位, 用来表示当前第 i 趟是否有交换, 如果有则要进行 i+1 趟, 如果没有, 则说明当前数组
已经完成排序, 一旦发现已经排好序, 立即跳出循环, 减少无谓的比较次数.

//升序排序
void bubble_sort(int a[], int len){
    for(int i = 0; i < len; i ++){
        bool flag = true;//记录是否发生交换
        for(int j = 0; j < len - i - 1; j ++){
            if(a[j] > a[j + 1]){
                cnt ++;
                int t = a[j];
                a[j] = a[j + 1];
                a[j + 1] = t;
                flag = false;//发生交换
            }
        }
        if(flag) break;//某一趟已经完全排好序,直接退出.
    }
}

3.二次优化

利用一个标志位, 记录一下当前第 i 趟所交换的最后一个位置的下标,在进行第 i+1趟的时候, 只需要内循
环到标记位置就可以了, 因为后面位置上的元素在上一趟中没有换位, 这一次也不可能会换位置了.

//升序排序
void bubble_sort(int a[], int len){
    int pos;
    for(int i = 0; i < len; i ++){
        bool flag = true;
        for(int j = 0; j < len - i - 1; j ++){
            if(a[j] > a[j + 1]){
                cnt ++;
                int t = a[j];
                a[j] = a[j + 1];
                a[j + 1] = t;
                pos = j;//记录交换的位置
                flag = false;//发生交换
            }
        }
        len = pos;//记录上一次已比较好的位置,用来更新 len
        if(flag) break;
    }
}

总结:

一次优化主要是针对在中间的某一次已经完全排好序,无需再进行后续比较的情况,增加一个标记,根据标记判断当
前数组是否已经完全排好序,一旦排好序,循环立即退出,减少了后续不必要的比较.

二次优化代码主要是在之前的基础上增加一个pos变量,用于记录上一趟发生交换元素最后一个位置,目的是略过之
前已经排好序的元素,枚举到未排好序的元素为止.

完整代码

#include <iostream> 
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>

using namespace std;
const int N = 20;

int num;
int data[N],idx;

//冒泡排序 
void BubbleSort(int a[], int n){
	for(int i = 0; i < n - 1; i ++){// 控制趟数, n-1趟 
		bool flag = false;//标记每一趟是否有元素发生交换 
		for(int j = n - 1; j > i; j --){//从后往前依次枚举 
			if(a[j] < a[j - 1]){
			swap(a[j], a[j - 1]);
			flag = true;//发生交换 
			} 
		}
		if(flag == false) return;//本趟未发生交换,即所有元素都已经有序 
	}
}

int main(){
	//打开文件 
	ifstream infile;
	infile.open("D:\\学习\\数据结构\\第8章排序\\in.txt", ios::in);
	
	//写文件 
	ofstream outfile;
	outfile.open("D:\\学习\\数据结构\\第8章排序\\out.txt", ios::out);
	
	if(!infile.is_open()){//判断文件打开是否成功 
		cout << "file open failure!" << endl;
	}
	
	infile >> num;//读取元素个数 
	while(num --){//将文件中的元素复制到data[1...n] 数组中
		infile >> data[idx ++];
	}
	
	cout << "排序前元素序列:" << endl;
	for(int i = 0; i < idx; i ++) cout << data[i] << ' '; cout << endl;
	
	cout << "使用sort函数排序后序列: " << endl;
	sort(data, data + idx); 
	for(int i = 0; i < idx; i ++) cout << data[i] << ' '; cout << endl;
	
	BubbleSort(data, idx);
	cout << "使用冒泡排序后序列为:" << endl;
	for(int i = 0; i < idx; i ++) cout << data[i] << ' '; cout << endl;
	
	num = idx, idx = 0, outfile << num << endl;//写入数据数num以及在行末写入\n 
	while(num --){
		outfile << data[++ idx] << ' ';//将排序后的数据写到文件中 
	}
	outfile << endl;//向文件末尾写入\n结束符 
	
	//关闭文件 
	infile.close();
	outfile.close();
	return 0;
}

输入数据(in.txt)

10
13 69 86 99 59 23 64 32 86 72

输出数据(out.txt)

10
13 23 32 59 64 69 72 86 86 99 
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

soyisou

您的鼓励将是我创作的最大动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值