[交换排序] 冒泡排序(两种优化)、快速排序(3种方法+两种优化)

本文详细介绍了冒泡排序的三种优化方法,包括基础冒泡排序、增加flag标志优化和双向冒泡排序,并探讨了快速排序的基本思想、双指针法、挖坑法和前后指针法。此外,还讨论了快速排序的优化策略,如在一定范围内使用直接插入排序和三数取中法确定基准值。
摘要由CSDN通过智能技术生成

一、冒泡排序

1.1 基础的冒泡排序

  算法思想:从后向前,相邻两个元素进行两两比较,如果后面的元素的值小于前面元素的值,就交换两个元素。一趟冒泡下来,最小的元素就会被放到最前面的位置。这样最多进行n-1趟冒泡,就会把所有的元素全部排好。
  相关代码如下:

void Bubble_sort(int[] array){
		for(int i=0; i<array.length; i++){
			for(int j=array.length-1; j>i; j--){
			//一趟冒泡排序:从后往前,相邻两个元素进行两两比较
				if(array[j]<array[j-1]){
				//如果后面的元素小于前面的元素,就交换
					Swap(array, j, j-1);
				}
			}
		}
	}
	/*
	 * 时间复杂度:
	 * 最坏情况:O(n^2)   最好情况(有序时):O(n)
	 * 空间复杂度:O(1)
	 * 稳定性:稳定
	 */

1.2 冒泡排序的优化(增加flag标志)

  算法思想:在基础冒泡排序的算法上,增加flag标志,用来标记当前冒泡过程中是否发生元素交换。如果在某一趟的比较过程中,发现没有任何元素移动,就不再进行接下来的比较。
  相关代码如下:

	void Bubble_sort(int[] array){
		for(int i=0; i<array.length; i++){
			boolean flag = false; //设置一个是否发生元素的标志
			for(int j=array.length-1; j>i; j--){
				if(array[j]<array[j-1]){
					Swap(array, j, j-1);
					flag = true;
				}
			}
			if(flag==false) 
			//该趟没有发生元素交换,表明该数组已经有序,直接返回
				return;
		}
	}

1.3 冒泡排序的优化(双向冒泡排序法)

  算法思想:在待排序列的正反两个方向交替进行扫描,即,第一趟把元素值最大的放在序列的最后面,第二趟把元素值最小的放在序列的最前面,如此反复进行,直到整个待排序列有序。
基本步骤:
  1.在基础冒泡排序的算法上进行操作
  2.奇数趟时,从前向后比较相邻元素的值,如果前面元素的值大于后面元素的值就交换,直到把序列中最大值元素移动到序列的最右端
  3.更新上界
  4.偶数趟时,从后向前比较相邻元素的值,如果后面元素的值小于前面元素的值就交换,直到把序列中最小值元素移动到序列的最右端
  5.更新下界
  6.直到整个序列有序
相关代码如下:

	void Bubble_sort(int[] array, int left, int right) {
		int i = 0;
		boolean flag = true; // 用来标记一趟冒泡排序是否发生,初始为true
		while (left < right && flag == true) {
			flag = false;
			// 第一趟先把元素值最大的放在序列的最后面
			for (i = left; i < right; i++) {
				if (array[i] > array[i + 1]) { // 前一个元素的值大于后一个元素的值
					Swap(array, i, i + 1);
					flag = true; // 发生了元素交换,flag置为true
				}
			}
			right--; // 更新上界
			// 第二趟把元素值最小的放在序列的最前面
			for (i = right; i > left; i--) {
				if (array[i] < array[i - 1]) { // 后一个元素的值小于前一个元素的值
					Swap(array, i, i - 1);
					flag = true; // 发生了元素交换,flag置为true
				}
			}
			left++; // 更新下界
		}
	}

二、快速排序

2.1 基础的快速排序

  算法思想:先在待排序列中任取一个元素作为pivot基准值,通过一趟排序把待排序列分成两个部分:前一部分元素都小于基准值,后一部分元素都大于基准值。此时,pivot就被放到了最终位置上。然后,分别对两个字表重复上述步骤,直到每一个部分只有一个元素或者为空。
  一趟快速排序实际上是一个交替搜索与排序的过程。
  快排的时间效率与基准值划分的字表是否对称有关:当待排序列被划分为两个等长的子表时,排序的速度最快。
  当初始表基本有序或者基本逆序时,快排的时间效率最低(为一棵单支二叉树)

2.1.1 双指针法

基本步骤:
  1.把第一个元素取下来,作为pivot
  2.一个指针从尾开始找比pivot小的数
  3.另一个指针从头开始找比pivot大的数
  4.如果找到,就交换两个数
  5.最后,把pivot和尾指针指向的数交换(此时,pivot被放到其最终位置)
