我看的是Princeton 的Algorithms 4th,在快速排序这一章,partition部分的比较数到底是N-1还是N还是N+1啊?有人教教我怎么算吗?
或者有人帮忙证明一下快速排序的比较数为什么约等于2NlnN?
“Devil组”引证GPT后的撰写:
下面是快速排序平均比较次数约为2NlnN的证明概述:
因此,快速排序的比较次数可以表示为:
C(N) = 2NlogN + 2N + 2N + ... + 2 = 2NlnN + O(N)
参考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)。
建议多玩下leetcode, https://leetcode.cn/problemset/all/
回答引自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。
您可以看看:https://blog.csdn.net/gfeng_ict/article/details/5778778
参考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。
回答不易,还请采纳!!!
以下答案由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)
如果我的回答解决了您的问题,请采纳!
快速排序的比较数分析比较复杂,需要分别分析最坏情况和平均情况。最坏情况下,每次划分的基准元素都是当前子数组中的最大或最小元素,这时每次划分只能减少一个元素,需要进行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。
需要注意的是,这只是快排比较次数的平均值,实际比较次数可能会更多或更少,取决于初始数组的顺序和选取的枢轴元素。
快速排序的partition部分的比较数是N-1,因为在partition过程中,你要比较每个元素和pivot,但是最后一个元素不用比较,因为它已经在partition之前就位于正确的位置上了。
关于快速排序的比较数为什么约等于2NlnN,这是因为快速排序的时间复杂度是O(NlogN),而比较数是时间复杂度的上界,所以比较数也是O(NlogN),由于快速排序是一个平均时间复杂度,所以比较数也是平均时间复杂度,平均时间复杂度的最优值是2NlnN,所以比较数的最优值也是2NlnN。