【初探】 “ 冒泡排序 ” —— C++代码实现

目录

普通的冒泡排序

使用标志的冒泡排序



普通的冒泡排序


冒泡排序是一种交换排序。

 什么是交换排序呢?

交换排序:两两比较待排序的关键字,并交换不满足次序要求的那对数,直到整个表都满足次序要求为止。

 ● 冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

 ● 冒泡排序算法的运作如下:

 

  •    是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换,
  • 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最大或最小的数字被交换到了最后一位(每将一个数归位我们将其称为“一趟冒泡”)
  •  针对所有的元素重复以上的步骤,直到最后一个尚未归位的数,已经归位的数列无需在进行比较,(比如第二趟冒泡,最后一个数就不需要比较了,因为第一次冒泡已经确定了最后一个数是该数列中最大或最小的数,所以就不需要在进行比较了)。
  • 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

由于它的简洁,冒泡排序通常被用来对于程式设计入门的学生介绍算法的概念。

“冒泡排序”的原理是:

  • 每一趟只能确定将一个数归位。即第一趟只能确定将末位上的 数(即第 5 位)归位,第二趟只能将倒数第 2 位上的数(即第 4 位)归位,第三趟只能将倒 数第 3 位上的数(即第 3 位)归位,而现在前面还有两个位置上的数没有归位,因此我们仍 然需要进行“第四趟”。                                                                 
    算法演示

 

● 最后我们总结一下:如果有 n 个数进行排序,只需将 n-1 个数归位,也就是说要进行 n-1 趟操作。 而“每一趟”都需要从第 1 位开始进行相邻两个数的比较,将较小的一个数放 在前面,比较完毕后向后挪一位继续比较下面两个相邻数的大小,重复此步骤,直到最后一 
个尚未归位的数,已经归位的数则无需再进行比较(已经归位的数你还比较个啥,浪费表情)。

下面看我的示例代码:

#include<iostream>
#include<cassert>
using namespace std;

class SqList
{
public:
	SqList(size_t sizeElem);
	~SqList();
	void printElem();
	void swapElem(int &a, int &b);
	void BubbleSort();
	void create(const size_t length);
private:
	int *m_base; //指向数组
	int m_length;  //记录数组中的个数
};

SqList::SqList(size_t sizeElem)
{
	m_base = new int[sizeElem];
	assert(m_base != nullptr);
	m_length = 0;
}
SqList::~SqList()
{
	delete m_base;
	m_base = nullptr;
}

void SqList::create(const size_t length)
{
	m_length = length;
	cout << "请分别输入你想排序的这" << length << "个元素,中间以回车键隔开:\n";
	for (size_t i = 0; i != length; ++i)
	{
		cin >> m_base[i];
	}
	cout << endl;
}
void SqList::printElem()
{
	for (size_t i = 0; i != m_length; ++i)
	{
		cout << m_base[i] << " ";
	}
	cout << endl;
}
void SqList::swapElem(int &a, int &b)
{
	int temp = a;
	a = b;
	b = temp;
}
void SqList::BubbleSort()
{
	/*int  count = 0; //设置一个临时变量,看几趟,才能排序完成
	for (int i = 0; i < m_length -1; ++i)  
	{
		for (int j = m_length -2 ; i <= j ; --j)  //这是从数组的后面往前面循环
		{
			if (m_base[j] > m_base[j+1])  //若前者大于后者,就交换
			{
				swapElem(m_base[j],m_base[j+1]);
			}
		}
		++count;
	}
	cout << "普通交换排序花了" << count << "趟,完成的排序!" << endl;*/
	//这是从数组的前面往后面循环
	int  count = 0; //设置一个临时变量,看几趟,才能排序完成
	for (int i = 0; i < m_length -1 ; ++i) //如果有 m_length个数进行排序,只需将 m_length-1 个数归位,也就是说要进行 n-1 趟操作。 
	{
		for (int j = 0; j < m_length -i- 1; ++j)
		{
			if (m_base[j] > m_base[j + 1])
			{
				swapElem(m_base[j], m_base[j + 1]);
			}
		}
		++count;
	}
	cout << "普通冒泡排序花了" << count << "趟,完成的排序!" << endl;
}

int main()
{
	{
		int sizeCapacity(0);
		cout << "输入数组的最大容量:";
		cin >> sizeCapacity;
		SqList mySqList(sizeCapacity);

		while (true)
		{
			{
				cout << "\n************************ 欢迎来到来到普通冒泡排序的世界!**********************\n" << endl
					<< "输入0,退出程序!" << endl
					<< "输入1,进行冒泡排序!" << endl
					<< "输入2,清屏!" << endl;
			}

			cout << "************************* 请输入你想要使用的功能的序号 **********************" << endl;
			int select(0);
			cout << "请输入你的选择:";
			cin >> select;
			if (!select)
			{
				cout << "程序已退出,感谢你的使用!" << endl;
				break;
			}
			switch (select)
			{
			case 1:
			{
				cout << "请输入你想排序数组元素的个数:";
				int arraySize(0);
				cin >> arraySize;
				assert(arraySize != 0);

				mySqList.create(arraySize);
				cout << "先输出排序前的元素:";
				mySqList.printElem();

				mySqList.BubbleSort();
				cout << "再输出排序后的元素:";
				mySqList.printElem();
				break;
			}
			case 2:
				system("cls");
				cout << "程序已清屏!可以重新输入!" << endl;
				break;
			default:
				cout << "输入的序号不正确,请重新输入!" << endl;
			}
		}
	}
	system("pause");
	return 0;
}