相关代码如下:

	//基于分治的思想
	int Partition(int[] array, int left, int right){
		int pivot = array[left];
		while(left<right){
			while(left<right && array[right]>=pivot){
				right--;
			}
			//找到了比pivot小的数,交换,放到前面
			Swap(array, right, left);
			while(left<right && array[left]<pivot){
				left++;
			}
			//找到了比pivot大的数,交换,放到后面
			Swap(array, left, right);
		}
		//找到了pivot的最终位置
		array[left] = pivot;
		return left;
	}

	void Qucik_sort(int[] array, int left, int right){
		if(left<right){
			//先找基准值
			int partition = Partition(array, left, right);
			//以基准值为界,依次对左右两个字表进行递归排序
			Qucik_sort(array, left, partition-1);
			Qucik_sort(array, partition+1, right);
		}
	}
	/*
	 * 快速排序时间复杂度:
	 * 最坏情况:O(n^2)   最好情况(有序时):O(nlogn)
	 * 空间复杂度:最大递归深度为n,最小递归深度为logn
	 * 最坏情况:O(n)   最好情况(有序时):O(nlogn)
	 * 稳定性:不稳定
	 */

2.1.2 挖坑法

基本步骤:
  1.把第一个元素的值pivot拿出来,把第一个位置当做坑
  2.一个指针从尾开始找比pivot小的数,找到以后,放在坑里,该指针所在的位置作为新的坑
  3.另一个指针从头开始找比pivot大的数,找到以后,放在坑里,该指针所在的位置作为新的坑
  4.直到头尾指针相遇
  5.然后把第一个数据放在最后一个坑里
相关代码如下:

	int Partition1(int[] array, int left, int right) {
		int pivot = array[left]; // 取第一个元素作为坑
		while (left < right) {
			//从尾开始找比pivot小的数,找到以后,放在坑里,该指针所在的位置作为新的坑
			while (array[right] >= pivot && left < right) {
				right--;
			}
			array[left] = array[right];
			//从头开始找比pivot大的数,找到以后,放在坑里,该指针所在的位置作为新的坑
			while (array[left] < pivot && left < right) {
				left++;
			}
			array[right] = array[left];
		}
		// 把第一个数据放在最后一个坑里
		array[left] = pivot;
		return left;
	}

2.1.3 前后指针法

基本步骤:
  1.取第一个元素的值pivot,让prev指向第一个数据,让cur指向第二个数据
  2.让cur从第二个数据开始依次遍历每一个数据,如果cur所指向的数据比pivot小,就把它和prev++所指向的数据交换(要保证prev++ != cur)
  3.最后,让prev所指向的数据和pivot交换
相关代码如下:

	int Partition2(int[] array, int left, int right) {
		// 取第一个元素为pivot,让prev指向第一个数据,让cur指向第二个数据
		int pivot = array[left];
		int prev = left;
		int cur = left + 1;
		// 让cur依次遍历每一个元素
		while (cur <= right) {
			if (array[cur] <= pivot && prev++ != cur) {
				Swap(array, cur, prev);
			}
			++cur;
		}
		Swap(array, prev, left);
		return prev;
	}

2.2 快排的优化(在一定范围内使用直接插入排序)

	void Qucik_sort2(int[] array, int left, int right){
		if(left >= right)
			return;
		//优化方式一:在right-left+1<=100范围里,使用直接插入排序
		if(right-left+1 <=  100){
			Insert_sort(array, left, right);
		}
		if(left < right){
			int partition = Partition_1(array, left, right);
			Qucik_sort2(array, left, partition-1);
			Qucik_sort2(array, partition+1, right);
		}
	}

2.3 快排的优化(使用三数取中法确定基准值)

  基本思想:利用三数取中法,把中间的元素换到最左边(相当于传统划分方法中的第一个位置上的元素),一般是在基本有序的范围内使用。
即,实现array[mid]<=array[left]<=array[right]的效果。
  相关代码如下:

	void three_num_mid(int[] array, int left, int right){
		int mid = (left+right)/2;
		//mid下标的为中间元素,使其放到第一个位置,作为基准值
		if(array[mid] > array[left]){
			Swap(array, mid, left);
		}
		if(array[mid] > array[right]){
			Swap(array, mid, right);
		}
		if(array[left] > array[right]){
			Swap(array, left, right);
		}
	}
	
	void Quick_sort3(int[] array, int left, int right) {
		if (left >= right)
			return;
		if (right - left + 1 <= 100) {
			Insert_sort(array, left, right);
		}
		three_num_mid(array, left, right);
		int partition = Partition_1(array, left, right);
		Quick_sort3(array, left, partition - 1);
		Quick_sort3(array, partition + 1, right);
	}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值