算法札记:快速排序

快速排序

本质是分治,从某种程度上有点像动态规划,都是将问题转换为子问题

核心思路:将某一个序列重新排列,使得存在某个位置,其左小于等于自己,其右大于等于自己,然后对左右两段重复上述操作

看一下Deepseek给出的动态规划与分治的关系

动态规划与分治算法是算法设计中一对关系密切、却又有着本质区别的思想。它们都体现了"化大为小"的智慧,但处理子问题的方式截然不同。

下面从‌共同点‌和‌区别‌两个维度来梳理它们的关系。

一、两者的共同点
都采用问题分解策略‌:两者都将一个复杂的大问题拆分成若干个规模更小的子问题,通过求解子问题再合并出原问题的解。

都要求最优子结构性质‌:原问题的最优解必须包含子问题的最优解,这样才能保证通过子问题的最优解可以推导出全局最优解。

都存在递推关系‌:分治通过递归式层层分解,动态规划通过状态转移方程递推求解,本质上都是在描述问题之间的递推规律。

二、两者的核心区别
1. 子问题是否独立(最本质的差异)
表格
算法    子问题关系
分治算法    子问题‌相互独立‌,彼此之间没有重叠
动态规划    子问题‌相互重叠‌,不同的子问题可能包含公共的子子问题
分治‌的子问题各自为政,互不干扰。比如归并排序中,对左半数组的排序和对右半数组的排序是完全独立的两个操作,左半边的排序结果不会影响右半边。

动态规划‌的子问题则是一环套一环的,同一子问题可能被多次重复计算。比如计算斐波那契数列时,f(5)需要用到f(4)和f(3),而f(4)又需要用到f(3)和f(2),f(3)被重复计算了。如果放任这种重复,计算量会呈指数级增长。

2. 是否保存子问题的解
分治法‌:不刻意保存子问题的解,每次递归都重新计算,因为子问题本身就是独立不重叠的,没必要存储。
动态规划‌:必须用表格(数组、哈希表等)将已求解过的子问题结果保存下来,再遇到时直接查表取出,从而避免大量重复计算。这正是动态规划从指数复杂度降至多项式复杂度的关键。
3. 求解方向
分治法‌:通常是‌自顶向下‌的递归结构。先分解到底,再逐层合并返回。
动态规划‌:通常采用‌自底向上‌的迭代方式,从小问题推到大问题;也可以用带备忘录的自顶向下递归(记忆化搜索)。
三、一个生动的类比
用考学的例子来理解这个区别:

分治思想——高考‌:大目标是"高考取得好成绩",可以拆分成语文、数学、英语等各个科目来分别攻克。语文学得好不好,并不影响数学的学习成果,这些子问题是‌互相独立‌的,逐一解决后合并起来就能达成总目标。

动态规划——考博士‌:目标是"考上博士",需要拆分成上高中→上大学→考研→考博这一系列阶段。不上高中就不能上大学,不上大学就不能考研,每一个阶段的结果都会"传递"给下一阶段,子问题是‌有衔接、有状态转移‌的。而且每个阶段的"最优路径"依赖于上一阶段的成果,体现的就是状态递推关系。

四、从典型案例看差异
表格
维度    分治算法典型:归并排序    动态规划典型:斐波那契数列 / 打家劫舍
分解方式    将数组对半拆成两个子数组,各自排序    将n间房子的最优解拆成与n-1间、n-2间相关
子问题关系    左右子数组的排序完全独立    f(3)同时被f(4)和f(5)依赖,存在重叠
解的合并    用双指针将两个有序子数组合并成一个有序数组    通过状态转移方程(如 dp[i]=max(dp[i-1], dp[i-2]+nums[i]))递推
若不做记忆化    不影响结果,因为子问题不重复    会导致指数级重复计算,效率极低
五、总结
分治算法和动态规划的本质关系可以概括为:‌

两者都是通过组合子问题的解来求解原问题。区别在于,分治法的子问题是‌独立不重叠‌的,直接递归求解即可;动态规划的子问题是‌重叠‌的,必须通过保存子问题解的方式来避免重复计算。

在实际应用中,如果把动态规划的题目用纯分治的方式去做,往往会因为反复求解相同子问题而导致指数级别的时间复杂度,这也是为什么需要"动规"这一专门方法来应对这类问题。

如果你有具体的题目想对比分析,或者想进一步了解动态规划的状态转移方程怎么设计,随时可以继续聊。

void quick_sort(int q[],int l,int r){
    if (l>=r) return;
    int x=q[l+r>>1],i=l-1,j=r+1;
    while (i<j){
        do{
            i++;
        }while (q[i]<x);//First
        do{
            j--;
        }while (q[j]>x);
        if (i<j){
            swap(q[i],q[j]);
        }
    }
    quick_sort(q,l,j);//Second
    quick_sort(q,j+1,r);
}

快排写法千千万,此般写法不寻常

//First 为什么不取等?如果取等,带数据【5换行1 2 5 2 1】就爆栈了

//Second 能不能把j和j+1换成i-1和i?不可以,带数据【2换行1 2】爆栈

这个Second让我想到了二分,于是我明白了每当我MB时代【2换行1 2】数据试一下

变式

如果我只关心求第k小的数,我可以剪枝【 x+1>=k ? quick_sort(q,l,j) : quick_sort(q,j+1,r)】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值