Notes of Reading Introduction to Algorithm —— Quicksort

Just as Mergesort, Quicksort also applies Divide and Conquer. But in some cases, the time to call Quicksort can be Θ ( n 2 ) \Theta (n^2) Θ(n2), just like Insertionsort, which is different from Mergesort. In most cases, however, the time to call Quicksort is Θ ( n lg ⁡ n ) \Theta (n\lg n) Θ(nlgn), and the constant factor is smaller. Moreover, it also has the advantage of sorting in place.

Partition

First, we should find a pivot. In the code below, we choose the last element to be the pivot. However, in the next part, we will randomize the partition, which will choose the pivot at random.

After choosing the pivot, we will divide the array into three parts. In part 1, all the elements are larger than or equal to the pivot. In part 2, all the elements are smaller than the pivot. Part 3 is just the pivot.

int Partition(int *A, int p, int r)
{
    int i = p - 1, q = p;
    for (; q < r; q++)
    {
    	if (A[q] <= A[r])
    	{
	        i++;
        	int temp;
        	temp = A[q]; A[q] = A[i]; A[i] = temp;
    	}
    }
    i++;
    int temp;
    temp = A[r]; A[r] = A[i]; A[i] = temp;
    return i;
}

Obviously, the time to call Partition is Θ ( n ) \Theta(n) Θ(n), n = r − p + 1 n = r-p+1 n=rp+1.

Divide & Conquer

The array has been divide into three parts, then we “conquer” part 1 and part 2. As the elements are just in place, we needn’t combine.

void Quicksort(int *A, int p, int r)
{
    if (p < r)
    {
        int q = Partition(A, p, r);
        Quicksort(A, p, q-1);
        Quicksort(A, q+1, r);
    }
}

Performance of Quicksort

The time to call the Quicksort depends on the input array.

The Worst Case

Just think about this case, the numbers in the array are sorted, like 0, 1, 2, 3, … .

In this case, T ( n ) = T ( n − 1 ) + Θ ( n ) T(n)=T(n-1) + \Theta (n) T(n)=T(n1)+Θ(n). We can work out that the time complexity is Θ ( n 2 ) \Theta(n^2) Θ(n2). In this case, the time complexity of Insertionsort is only Θ ( n ) \Theta(n) Θ(n).

The Best Case

The best case turns up when part 1 and part 2 have the same number of elements for every partition. At this time, T ( n ) < 2 T ( n 2 ) + Θ ( n ) T(n)<2T(\frac{n}{2})+\Theta(n) T(n)<2T(2n)+Θ(n). Therefore, the time complexity is Θ ( n lg ⁡ n ) \Theta(n\lg n) Θ(nlgn).

Analysis

In most cases, the time to call Quicksort is much closer to the best case other than the worst case. The two pictures below can help us to understand this.
Picture 1

Picture 2

Picture 1 shows that the unbalanced cases also have the time complexity of O ( n lg ⁡ n ) O(n\lg n) O(nlgn). Picture 2 shows that the worst case plus the best case has the same time complexity with the best case.

Randomized Version of Quicksort

In this version, we will choose the pivot at random. Then we have the reason to expect the split of the input to be balanced on average.

int RandomizedPartition(int *A, int p, int r)
{
    int i = rand() % (r - p + 1) + p;
    int temp;
    temp = A[i]; A[i] = A[r]; A[r] = temp;
    return Partition(A, p, r);
}

void RandomizedQuicksort(int *A, int p, int r)
{
    if (p < r)
    {
        int q = RandomizedPartition(A, p, r);
        RandomizedQuicksort(A, p, q-1);
        RandomizedQuicksort(A, q+1, r);
    }
}

Analysis of Quicksort

Obviously, the upper bound of Quicksort is O ( n ) O(n) O(n).

In the function of Quicksort, we need to call RandomizedQuicksort for at most n times. And in every RandomizedQuicksort we will call RandomizedPartition , and then the Partition . Therefore, the time we cost is related to how much time we will cost in the function Partition.

Moreover, the time of calling Partition is related to the times we compare a pair of numbers, because the loop in the function includes the comparison.

We define X i j = I X_{ij}=I Xij=I { z i z_i zi is compared to z j z_j zj} , and all the times we compare equals X = ∑ i = 1 n − 1 ∑ j = i + 1 n X i j X=\sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^{n}X_{ij} X=i=1n1j=i+1nXij. E ( X ) = E ( ∑ i = 1 n − 1 ∑ j = i + 1 n X i j ) = ∑ i = 1 n − 1 ∑ j = i + 1 n E ( X i j ) = ∑ i = 1 n − 1 ∑ j = i + 1 n Pr ⁡ E(X)=E(\sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^{n}X_{ij})=\sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^{n}E(X_{ij})=\sum\limits_{i=1}^{n-1}\sum\limits_{j=i+1}^{n}\Pr E(X)=E(i=1n1j=i+1nXij)=i=1n1j=i+1nE(Xij)=i=1n1j=i+1nPr{ z i z_i zi is compared to z j z_j zj} .

Pr ⁡ \Pr Pr{ z i z_i zi is compared to z j z_j zj} = = = Pr ⁡ \Pr Pr{ z i z_i zi or z j z_j zj is the first pivot to be chosen from the elements indexed from i i i to j j j} = 2 j − i + 1 =\frac{2}{j-i+1} =ji+12.Then we can work out that E ( X ) < O ( n lg ⁡ n ) E(X)<O(n\lg n) E(X)<O(nlgn).

In conclusion, the average time we call RandomizedQuicksort has an upper bound —— O ( n lg ⁡ n ) O(n\lg n) O(nlgn).

All my codes along with Heapsort are shown below:

To get the complete code please visit my repositories: github.com.

Reference: Introduction to Algorithm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值