快速排序详细解说

一、基本思想(左右指针法)

1、单趟排序

1、把第一个元素命名为关键key,让begin指针指向首元素,end指针指向尾元素
2、end从左向右找小(小于key的元素),begin从右向左找大(大于key的元素)
3、交换end和begin指向的元素
4、end继续找小,begin继续找大,再交换。
5、直到end等于begin,就让key与begin指向的元素交换。

2、过程演示
在这里插入图片描述

不难发现,经过一趟排序后,key左边都是比它小的元素,key右边都是比它大的元素。所以key就排到了自己该有的位置
即单趟排就能排好一个元素(排好的元素就不用动了)。

若我们在key左边的数组再次用单趟排(选一个新key),在key右边的数组用单趟排,则就又能排好一个元素。

当我们不断递归下去,直到key左边和右边的数组元素只有一个时,此时key左边和右边都有序,整个数组就有序了。

在这里插入图片描述
3、具体代码

//左右指针法:1、end先找小  2、begin再找大  3、再交换两个数  4、end与begin相等时,key与begin值交换
void QuickSort2(int* arr, int left, int right) {

	if (left >= right)//递归到只有一个元素就返回
		return;
	//一次排序
	int begin = left, end = right;
	int key = begin;
	while (begin < end) {
		//end找小
		while (arr[key] <= arr[end] && begin < end) {
			end--;
		}

		//begin找大
		while (arr[key] >= arr[begin] && begin < end) {
			begin++;
		}

		//交换两个数
		int tmp = arr[begin];
		arr[begin] = arr[end];
		arr[end] = tmp;
	}
	//交换
	int cur = arr[key];
	arr[key] = arr[begin];
	arr[begin] = cur;

	//递归左子边,递归右子边
	//左边[left,pivot-1],pivot,[pivot+1,right]
	QuickSort2(arr, left, begin-1);
	QuickSort2(arr, begin+1, right);

}

二、挖坑法

1、基本思路

1、把第一个元素挖走放到key,第一个位置成坑
2、end(最后一个元素的指针)找小,找到后把元素扔到坑里,end处形成新的坑
3、begin(第一个元素的指针)找大,找到后把元素扔到坑里,begin处形成新的坑
4、不断地找小找大,当end等于begin时(此处为坑),把key值放到坑
5、递归左边,递归右边

2、具体代码

void QuickSort1(int* arr, int left, int right) {

	if (left >= right)
		return;
	//一次排序
	int begin = left, end = right;
	int pivot = left; 
	int key = arr[pivot];
	while (begin < end) {
		//end找小
		while(arr[end] >= key && begin < end) {
			end--;
		}
		//找到后扔到坑
		arr[pivot] = arr[end];
		pivot = end;
		//begin找大
		while(arr[begin] <= key && begin < end) {
			begin++;
		}
		//找到后扔到坑
		arr[pivot] = arr[begin];
		pivot = begin;
	}
	pivot = begin;
	arr[pivot] = key;
	//递归左子边,递归右子边
	//左边[left,pivot-1],pivot,[pivot+1,right]
	QuickSort1(arr, left, pivot - 1);
	QuickSort1(arr, pivot+1, right);

}

三、前后指针法

1、基本思路

1、定义一个前指针指向第一个元素,定义一个后指针指向前指针的后一个位置。
2、cur找小,遇到比keyi小的值就停下,并且prev指针++,交换两个指针的值
3、cur走到底后,把prev位置的值与keyi位置交换
4、递归左边,递归右边

2、具体代码

//前后指针法,cur找小,遇到比keyi小的值就停下,并且prev指针++,交换两个指针的值
// cur走到底后,把prev位置的值与keyi位置交换
void QuickSort3(int* arr, int left, int right) {

	if (left >= right)
		return;
	//一次排序
	int keyi = left;
	int prev = left, cur = prev + 1;

	while (cur <= right) {

		while (arr[cur] <= arr[keyi] && cur != prev) {
			prev++;
			int tmp = arr[cur];
			arr[cur] = arr[prev];
			arr[prev] = tmp;
		}
		cur++;
	}

	int t = arr[keyi];
	arr[keyi] = arr[prev];
	arr[prev] = t;

	//递归左子边,递归右子边
	//左边[left,pivot-1],pivot,[pivot+1,right]
	QuickSort3(arr, left, prev-1);
	QuickSort3(arr, prev+1, right);

}

