数据结构与算法详解——排序篇(附c++实现代码)

如何分析评价一个排序算法?

  1. 最好、最坏情况和平均时间复杂度
  2. 空间复杂度,这里有一个专有名词原地排序,指的是空间复杂度为O(1)的排序
  3. 排序的稳定性

  这里需要详细讲一下第三点,排序的稳定性:假设现在我们有一组数据{4,3,1,2,1}需要排序,排序后为{1,1,2,3,4},如果排序前后两个1的前后顺序没有改变,那么就说这个排序算法是稳定的排序算法。
  这个稳定性有什么用?假设现在要为学校的学生的考试总成绩进行排序,分数相同的按照学号的大小顺序进行排序。
  比较容易想到的做法是,直接按照成绩进行排序,然后在遍历数据,成绩相同的数据再按照学号大小排序,这种方法实现起来会比较复杂。
  如果是稳定的排序算法,这个问题可以很好地解决:先将所有数据按照学号大小进行排序,再对排序好的数据按照成绩进行排序。第一次排序后,所有数据按照学号大小排好了,进行第二次排序,由于稳定排序可以保持排序前后成绩相同的两个数据前后顺序不变,所以第二次排序完成后,成绩相同的两个数据他们的学号大小也是排好的。

时间复杂度为O(n^2)的排序

冒泡排序

数组下标 原始数据
4 1 1 1 1 5
3 2 2 2 5 1
2 4 4 5 2 2
1 3 5 4 4 4
0 5 3 3 3 3

  假设现在要为数组{5,3,4,2,1}进行排序,上面就是第一次进行冒泡的操作,从下标为0开始,比较这个位置和下一个位置的数组值的大小,如果大于就交换,否则就不交换,直到遍历完数组,这就完成了一次冒泡。可以看到在第一次冒泡的过程中,最大的值5慢慢往上冒,直到到达数组的最后的位置,这个过程很像气泡冒泡的过程,因此也叫作冒泡排序。
  一次冒泡就会将最大值排到数组最后的位置,所以有n个数据就需要n次冒泡,每次冒泡排好一个数据。

数组下标 原始数据 第一次冒泡 第二次冒泡 第三次冒泡 第四次冒泡 第五次冒泡
4 1 5 5 5 5 5
3 2 1 4 4 4 4
2 4 2 1 3 3 3
1 3 4 2 1 2 2
0 5 3 3 2 1 1

  上面列出了5次冒泡的过程,可以看到最后一次的冒泡是不需要的,因为数组已经排好序了,所以我们可以优化代码,当没有数据交换时结束排序。

template<typename T>
void bubbleSort(std::vector<T> &vec) {
   
	int n = vec.size();
	if (n <= 1)
		return;
	for (int i = 0; i < n; i++) {
   
		printVec(vec);			//打印过程
		bool flag = false;		//flag表示是否有数据交换(冒泡)
		for (int j = 0; j < n - i - 1;j++) {
   		//为什么n减i还要减1,因为要访问j+1
			if (vec[j] > vec[j + 1]) {
   
				T tmp = vec[j];
				vec[j] = vec[j + 1];
				vec[j + 1] = tmp;
				flag = true;	//如果有数据可以交换,那么将flag置为true
			}
		}
		if (!flag)break;		//如果没有数据交换,说明vec已经排好序了,直接跳出循环,算是一种优化
	}
	return;
}

template<typename T>
void printVec(std::vector<T> &vec) {
   
	for (const auto& v : vec) 
		std::cout << v << " ";
	std::cout << std::endl;
}

  代码还是比较简单的,这里就不解释了。
  下面进行算法分析:

  1. 冒泡排序是原地排序,只用了一个临时变量tmp进行数据交换
  2. 冒泡排序是稳定排序,因为在进行比较时,a[i]>a[i+1]才交换,等于时不交换,相同的数据在排序前后顺序不变。(如果是a[i]>=a[i+1]时交换就不是稳定排序了)
  3. 最好的情况是数据已经是有序的,此时只需要进行一次冒泡,时间复杂度是O(n);最坏的情况是数据倒序,此时需要进行n次冒泡,时间复杂度为O(n^2); 平均时间复杂度是O(n^2)。

插入排序

  插入排序的思路也比较简单:将数据分割为有序区和无序区,初始时有序区就是数组的第一个元素,也就是下标为0的位置,无序区就是下标为1的位置开始到数组的末尾。有序区末尾的下标=有序区起始下标-1。
  遍历无序区的元素,选到这个元素value后,从有序区的末尾开始往前遍历,找到第一个小于value的元素的位置,这个位置的后一个位置就是插入value的位置。

在这里插入图片描述
  上图红色区域表示有序区,蓝色区域表示无序区,遍历无序区,每次都会拿到无序区的第一个元素,然后在有序区里找到插入的位置,执行插入操作。

template<typename T>
void insertionSort(std::vector<T>& vec) {
   
	for (int i = 1; i < vec.size(); i++) {
   	//vec[i]是待排序的目标,在有序区中找位置插入
		printVec(vec
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值