使用标志的冒泡排序


带标志非常有效,全部排序都需要这个,新手这个务必掌握。只要这一趟没有发生交换,那么就会停止循环扫描,减少了扫描的次数。一个很简单的例子,比如一个顺序的一组元素,就拿1,2,3,4,5来说吧,第一趟扫描完,发现没有元素交换,如果没有标志,最外层循环条件为for(int i=0; i <5;++i)

下面看标志冒泡的代码示例:

#include<iostream>
#include<cassert>
using namespace std;

class SqList
{
public:
	SqList(size_t sizeElem);
	~SqList();
	void printElem();
	void swapElem(int &a, int &b);
	void BubbleSort();
	void create(const size_t length);
private:
	int *m_base; //指向数组
	int m_length;  //记录数组中的个数
};

SqList::SqList(size_t sizeElem)
{
	m_base = new int[sizeElem];
	assert(m_base != nullptr);
	m_length = 0;
}
SqList::~SqList()
{
	delete m_base;
	m_base = nullptr;
}

void SqList::create(const size_t length)
{
	m_length = length;
	cout << "请分别输入你想排序的这" << length << "个元素,中间以回车键隔开:\n";
	for (size_t i = 0; i != length; ++i)
	{
		cin >> m_base[i];
	}
	cout << endl;
}
void SqList::printElem()
{
	for (size_t i = 0; i != m_length; ++i)
	{
		cout << m_base[i] << " ";
	}
	cout << endl;
}
void SqList::swapElem(int &a, int &b)
{
	int temp = a;
	a = b;
	b = temp;
}
void SqList::BubbleSort()
{
	/*int  count = 0; //设置一个临时变量,看几趟,才能排序完成
	bool flag = true;
	for (int i = 0; i < m_length -1 && flag ; ++i)  
	{
		flag = false;
		for (int j = m_length -2 ; i <= j ; --j)  //这是从数组的后面往前面循环
		{
			if (m_base[j] > m_base[j+1])  //若前者大于后者,就交换
			{
				swapElem(m_base[j],m_base[j+1]);
				flag = true;
			}
		}
		++count;
	}
	cout << "普通交换排序花了" << count << "趟,完成的排序!" << endl;*/


	//这是从数组的前面往后面循环
	int  count = 0; //设置一个临时变量,看几趟,才能排序完成
	bool flag = true; //设置一个标志flag, 如果某一趟没有交换,那么该序列就是有序的
	for (int i = 0; i < m_length -1 && flag; ++i) //如果有 m_length个数进行排序,只需将 m_length-1 个数归位,也就是说要进行 n-1 趟操作。 
	{
		flag = false;
		for (int j = 0; j < m_length -i- 1; ++j)
		{
			if (m_base[j] > m_base[j + 1])
			{
				swapElem(m_base[j], m_base[j + 1]);
				flag = true;
			}
		}
		++count;
	}
	cout << "普通冒泡排序花了" << count << "趟,完成的排序!" << endl;
}

int main()
{
	{
		int sizeCapacity(0);
		cout << "输入数组的最大容量:";
		cin >> sizeCapacity;
		SqList mySqList(sizeCapacity);

		while (true)
		{
			{
				cout << "\n************************ 欢迎来到来到普通冒泡排序的世界!**********************\n" << endl
					<< "输入0,退出程序!" << endl
					<< "输入1,进行冒泡排序!" << endl
					<< "输入2,清屏!" << endl;
			}

			cout << "************************* 请输入你想要使用的功能的序号 **********************" << endl;
			int select(0);
			cout << "请输入你的选择:";
			cin >> select;
			if (!select)
			{
				cout << "程序已退出,感谢你的使用!" << endl;
				break;
			}
			switch (select)
			{
			case 1:
			{
				cout << "请输入你想排序数组元素的个数:";
				int arraySize(0);
				cin >> arraySize;
				assert(arraySize != 0);

				mySqList.create(arraySize);
				cout << "先输出排序前的元素:";
				mySqList.printElem();

				mySqList.BubbleSort();
				cout << "再输出排序后的元素:";
				mySqList.printElem();
				break;
			}
			case 2:
				system("cls");
				cout << "程序已清屏!可以重新输入!" << endl;
				break;
			default:
				cout << "输入的序号不正确,请重新输入!" << endl;
			}
		}
	}
	system("pause");
	return 0;
}


时间复杂度


这里写图片描述
 

1.设对象个数为n 个,比较次数和移动次数与初始排序有关

最好的情况 ——若文件的初始状态是正序的(如:1 2 3 4 5),一趟扫描即可完成排序。所需的关键字比较次数和记录移动次数均达到最小值:

比较次数 = N - 1, 移动次数= 0。该序列没有数据交换,所以,冒泡排序最好时间复杂度为O(N)。

最坏的情况——若初始条件是反序的(如:5 4 3 2 1),需要进行 N -1 趟排序 :

比较次数 = N(N-1)/2 = O(N²) 
移动次数 = N(N-1)/2 = O(N²) 

冒泡排序的最坏时间复杂度为O(N²)。 
因此,冒泡排序的平均时间复杂度为O(N²)。 

空间复杂度为O(1) 

总结起来,其实就是一句话:当数据越接近正序时,冒泡排序性能越好。


算法稳定性



冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。

所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值