Day2 分治法——快速排序

Day2 分治法——快速排序

十大经典算法总结
https://www.cnblogs.com/onepixel/articles/7674659

一、思想:分治,递归

序位,前后位置——下标
元素的值:关键字,数字的大小

  • 分治:以一个关键字(key)为基准(basic)分割数据在前后两边,小于等于basic的在basic前,大于等于basic的在后

    这个basic可以随便取,通常可以取左边界元素的值

  • 多次分割,完成排序(可利用递归)

快速排序,效率很高。

图示说明

(和具体操作一起看,那里的一些结论直接画在图上了)

在这里插入图片描述

在这里插入图片描述

二、具体操作
(一)思路

Basic,分割后的数组左右边界lr(下标),动的标记i,j(下标)

  • 一次分割:
    一部分:找到两边“位置”不对的数,进行交换完成分割(先规定元素等于basic时,是“位置正确”。这样会有问题在这里先忽略不详细讨论)

    做标记l从最左边向右移动,直到 i 所指的数大于basic的数时,说明他位置不对,需要交换,i停止右移。
    再从右向左找小于basic,位置不对需要交换的数,即r开始从最右边向左移动,直到j所指的数小于basic停止。
    此时,交换i,j当前所指的元素。

    二部分: 多次进行上述移动&交换操作,遍历到所有元素 完成一次分割

  • 运用递归法,将分割好的两部分各进行上述分割操作。

(二)思考
  • 一部分
    1.左右移动的循环:
    循环条件:什么时候停止左右移动?找到不符合条件的。“停止”不是循环,“左右移动”才是循环,所以左右移动的循环条件是“位置正确”。
    2.j移动在i停止移动之后
    3.什么时候交换?当j停止移动时。
    i移动的循环,j移动的循环,交换,三个按顺序进行,顺序结构。

  • 二部分
    完成分割需要进行多次“一部分”的整体,又是一个循环。
    循环条件:什么时候完成一次分割?要遍历当前边界的所有元素,所以i一定要等于j或者超过j。那么现在考虑i能否超过j。
    1)j 后面位置:由于j是从右往左,“位置对”才会移动,所以j后面的一定“位置对”,即大于等于basic。
    2)再来考虑j位置:
    i 停止右移动,j才会左移动。
    i 移动时,一定完成了上一次交换,或者j在边界R处一次没动过。
    情况1:若果是完成了上一次交换,那么当前 j 位置一定正确,即大于等于basic。
    情况2:若是j在边界R处一次没动过,说明没有和basic比较过,则有位置不对的可能,即小于basic。
    3)综上,从j到j后中,只有j 处在情况2时才有可能位置不对(即小于basic)。
    4)i右移动时,“位置对”才右移动。
    情况1时,i最多移动到i=j;
    情况2时,j=r,j小于basic时,i可能加到r+1处。防止出现这种情况while循环条件加上i<r,防止溢出。

    所以一次分割结束时,i=j。i=j,是一次分割完成的标志。
    但“结束”不是循环,i 不断变大最大值是 j ,所以循环条件是 i < j 。

  • 一个问题:等于basic是否判断为“位置正确”?

    如果i,j移动时等于basic都代表位置正确,那么如数组8 2 1 4 3 0,取左边界元素8为basic,那么i会因为一直“位置”正确向右移动,直到i=j;那么这样一直不会分割。
    经过思考,我们会发现,等于basic对于判断 i 是否移动时,应算作位置不正确,停止移动,对于 j 应该算作位置正确,继续移动。

  • 递归部分

  1. 建立分割的这个函数,这个需要在递归中多次用到的函数,可以说他是一个分割函数,一次分割,通常说他是一次“快排”,有人将他命名为quik_sort()
  2. 形参表我们要找这个函数里面的变量,形参表需要传递的数
    1)想一想:每一次递归需要更新的部分?
    每一次都只对分割后的数组进行再次分割,其实需要传递新的左右边界
    2)再想一想新的边界是什么?
    分割后的左边部分,需要更新右边界。
    分割后的右边部分,需要更新左边界。
    它们其实都在上次分割的分割点(i=j)处,新的右边界是分割点-1,左边界是分割点。
(三)代码

问题代码:

#include <iostream>
using namespace std;
void quick_sort(int q[],int l,int r){
	int i,j,basic;
	i=l;j=r;basic=q[l];
	while(i<j){
		while(q[i]<basic&&i<=r)i++;
		while(q[j]>=basic&&i<j)j--;
		/*防止j左移动多i:当i移动到等于j时由于上面呢个while控制最后一
		定会停在j这里,若不加个这个条件,此时j若不满足条件可能左移动越过i,
		交换已正确拍的部分*/
		swap(q[i],q[j]); 	
	}//完成一次基本分割 
	quick_sort(q,l,j-1);//左半边再分割 
	/*一直执行这句到不了下一句为啥??????? */ 
	quick_sort(q,j,r);//右半边再分割 
}
int main(){ 
	int q[100],n;
	cin>>n;
	for(int i=0;i<n;i++)cin>>q[i];
	quick_sort(q,0,n-1);
	for(int i=0;i<n;i++)cout<<q[i];
	return 0;
} 
void swap(int a,int b){
	int c;
	c=a;
	a=b;
	b=a;
}

改正后代码,依然有问题。

#include <iostream>
using namespace std;
void quick_sort(int q[],int l,int r){
	int i,j,basic;
	i=l;j=r;basic=q[l];
	while(i<j){
		while(q[i]<basic&&i<=r)i++;
		while(q[j]>=basic&&i<j)j--;
		swap(q[i],q[j]); 	
	}//完成一次基本分割 
	if(l<j-1) quick_sort(q,l,j-1);//左半边再分割 
	/*解决方案:增加左边界比有边界小这个条件 */ 
	if(j<r)quick_sort(q,j,r);//右半边再分割 
	/*依然有问题:进行到这里r值不是整体边界了,左边分割时将j-1不断传给r改变了r值了,现在的r值是左半边的有边界了,但是这里需要的是整体的右边界
	怎么解决?快排学不会了 */
	
}
int main(){ 
	int q[100],n;
	cin>>n;
	for(int i=0;i<n;i++)cin>>q[i];
	quick_sort(q,0,n-1);
	for(int i=0;i<n;i++)cout<<q[i];
	return 0;
} 
void swap(int a,int b){
	int c;
	c=a;
	a=b;
	b=a;
}

提供的模板方法代码

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);
}

作者:yxc
链接:https://www.acwing.com/blog/content/277/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
发布了20 篇原创文章 · 获赞 0 · 访问量 710

猜你喜欢

转载自blog.csdn.net/m0_37733257/article/details/101993879
今日推荐