1.快速排序

1.快速排序

算法思想时这样的:

1.每次选取第一个数为基准数;

2.然后使用“乾坤挪移大法”将大于和小于基准的元素分别放置于基准数两边;

3.继续分别对基准数两侧未排序的数据使用分治法进行细分处理,直至整个序列有序。对于下面待排序的数组:

image-20230627192325873

第一步:先选择第一个数163 为基准数,以163 为基准将小于它的数排在它前面,大于等于它的数排在其后,结果如下:

image-20230627192430251

具体排列数据的步骤如下:

1.确定163 为基准数后,先把163 从数组中取出来

image-20230627192552110

2.然后从最右端开始,查找小于基准数163 的数,找到162,将其移至空出来的元素中

image-20230627192628262

3.接下来,从最左边未处理的元素中从左至右扫描比基数163 大的数,将其移动至右侧空出来的元素中

image-20230627192710641

4.接下来,继续从最右边未处理的元素中从右至左扫描比基数163 小的数,将其移动至左侧空出来的元素中

image-20230627192750073

接下来再重复执行步骤3,171 执行右移

image-20230627192825040

重复执行步骤4,此时右边的值已经均大于基数,左边的值均已小于基数

image-20230627192857540

接下来我们将基数保存回黄色空格中

image-20230627192951381

第二步:采用分治法分别对基数左边和右边的部分运用第一步中的方法进行递归操作,直到整个数组变得有序,以左边的数组为例:

image-20230627193025790

选择162 为基数,运用“乾坤挪移大法”得到结果如下:

image-20230627193100190

以162 为界,把数组分成两个部分,此时,基数右侧已经没有数据,所以,接下来只要继续对左侧的数组分治处理即可,选择159 为基数,再次运用“乾坤挪移大法”得到结果如下:

image-20230627193141500

输入优化(避坑点)​

  • 当数据量较大(如 1e6)时,需用快速输入方式:
  • C++:优先用scanf,而非cin(若用cin需加ios::sync_with_stdio(false););
  • Java:必须用BufferedReader,避免Scanner(速度差 10-20 倍)。

排序算法:快速排序与归并排序

1.1 快速排序(快排):分治思想的经典应用

1.1.1 核心思想:分治(先分区,再递归排序)​

  • 本质:通过 “分界点” 将数组划分为两部分,左半部分≤分界值,右半部分≥分界值,再递归处理左右区间。

1.1.2 三步实现法​

步骤 1:确定分界点 X

  • 可选方式(无优劣,推荐 “中间值” 或 “随机”,避免极端情况):

  • 左边界:X = q[L];

  • 中间值:X = q[(L+R)/2];

  • 右边界:X = q[R];

  • 随机值:X = q[rand()%(R-L+1)+L]。

步骤 2:调整区间(核心难点)

  • 目标:让左区间所有数≤X,右区间所有数≥X。

  • 两种实现方式:

方式 1:暴力法(简单但占额外空间)
  1. 开两个辅助数组a[]和b[];
  2. 遍历原数组,<X 的数放入a,>X 的数放入b;$ q[l \sim r] =\begin{cases}q[i]< x, & x→a[] \q[i] > x, & x→b[]\end{cases} $
  3. 合并a和b,覆盖原数组(时间复杂度 O (n),空间复杂度 O (n))。
方式 2:双指针法(优雅无额外空间,推荐)
  1. 初始化指针:i = l-1(左指针,从左边界左侧开始),j = r+1(右指针,从右边界右侧开始);

  2. 移动指针:

    • i向右移,直到q[i] > X(此时i指向的数应放右区间);

    • j向左移,直到q[j] < X (此时j指向的数应放左区间);

  3. 交换q[i]和q[j],重复步骤 2,直到i >= j;

  4. 原理:任何时刻,i左侧所有数≤X,j右侧所有数≥X,最终实现区间划分。

  • 示例模拟(数组[3,1,2,3,5],x=3):
  1. 初始:i=-1,j=5;
  2. i右移到q[0]=3(>x,停止),j左移到q[3]=3(<x 不成立,停止);
  3. 交换q[0]和q[3](仍为 3,无变化),i=0,j=2;
  4. i右移到q[1]=1(≤x,继续)→q[2]=2(≤x,继续)→q[3]=3(>x,停止);
  5. j左移到q[2]=2(;此时i=3 > j=2,划分结束,左区间[3,1,2](≤3),右区间[3,5](≥3)。

步骤 3:递归处理左右区间

  • 左区间:quick_sort(q, l, j)(或i-1,需与指针初始化匹配);

  • 右区间:quick_sort(q, j+1, r)(或i);

  • 注意:递归终止条件 —— 当l >= r时(区间内无元素或仅 1 个元素,无需排序)。

1.1.3 快排代码模板(C++)

void quick_sort(int q[], int l, int r)
{
	//递归的终止情况
	if (l >= r) return;
	//选取分界线。这里选数组中间那个数
	int i = l - 1, j = r + 1, x = q[l + r >> 1];
	//划分成左右两个部分
	while (i < j)
	{
		do i++; while (q[i] < x);
		do j--; while (q[j] > x);
		if (i < j) swap(q[i], q[j]);
	}
	//对左右部分排序
	quick_sort(q, l, j), quick_sort(q, j + 1, r);
}

1.1.4 关键注意点(避坑!)​

  • 边界问题:若分界点选x = q[l],且区间为[2,1],会触发死循环(递归始终处理[2,1]),因此推荐选 “中间值” 或 “随机值”;

  • 稳定性:快排是不稳定排序(相同值的相对位置可能变化),若需稳定,可将元素存为二元组(值, 原下标),按 “值优先,下标其次” 排序;

  • 时间复杂度:平均 O (nlogn),最坏 O (n²)(极端分界点,如有序数组选左边界),但实际应用中快排效率极高。

边界问题
因为边界问题只有这两种组合,不能随意搭配

x不能取q[l]和q[l+r>>1];
quick_sort(q,l,i-1),quick_sort(q,i,r);
x不能取q[r]和q[(l+r+1)>>1];
quick_sort(q,l,j),quick_sort(q,j+1,r)

参考:ACWing

posted @ 2026-03-24 09:57  CodeMagicianT  阅读(8)  评论(2)    收藏  举报