考研:交换排序(冒泡排序、快速排序)

一、 冒泡排序


(一)算法思想:
        每一轮都进行相邻的往后比较,如果是逆序,就交换位置,如果不是,则往后移动,每一轮都可以比较出一个最值(取决于你是升序还是降序)
        对了,他叫冒泡的原因也是因为这个,假如我们要升序,那么每次比较如果前一个比后一个大,那么我们就要让这俩交换位置,才能让大的往后走,达到升序的效果,这也就像最大的值在不断的往水面上冒泡,而较小的值因为交换则不断地沉入湖底。


(二)算法逻辑:

  1. 第一个和第二个比较,如果逆序就交换位置然后进行第二个和第三个的比较,如果不逆序,进行第二个和第三个的比较;然后以此类推。
  2. 第一轮就会因为比较,就把第一个最值给放在了最后一个位置
  3. 然后第二轮就进行从第一个到倒数第二个的依次比较,因为最后一个已经是我们比较出来的最值,所以没必要再比较一次。这一轮比较出来的第二最值就会被放在倒数第二个位置,因为最后一个位置没有进行比较。
  4. 假如数组里面一共有 n 个元素要进行排序,每一轮比较都可以找出一个最值往后放,而且当只剩下一个元素的时候,就是自然有序的,不需要再进行比较,所以我们只需要 n-1 轮比较就可以将整个数组进行完整的排序。

(三)代码如下:
       逻辑大致我们是懂了,下面看代码。

// arr是待排序的数组,你也可以想象为顺序表,因为顺序表的本质就是一维数组
// n表示数组里面元素的个数
void BubbleSort(int arr[], int n){
	// 两个变量,一个用于内层循环,一个用于外层循环
	int i, j;
	
	// 首先,我们要进行n-1轮冒泡才能让这个数组有序
	for (i=1; i<=(n-1); i++){
		
		// 然后,每一轮,我们要比较并在逆序的时候交换元素,从第一轮到第 n-1 轮,一共依次比较 n-1,n-2,...,2,1
		for (j=1; j<=n-i; j++){

			if (arr[j]<arr[j+1]){ // 这里假设降序

				// 以arr[0]作为暂时存储的空间,进行交换次序
				arr[0] = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = arr[0];
			}
		}
	}
}


二、快速排序(在考研初试并不要求这个排序的代码,但也要掌握这个算法的思路,如果你对代码感兴趣,可以往下看,如果只是想了解思路,看看书就好)


