快速排序的复杂度分析以及使用插入排序优化的快速排序

首先,对快速排序分析可知,在一个包含n个元素的数组上运行快速排序时,记总共的两个元素之间的比较次数为X,则快速排序的运行时间为 O(n+X) (算法导论第三版7.4.2节引理7.1)。因此,必须了解算法在什么时候对数组中的两个元素进行比较,什么时候不进行比较。
将数组A中的各个元素重新命名为 z1,z2,z3,...,zn ,其中 zi 是数组中第i小的元素。定义 Zij=zi,zi+1,zi+2,...,zj zi zj 之间(含i和j)的元素集合。定义指示器随机变量:

Xij=I{zizj}

则算法的总共的比较次数为:

X=n1i=1nj=i+1Xij

对上述式子两边取期望可得:

E[X]=E[n1i=1nj=i+1Xij]=n1i=1nj=i+1E[Xij]=n1i=1nj=i+1Pr{zizj}

通常,假设每个元素的值是互异的,因此,一旦一个满足 zi<x<zj 的主元被选择后, zi zj 就再也不可能被比较了,因为 zi zj 已经被划分到两个不同的划分中。然而,若是 zi Zij 中的其他所有元素之前被选为主元,那么 zi 将和 Zij 中除了它自身之外的所有元素进行比较。因此, zi zj 当且仅当 Zij 中第一个被选为主元的元素是 zi 或者 zj
接着计算这一事件发生的概率。在任意一个 Zij 内的元素被选择为主元之前,整个集合均在一个划分的同一个分区之中。因此,每个 Zij 中的元素被选为主元的概率相等。因为集合中有 ji+1 个元素,并且主元的选择是独立且随机的,所以任何元素首先被选为主元的概率为 1ji+1 。于是:

Pr{zizj}=Pr{zizj}=2ji+1

所以有:

E[X]=n1i=1nj=i+12ji+1

所以,可以有:

E[X]=n1i=1nj=i+12ji+1=n1i=1nik=12k+1<n1i=1nik=12k=n1i=1O(lgn)=O(nlgn)

由此可得,在输入元素均不相同的情况下,随机化的快速排序算法的期望运行时间是 O(nlgn)
对于上式,又有:

E[X]=n1i=1nj=i+12ji+1=n1i=1nik=12k+1n1i=1nik=122kn1i=1Ω(lgn)=Ω(nlgn)

所以,又有,随机化的快速排序算法的期望运行时间为 Ω(nlgn) 。由上可知,随机化的快速排序算法的期望运行时间为 Θ(nlgn)
当输入数据已经几乎有序时,插入排序的速度很快。在实际应用中,可以利用这一特点来提高快速排序的速度。当对一个长度小于k的子数组调用快速排序时,可以让它不做任何处理就返回。当上层快速排序调用返回后,对整个数组进行插入排序来完成排序过程。该算法的期望时间复杂度为 O(nk+nlg(n/k)) 。该算法的时间复杂度分析如下:
对于快速排序结束后的插入排序,假设每一小结的插入排序时间复杂度为O(k^2),则n/k个小结的时间复杂度为 nkO(k2)=O(nk)
对于随机的快速排序过程,时间复杂度是 O(nlg(n/k)) ,网上很多解答说这个利用指示器随机变量,然后计算j与i的距离大于k的均值即可,但是笔者认为,在划分过程中,j于i之间的距离小于k时的比较也可能出现,所以不可以单单这样计算,然而想不出正确解法,故留待之后解决。
C语言实现优化的快速排序如下:

void insertion_sort(int *source, int head, int tail) {
    if (head > tail) {
        fprintf(stderr, "Error head and tail\n");
        return;
    }
    int i = head + 1;
    int j = head;
    int temp;
    for (i = head + 1; i <= tail; i++) {
        temp = source[i];
        for (j = i - 1; j >= head && source[j] > temp; j--) {
            source[j + 1] = source[j];
        }
        source[j + 1] = temp;
    }
}

int randomized_partition(int *source, int head, int tail) {
    swap(source + rand() % (tail - head + 1) + head, source + tail);
    int x = source[tail];
    int i = head - 1, j = head;
    for (j = head; j < tail; j++) {
        if (source[j] <= x) {
            i++;
            swap(source + j, source + i);
        }
    }
    swap(source + tail, source + i + 1);
    return i + 1;
}

void limited_quick_sort(int *source, int head, int tail, int k) {
    if (tail - head >= k)
        return;
    int mid = randomized_partition(source, head, tail);
    limited_quick_sort(source, head, mid - 1, k);
    limited_quick_sort(source, mid + 1, tail, k);
}

void insertion_optimized_quick_sort(int *source, int head, int tail, int k) {
    limited_quick_sort(source, head, tail, k);
    insertion_sort(source, head, tail);
}

猜你喜欢

转载自blog.csdn.net/sinat_16709955/article/details/77099487
今日推荐