四、非递归算法实现快排

1、基本思想

1、让要排序的元素都入栈
2、让所以元素出栈,对出栈元素进行一趟排序(排好一个元素key)。
3、若key右边还有两个以上元素,让key右边元素都入栈;若key左边还有两个以上元素,再让左边元素都入栈
4、让左边元素都出栈,进行一趟排序;让右边元素都出栈,进行一趟排序
5、不断循环,直到key左边和右边都只有一个元素,都不需要入栈;即栈为空,就退出循环

2、具体代码

//单趟排(挖坑法),排好一个元素
int PartSort(int* arr, int left, int right) {
	if (left >= right)
		return left;
	//一次排序
	int begin = left, end = right;
	int pivot = left;
	int key = arr[pivot];
	while (begin < end) {

		while (arr[end] >= key && begin < end) {
			end--;
		}
		arr[pivot] = arr[end];
		pivot = end;

		while (arr[begin] <= key && begin < end) {
			begin++;
		}
		arr[pivot] = arr[begin];
		pivot = begin;
	}
	pivot = begin;
	arr[pivot] = key;
	return pivot;
}

void QuickSortNotR(int* arr, int n) {
	Stack s;
	InitStack(&s);
	//头尾指针入栈,相当与所以数据都入栈
	PushStack(&s, n - 1);
	PushStack(&s, 0);

	while (s.top != 0) {
		//左先出栈,右再出栈   (有数据出栈就需进行排序操作)
		int left = TopStack(&s);
		PopStack(&s);//出栈
		int right = TopStack(&s);
		PopStack(&s);
		//单趟排序排好一个坑位
		int pivot = PartSort(arr, left, right);
		//[left,pivot-1],pivot,[pivot+1,right]
		if (pivot + 1 < right) { //若成立,说明坑右边还有多个数据,需要进一步排序
			PushStack(&s, right);
			PushStack(&s, pivot+1);
		}
		if (left < pivot - 1) {
			PushStack(&s, pivot - 1);
			PushStack(&s, left);
		}
	}
}

五、时间复杂度

T=单趟所用时间*排序趟数

1、单趟排序所用时间(以左右指针法为列)
当左右指针相遇时,排序停止,所以用时N

2、排序趟数
第一趟排好一个元素
递归左边,右边;选出两个key
第二趟排好两个元素(一共排好了3个元素,把数组分成了四段)
选出四个key
第三趟排好四个元素
。。。。。
所以排序趟数为logN

快排时间复杂度为N*logN

  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Activity 是 Android 应用程序的基本组成部分之一,它负责展示用户界面和处理与用户的交互。Activity 可以看作是一个具有生命周期的窗口,它可以接收来自系统或用户的事件并作出响应。 Activity 的主要作用是管理用户界面和应用程序状态,它可以通过 setContentView() 方法加载布局文件并显示用户界面,同时也可以实现用户与应用程序之间的通信和数据交互。例如,当用户点击按钮或输入文本时,Activity 可以响应这些事件并执行相应的操作。 Activity 还可以通过 Intent 启动其他 Activity,实现多个界面的切换和交互。在启动另一个 Activity 时,当前 Activity 可以传递一些数据给被启动的 Activity,也可以接收被启动的 Activity 返回的数据。 Activity 生命周期包括以下几个阶段: 1. onCreate():在 Activity 创建时被调用,用于初始化界面和数据。 2. onStart():在 Activity 可见但没有获取焦点时被调用,用于执行一些初始化操作。 3. onResume():在 Activity 可见并获取焦点时被调用,用于开始执行程序逻辑。 4. onPause():在 Activity 失去焦点但仍可见时被调用,用于释放资源和保存数据。 5. onStop():在 Activity 不再可见时被调用,用于释放资源和保存数据。 6. onDestroy():在 Activity 被销毁时被调用,用于释放资源和清理数据。 Activity 生命周期中的每个阶段都提供了一些回调方法,可以在这些方法中执行一些操作,例如初始化界面、加载数据、保存状态等。 总的来说,Activity 是 Android 应用程序的基本组成部分之一,它负责展示用户界面和处理与用户的交互,通过生命周期管理应用程序状态,实现多个界面的切换和交互。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值