come and bite me 2023-03-05 01:44 采纳率: 100%
浏览 101
已结题

关于快速排序算法的问题

我看的是Princeton 的Algorithms 4th,在快速排序这一章,partition部分的比较数到底是N-1还是N还是N+1啊?有人教教我怎么算吗?
或者有人帮忙证明一下快速排序的比较数为什么约等于2NlnN?

  • 写回答

10条回答 默认 最新

  • 「已注销」 2023-03-05 13:16
    关注

    “Devil组”引证GPT后的撰写:

    • 快速排序的比较次数取决于分割(partition)步骤中基准元素的选择方法和分割时元素的排列情况。在最坏情况下,分割过程将分割数组为两个不均衡的部分,并且每次分割只会将一个元素移动到其最终的位置,因此比较次数将达到N-1。在最好的情况下,每次分割都能将数组均分成两部分,比较次数为NlogN。平均情况下,我们可以预期比较次数约为2NlnN。

    下面是快速排序平均比较次数约为2NlnN的证明概述:

    • 对于快速排序的平均比较次数进行分析,我们需要考虑随机地选择基准元素的影响。我们可以考虑一个假想的排序过程,其中我们将基准元素选择为随机的,每个元素被选中的概率相等。在这种情况下,我们期望在进行分割操作时,选中的基准元素将数组分成两个大小大致相等的部分。
    • 现在假设我们有一个大小为N的数组,我们要对其进行排序。为了方便,我们将N假定为2的幂,因此有N = 2^k。在第一轮分割之后,数组将被分成两个大小大致相等的子数组,每个大小为N/2。然后,我们需要对两个大小为N/2的子数组进行排序。
    • 对于子数组的排序,我们仍然采用随机选择基准元素的方法。由于基准元素的选择是随机的,因此我们期望将每个子数组分成大小相等的两个子数组。在这种情况下,我们需要进行2次比较,以将基准元素与子数组的剩余元素进行比较。然后,我们需要对四个大小为N/4的子数组进行排序,每个子数组需要进行2次比较。以此类推,直到我们对大小为1的子数组进行排序。

    因此,快速排序的比较次数可以表示为:

    C(N) = 2NlogN + 2N + 2N + ... + 2 = 2NlnN + O(N)
    
    
    
    • 其中,第一个项表示在主数组中进行的比较数,第二个项表示在子数组中进行的比较数,最后一项是一个小的修正项。因此,我们可以预期快速排序的比较次数约为2NlnN。这个证明仅仅是对快速排序的平均情况进行了分析,最坏情况下的比较次数可能达到N^2
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
  • 「已注销」 2023-03-05 04:08
    关注

    参考gpt和自己的思路,在快速排序的算法实现中,partition部分的比较次数为N-1,其中N为待排序数组的长度。

    快速排序的比较次数为约2NlnN,其中ln是自然对数。这个结果可以通过数学归纳法来证明。假设对于长度小于N的数组,快速排序的比较次数为约2NlnN。现在考虑对于长度为N的数组,如何证明快速排序的比较次数也是约2NlnN。

    在快速排序中,我们首先选择一个pivot元素,将数组分为左右两个子数组,其中左子数组的所有元素都小于pivot,右子数组的所有元素都大于pivot。然后对左右子数组递归地进行快速排序,直到整个数组有序。

    在partition部分,我们需要将数组中的元素与pivot进行比较,并将小于pivot的元素放到左子数组中,大于pivot的元素放到右子数组中。假设pivot在排序后的位置为k,则左子数组的长度为k-1,右子数组的长度为N-k。由于比较pivot元素需要一次比较,所以partition的比较次数为N-1。因此,我们可以将快速排序的比较次数分为以下两个部分:

    在partition部分进行的比较次数,约为N-1次。
    对左右子数组进行递归排序时,每个元素都会参与一次比较。因此,总的比较次数为左右子数组中元素比较次数之和。
    根据归纳假设,左右子数组的长度分别为k-1和N-k,因此比较次数分别为约2(k-1)ln(k-1)和约2(N-k)ln(N-k)。将两个式子相加,我们得到:

    2(k-1)ln(k-1) + 2(N-k)ln(N-k)

    = 2(k-1)ln(k-1) + 2NlnN - 2klnk - 2(N-k)ln(N-k) + 2klnk

    = 2NlnN - 2(N-k)ln(N-k) - 2(k-1)ln(k-1) + 2klnk

    = 2NlnN - 2(N-k)ln((N-k)/(k-1)) - 2(k-1)ln((k-1)/k) + 2klnk

    由于N-k和k-1都小于N,因此(N-k)/(k-1)和(k-1)/k都可以近似为1。因此,上面的式子可以近似为:

    2NlnN - 2klnk - 2(N-k)ln(N-k)

    约等于2NlnN,这就证明了快速排序的比较次数为约2NlnN。
    以下是一个简单的Java实现快速排序算法的示例代码:

    
    public class QuickSort {
    
        public static void sort(int[] arr, int low, int high) {
            if (low < high) {
                int pivotIndex = partition(arr, low, high);
                sort(arr, low, pivotIndex - 1);
                sort(arr, pivotIndex + 1, high);
            }
        }
    
        private static int partition(int[] arr, int low, int high) {
            int pivot = arr[low];
            int left = low;
            int right = high;
    
            while (left < right) {
                while (left < right && arr[right] >= pivot) {
                    right--;
                }
    
                if (left < right) {
                    arr[left] = arr[right];
                    left++;
                }
    
                while (left < right && arr[left] < pivot) {
                    left++;
                }
    
                if (left < right) {
                    arr[right] = arr[left];
                    right--;
                }
            }
    
            arr[left] = pivot;
            return left;
        }
    
        public static void main(String[] args) {
            int[] arr = {5, 2, 8, 4, 7, 1, 3, 9, 6};
            sort(arr, 0, arr.length - 1);
            for (int i : arr) {
                System.out.print(i + " ");
            }
        }
    }
    
    
    

    在这个示例中,sort方法接受要排序的数组和数组的开始和结束位置。它首先选择一个枢轴元素,并将数组分成两个部分:左边的元素小于枢轴,右边的元素大于枢轴。然后对左右两部分分别递归排序。

    partition方法选择第一个元素作为枢轴,将左右指针移动直到左指针大于右指针,然后交换左右指针所指向的元素。最后,将枢轴元素放置在它最终应该在的位置,并返回该位置。

    要回答您的另一个问题,关于比较次数的计算方法是 NlogN。但是,这个值并不是精确的,因为它只是平均比较次数的一个上限。事实上,在最坏情况下,比较次数可以达到 O(N^2)。

    评论
  • CSDN-Ada助手 CSDN-AI 官方账号 2023-03-05 06:31
    关注
    评论
  • _长银_ 2023-03-05 07:40
    关注

    建议多玩下leetcode, https://leetcode.cn/problemset/all/

    评论
  • 白驹_过隙 算法领域新星创作者 2023-03-05 08:54
    关注

    回答引自chatgpt
    在快速排序的partition过程中,比较次数的范围是从N-1到2N-1,具体取决于选取的枢轴元素的位置。如果每次都选择了中位数,则比较次数为2NlogN,但是这种情况并不一定总能出现。因此,快速排序的比较次数的期望值为约2NlnN。
    下面是一个简单的证明:
    假设快速排序中每个元素都有相等的概率被选为枢轴元素,我们可以利用期望值的线性性来计算比较次数的期望值。在每一次比较中,我们都会将一个元素与枢轴元素进行比较,因此总的比较次数可以表示为:
    T(n) = (n-1) + E[T(k)] + E[T(n-k-1)]
    其中,n为待排序的元素个数,k为枢轴元素左侧的元素个数,E[T(k)]为枢轴元素左侧元素的比较次数的期望值,E[T(n-k-1)]为枢轴元素右侧元素的比较次数的期望值。
    根据期望值的定义,我们可以将E[T(k)]和E[T(n-k-1)]表示为:
    E[T(k)] = 1/k * Σ(i=0,...,k-1) T(i)
    E[T(n-k-1)] = 1/(n-k-1) * Σ(i=k+1,...,n-1) T(i)
    将上面的两个等式代入T(n)中,得到:
    T(n) = (n-1) + 1/n * Σ(i=0,...,n-1) T(i)
    将上式两边同时乘以n,得到:
    nT(n) = n(n-1) + Σ(i=0,...,n-1) T(i)
    将上式两边同时减去(n-1)T(n-1),得到:
    nT(n) - (n-1)T(n-1) = 2n - 1
    移项并除以n(n-1),得到:
    T(n)/n - T(n-1)/(n-1) = 2/(n(n-1))
    对上式从n=2到n=N进行累加,得到:
    T(N)/N - T(1)/1 = 2 Σ(i=2,...,N) 1/(i(i-1))
    化简上式,得到:
    T(N) = 2N lnN + O(N)
    因此,快速排序的比较次数的期望值为约2NlnN。

    评论
  • 我爱OJ 2023-03-05 09:59
    关注
    评论
  • dahe0825 2023-03-05 11:13
    关注

    参考GPT的内容和自己的思路,在快速排序中,partition函数会将数组划分为两个部分,一部分小于划分元素,另一部分大于等于划分元素。假设数组长度为N,则划分元素在排序后的位置记为j,则划分元素前面的元素都小于它,后面的元素都大于等于它。为了找到这个位置j,我们需要进行N-1次比较,因为每次比较都会排除一个元素。因此,在Princeton的Algorithms 4th中,partition部分的比较数应该是N-1。

    快速排序的比较次数的计算比较复杂。可以用数学方法证明,快速排序平均比较次数为2NlnN,其中N是数组长度。

    下面是简单的证明:

    假设一个长度为N的数组进行快速排序时,最终的排序树的高度为H。

    根据定义,快速排序的基本操作是比较两个元素并交换它们的位置。在排序树上,每个比较都对应一条路径,因此我们可以计算出所有比较的总数。

    对于每个节点,它需要进行一次比较来划分左右子树,因此一共需要进行N-1次比较。对于深度为h的节点,它在排序树上的深度为h,因此需要进行h次比较。根据排序树的性质,深度为h的节点数不超过2^(h-1),因此深度为h的所有节点需要进行h * 2^(h-1)次比较。

    因此,所有比较的总数为:

    C = (N-1) + 2*H(1)*2^(H-2) + 2*H(2)*2^(H-3) + ... + 2*H(H-1)*2^0
    
    
    

    其中H(i)表示深度为i的节点数。注意到排序树是一个二叉树,因此节点数等于叶子节点数。叶子节点是排好序的元素,因此深度为i的节点数等于2^i。因此,

    H(i) = 2^(H-i)
    
    
    

    将上述式子带入C中,可以得到:

    C = (N-1) + 2*H(1)*2^(H-2) + 2*H(2)*2^(H-3) + ... + 2*H(H-1)*2^0
      = (N-1) + 2*2^(H-2)*2^(H-2) + 2*2^(H-3)*2^(H-3) + ... + 2*2^0*2^1
      = (N-1) + 2*2^(2H-4) + 2*2^(2H-6) + ... + 2*2
      = (N-1) + 2*(2^(2H-4) +2^(2H-6) + ... + 22
    = (N-1) + 2(2^(2H-2) - 2)
    = 2^(2H-1) + N - 3
    
    
    

    因为最终的排序树的叶子节点是排好序的,因此H <= N,因此有:

    C <= 2^(2N-1) + N - 3
    
    

    因此,平均比较次数为:

    C/N <= 2^(2N-1)/N + 1 - 3/N
    
    

    当N趋近于无穷大时,2^(2N-1)/N趋近于无穷大,因此平均比较次数趋近于2NlnN。

    因此,我们可以得出结论,快速排序的平均比较次数约等于2NlnN。

    回答不易,还请采纳!!!

    评论
  • 蓝白、波罗歌 2023-03-05 16:22
    关注

    以下答案由GPT-3.5大模型与博主波罗歌共同编写:
    快速排序的比较数取决于划分的方式。在经典的Hoare划分中,每次划分需要比较1个pivot元素和至少两个其他元素,因此比较次数为N-1。而在Sedgewick的改进版中,每次划分需要比较3个元素,因此比较次数为N。

    证明快速排序比较次数为约等于2NlnN,可以通过数学归纳法证明。

    当N=1时,显然比较次数为0。

    假设对于任意k<N的情况下,快速排序比较次数为约等于2klnk。

    当N=k+1时,最坏情况下比较次数为N-1+k次,其中k次是pivot元素和其他元素的比较次数。最优情况下比较次数为N-1+ceil(k/2)次。

    因此,总比较次数为 (2klnk+k) + (2(k+1)ln(k+1)-2k) / (k+1) <= 2(k+1)ln(k+1),即可证明。

    以下是快速排序的Python实现代码:

    def quick_sort(arr):
        if len(arr) <= 1:
            return arr
        pivot = arr[len(arr) // 2]
        left = [x for x in arr if x < pivot]
        middle = [x for x in arr if x == pivot]
        right = [x for x in arr if x > pivot]
        return quick_sort(left) + middle + quick_sort(right)
    

    如果我的回答解决了您的问题,请采纳!

    评论
  • _rtf 2023-03-06 16:59
    关注

    快速排序的比较数分析比较复杂,需要分别分析最坏情况和平均情况。最坏情况下,每次划分的基准元素都是当前子数组中的最大或最小元素,这时每次划分只能减少一个元素,需要进行n-1次划分,所以比较次数为N-1。而平均情况下,比较次数约为2NlnN。

    关于为什么快速排序的比较数约为2NlnN,这里提供一个简单的证明:

    假设数组大小为N,每次划分时,基准元素的选取是随机的,我们可以假设每个元素都有相同的概率成为基准元素。在划分之后,原来的数组被分为两个子数组,分别包含了比基准元素小和大的元素。假设在划分之后,小于等于基准元素的子数组大小为k,大于基准元素的子数组大小为N-k-1(注意,基准元素不在任何一个子数组中)。根据定义,k的取值范围为0到N-1。

    现在我们考虑一次划分的比较次数。每次划分需要比较N-1次,因为基准元素需要和其他元素比较一次,而基准元素不和自己比较,所以一次划分的比较次数为N-1。如果我们能计算出每种划分大小的概率,就可以得到平均比较次数。

    对于任意的k,小于等于基准元素的子数组大小为k的概率为1/(N+1),因为基准元素有N+1个可能的位置(包括在数组外)。大于基准元素的子数组大小为N-k-1,因此这种情况的概率也为1/(N+1)。因此,对于固定的k,划分大小为k和N-k-1的概率都是1/(N+1)。由于k的取值范围是0到N-1,因此我们可以得到所有可能的划分大小的概率分布:

    P(k) = 1/(N+1) (k=0,1,...,N-1)
    

    现在我们可以计算平均比较次数了。假设T(N)表示数组大小为N的快速排序的平均比较次数。那么,一次划分的平均比较次数为:

    E(N-1) = Σ[k=0 to N-1] P(k) * (N-1) = N-1
    

    接下来考虑递归的平均比较次数。设递归到大小为N的子数组时的比较次数为T(N),则有:

    T(N) = N-1 + (T(0) + T(N-1))/2 + (T(1) + T(N-2))/2 + ... + (T(N-1) + T(0))/2
    

    其中,第一项N-1是partition的比较次数,后面的项是左右子数组的平均比较次数,平均是因为快排可能划分得不均匀,所以要对左右子数组都进行平均。

    通过一些数学推导,可以得到:

    T(N) = 2(N-1)H(N) ≈ 2NlnN
    

    其中,H(N)为调和级数,约等于lnN。所以快速排序的比较次数约为2NlnN。

    需要注意的是,这只是快排比较次数的平均值,实际比较次数可能会更多或更少,取决于初始数组的顺序和选取的枢轴元素。

    评论 编辑记录
  • 实相无相 2023-03-06 17:11
    关注

    快速排序的partition部分的比较数是N-1,因为在partition过程中,你要比较每个元素和pivot,但是最后一个元素不用比较,因为它已经在partition之前就位于正确的位置上了。

    关于快速排序的比较数为什么约等于2NlnN,这是因为快速排序的时间复杂度是O(NlogN),而比较数是时间复杂度的上界,所以比较数也是O(NlogN),由于快速排序是一个平均时间复杂度,所以比较数也是平均时间复杂度,平均时间复杂度的最优值是2NlnN,所以比较数的最优值也是2NlnN。

    评论
查看更多回答(9条)

报告相同问题?

问题事件

  • 系统已结题 3月15日
  • 已采纳回答 3月7日
  • 创建了问题 3月5日

悬赏问题

  • ¥15 spyder运行重复
  • ¥15 有偿做一个PUBG识别枪械配合罗技宏的
  • ¥15 我考考你,这代码是对的还是错的?
  • ¥15 我用C语言easyx图形库绘制了一个3d游戏方框透视,但进入游戏时候鼠标准星对准方框边缘 鼠标光标就会弹出来这是啥情况怎样让光标对准绘制的方框点击鼠标不弹出光标好烦这样
  • ¥20 用Power Query整合的问题
  • ¥20 基于python进行多背包问题的多值编码
  • ¥15 相同型号电脑与配置,发现主板有一台貌似缺少了好多元器件似的,会影响稳定性和使用寿命吗?
  • ¥15 C语言:数据子序列基础版
  • ¥20 powerbulider 导入excel文件,显示不完整
  • ¥15 paddle训练自己的数据loss降不下去