(一)代码思路(以升序为例):

  1. 首先要有一个用来将元素拆分成两半的函数。
  2. 然后有一个进行循环拆分的函数。(这个拆分的范围是从待拆分区域低位到高位
  3. 第二个函数可以通过递归实现,因为它首先对当前进行拆分,取到一个插入的位置,然后对插入的位置的左边和右边进行分别递归拆分,当拆分到最后只有一个元素的时候,就退出,因为一个元素自然有序。

(二)举个例子: 用引号括起来表示此处为标点或者已经确定位置。
1. 初始状态:‘2’    3     5      1    0 【我先以第一个元素2为标点,对剩下的元素进行移动】
2. 第一轮:     0    1    |‘2’|    5    3 【这时候就分成了左右两边,然后再左边以0为标点,这时候不会比较到超过2元素的位置,也就是只会比较左边的 0 和 1】
3. 第二轮:    ‘0’   ‘1’   |‘2’|    5    3 【同理再比较右边的两个元素,也不会过2这个边界】
4. 第三轮:    ‘0’   ‘1’   |‘2’|   ‘5’    3 【这时候一比较,显然是3<5,并且还在5的右边,我们遵循比标点元素小的放左边原则,那么3就要移动到5的位置,然后因为只有3一个元素,所以让5赋值到3的原来的位置】
5. 第四轮:    ‘0’   ‘1’   |‘2’|   ‘3’   ‘5’ 【此处已完结】


(三)看完上面例子的朋友应该大致了解了这么个元素,但是为什么那个 ‘5’ 要放在 ‘3’ 的位置,这是怎么判断的?
    问必有答好吧。

	5.1 首先我们先让标点的值赋值给arr[0],暂时存起来,便于后来使用。
	5.2 然后给定两个元素low和high,并且low最开始是指向第一个元素的位置的,也就是标点的位置。
	5.3 并且因为low已经指定了标点的位置,我们可以认为他指向的位置是空的,因为标点的值已经被我们放在了arr[0]中,所以low那个位置的值可以当做没有。
	5.4 然后我们就从high的位置开始比较,只要high的位置的元素比标点大,我们就让high--,往回指,依次判断。
	5.5 直到我们在high这边找到一个比标点的值小的值,我们就把这个值放在low的位置,然后让low++,从low这个位置开始找。
	5.6 不过我们这次找的是比标点的值大的值,当找到一个大的值的时候,我们又让这个大的值赋值给high所指的位置,因为high的值在上次已经赋值到low这边了,所以依然可以覆盖。
	5.7 以此不断的循环,当最后一次的时候,low和high会指向同一个位置,这个位置的值可能比标点大,也可能比标点小。
	5.8 但不管怎么样,就算赋值给另一边,由于他俩指向了同一个位置,所以还是没有影响,并且这个值在上一轮有low或者high移动到这里的时候,也已经被复制到别的地方了。
	5.9 所以这个high和low指向的同一个位置的值,是一个多余的值,可以被覆盖,也就是说可以被我们的标点覆盖。
	5.10 而我们标点要插入的位置,就是low和high相等的位置。low和high相等,也是我们退出循环的条件。

(四)代码如下:

// 这里要声明一下定位函数Position(),因为我们在用递归函数使用定位函数的时候,定位函数还没执行出来,他还在后面,
// 所以我们要先声明一下,告诉编译器,后面有个函数我们要用一下。
// 当然这里声明的时候不要求形参的名字和后面一样,我只是出于习惯所以写成一样的。
int Position(int arr[], int low, int high);

// 1. 我们先写递归的函数
// void表示没有返回值,这个我好像一直忘说了
// 第一个参数是待排序的数组参数arr[]
// 第二个和第三个是用于拆分的时候要使用的低位和高位
void QuickSort(int arr[],int low, int high){

	// 第一个用于循环
	// 第四个是用来接收拆分后确定下来的位置
	int i, pivotKey;
	
	// 这里修改一下,之前写的for循环,后来发现直接if语句判断low和high就行了,因为在不断拆分的时候,就已经把左右元素都拆分了一边,没必要循环了
	if (low<high  ){

		// Position()是定位函数。首先对第一个元素进行定位和对数组进行拆分,拆分后得到第一个元素要插入的位置
		pivotKey = Position(arr, low ,high); 
		
		// 然后对左边进行定位拆分:从 low 到 pivotKey-1 的位置
		QuickSort(arr, low, pivotKey-1);
		
		// 然后对右边进行定位拆分:从 pivotKey+1 到 high 的位置
		QuickSort(arr, pivotKey+1, high);

	}
	// 递归完了,也就排序完了,是不是很简单,下面的定位函数相对难点儿
}

// 2. 然后再写拆分定位的函数
// int 表示返回一个整数类型,也就是我们一会儿要返回定位好的位置的下标,便于递归的时候使用
// 传入参数一个待排序的数组,要进行拆分的低位 low 和高位 high
int Position(int arr[], int low, int high){

	// 把标点放入arr[0]位置暂时存放,一般取低位元素为标点
	// 你也可以取高位,不过如果你取高位作为标点,那么后面的while循环就从low开始
	// 我取的低位,所以后面的while循环会从高位开始
	arr[0] = arr[low];

	// 开始进行循环比较,并且退出循环的条件是low==high的时候,在这之前low<high恒成立
	while(low<high){

		// 先比较高位的的值,如果高位的值比标点大,并且high依然大于low的话,就让high--,相当于往前移动
		while (arr[high]>arr[0] && high>low)  high--;

		// 当退出上面的循环的时候,说明找到一个比标点小的或者high=low,这时候就执行高位赋值到低位的操作
		arr[low] = arr[high];

		// 当上面的while循环是以low=high退出的,那么这里的while循环是不会进入的
		// 然后比较低位的的值,如果低位的值比标点小,并且high依然大于low的话,就让low++,相当于往后移动
		while (arr[low]<arr[0] && high>low)  low++;
		
		// 当退出上面的循环的时候,说明找到一个比标点大的或者high=low,这时候就执行低位赋值到高位的操作
		arr[high] = arr[low];
	}

	// 当上面的while循环退出的时候,说明low=high成立了
	// 也就是我们确定要插入的位置了,因为low等于high,所以这个赋值给low的位置也行,high的位置也行
	arr[low] = arr[0];

	// 在把标点插入合适的位置后,我们把这个位置返回出去,便于后面的拆分后,左边和右边分别定位
	return low;  // return就是返回的语句,就表示你在使用这个函数的时候,可以用一个int类型的变量把这个值接住,便于你后面的使用
}



三、主函数

void main(void){
	int i;
	int arr[] = {0,5,68,1,548,3,16,8};
	int n = 7;
	// BubbleSort(arr, n);
	QuickSort(arr, 1, n);
	for (i=1; i<=n; i++){
		printf("%d,",arr[i]);
	}
	printf("\n");
}

        对于以上两个排序,快速排序就是冒泡排序的升级版,并且难度上也大了很多。(假设升序)快速排序就是把比标点大的元素往左边冒,把比标点小的元素往右边冒,这也算是一种冒泡。
        以上就是我个人的一些理解,如果大家还有独到的理解,可以评论区留言讨论。 ~ . ~

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
快速排序是一种高效的排序算法,它采用分治的思想,通过将数组分为较小和较大的两部分,递归地对这两部分进行排序,最终使整个数组有序。下面是用 C 语言实现快速排序的一种算法快速排序的基本思路是选择一个基准元素,将数组划分为左右两个部分,左部分比基准元素小,右部分比基准元素大,然后对左右两个部分分别进行递归排序。 具体实现如下: 1. 定义一个函数 `quick_sort`,用于排序整个数组; 2. 在函数内部定义三个变量:`left`、`right`、`pivot`; 3. `left` 表示数组的左边界,`right` 表示数组的右边界; 4. `pivot` 用于存储基准元素; 5. 如果 `left` 大于等于 `right`,则返回; 6. 将 `pivot` 设置为数组的第一个元素; 7. 记录下 `pivot` 的位置 `index`; 8. 从 `left+1` 开始遍历数组,如果找到比 `pivot` 大的元素,则将其与 `pivot` 交换,并停止遍历; 9. 从 `right` 开始遍历数组,如果找到比 `pivot` 小的元素,则将其与 `pivot` 交换,并停止遍历; 10. 继续上述步骤,直到 `left` 和 `right` 相交; 11. 将 `pivot` 与 `index` 所在位置的元素交换; 12. 分别对左右两个部分递归调用 `quick_sort` 函数。 这样,通过不断地划分和排序,最终可以使整个数组有序。 以上就是用 C 语言实现快速排序的基本思路和步骤,希望对您理解快速排序算法有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

为什么我不是源代码

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

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

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

打赏作者

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

抵扣说明:

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

余额充值