快速排序的分析与优化

news/2024/5/20 0:30:21 标签: 快速排序, 数据结构

一、快速排序的介绍

快速排序是一种排序算法,对包含n个数的输入数组,最坏的情况运行时间为Θ(n2)[Θ 读作theta]。虽然这个最坏情况的运行时间比较差,但快速排序通常是用于排序的最佳的实用选择。这是因为其平均情况下的性能相当好:期望的运行时间为 Θ(nlgn),且Θ(nlgn)记号中隐含的常数因子很小。另外,它还能够进行就地排序,在虚拟内存环境中也能很好的工作。

和归并排序一样,快速排序也是基于分治法(Divide and conquer):

  • 分解:数组A[p..r]被划分成两个(可能为空)的子数组A[p..q-1]和A[q+1..r],使得A[p..q-1]中的每个元素都小于等于A[q],A[q+1..r]中的每个元素都大于等于A[q]。这样元素A[q]就位于其最终位置上了。

  • 解决:通过递归调用快速排序,对子数组A[p..q-1]和A[q+1..r]排序。

  • 合并:因为两个子数组是就地排序,不需要合并,整个数组已有序。

伪代码

PARTITION(A, p, r)
    x = A[p]
    i = p
    for j=p+1 to r
        do if A[j] <= x
            then i = i+1
                 exchange(A[i],A[j])
    exchange(A[p], A[i])
    return i

QUICKSORT(A, p, r)
    if p < r
        then q = PARTITION(A, p, r)
             QUICKSORT(A, p, q-1)
             QUICKSORT(A, q+1, r)

二、性能分析

1、最坏情况

快速排序的最坏情况发生在当数组已经有序或者逆序排好的情况下。这样的话划分过程产生的两个区域中有一个没有元素,另一个包含n-1个元素。此时算法的运行时间可以递归地表示为:T(n) = T(n-1)+T(0)+Θ(n),递归式的解为T(n)=Θ(n^2)。可以看出,快速排序算法最坏情况运行时间并不比插入排序的更好。

2、最好情况

如果我们足够幸运,在每次划分操作中做到最平衡的划分,即将数组划分为n/2:n/2。此时得到的递归式为T(n) = 2T(n/2)+Θ(n),根据主定理的情况二可得T(n)=Θ(nlgn)

3、平均情况

假设一:快排中的划分点非常偏斜,比如每次都将数组划分为1/10 : 9/10的两个子区域,这种情况下运行时间是多少呢?运行时间递归式为T(n) = T(n/10)+T(9n/10)+Θ(n),使用递归树解得T(n)=Θ(nlgn)。可以看出,当划分点非常偏斜的时候,运行时间仍然是Θ(nlgn)。

假设二:Partition所产生的划分既有“好的”,也有“差的”,它们交替出现。这种平均情况下运行时间又是多少呢?这时的递归式为(G表示Good,B表示Bad):

G(n) = 2B(n/2) + Θ(n)

B(n) = G(n-1) + Θ(n)

解:G(n) = 2(G(n/2-1) + Θ(n/2)) + Θ(n) = 2G(n/2-1) + Θ(n) = Θ(nlgn)

可以看出,当好、差划分交替出现时,快排的运行时间就如全是好的划分一样,仍然是Θ(nlgn) 。

三、快排的优化

经过上面的分析可以知道,在输入有序或逆序时快速排序很慢,在其余情况则表现良好。如果输入本身已被排序,那么就糟了。那么我们如何确保对于所有输 入,它均能够获得较好的平均情况性能呢?前面的快速排序我们默认使用数组中第一个元素作为主元。假设随机选择数组中的元素作为主元,则快排的运行时间将不 依赖于输入序列的顺序。我们把随机选择主元快速排序叫做Randomized Quicksort。

在随机化的快速排序中,我们不是始终选择第一个元素作为主元,而是从数组A[p…r]中随机选择一个元素,然后将其与第一个元素交换。由于主元元素是随机选择的,我们期望在平均情况下,对输入数组的划分能够比较对称。

伪代码

RANDOMIZED-PARTITION(A, p, r)
    i = RANDOM(p, r)
    exchange(A[p], A[i])
    return PARTITION(A, p, r)

RANDOMIZED-QUICKSORT(A, p, r)
    if p < r
        then q = RANDOMIZED-PARTITION(A, p, r)
            RANDOMIZED-QUICKSORT(A, p, q-1)
            RANDOMIZED-QUICKSORT(A, q+1, r)

我们对3万个元素的有序序列分别进行传统的快速排序随机化的快速排序,并比较它们的运行时间:

/*************************************************************************
    > File Name: QuickSort.cpp
    > Author: SongLee
    > E-mail: lisong.shine@qq.com
    > Created Time: 2014年06月21日 星期六 10时11分30秒
    > Personal Blog: http://songlee24.github.com
 ************************************************************************/
#include<iostream>
#include<cstdlib>  // srand rand
#include<ctime>  // clock_t clock
using namespace std;

void swap(int &a, int &b)
{
    int tmp = a;
    a = b;
    b = tmp;
}

// 传统划分操作
int Partition(int A[], int low, int high)
{
    int pivot = A[low];
    int i = low;
    for(int j=low+1; j<=high; ++j)
    {
        if(A[j] <= pivot)
        {
            ++i;
            swap(A[i], A[j]);
        }
    }
    swap(A[i], A[low]);
    return i;
}

// 随机化划分操作,随机选择pivot
int Partition_Random(int A[], int low, int high)
{
    srand(time(NULL));
    int i = rand() % (high+1);
    swap(A[low], A[i]);
    return Partition(A, low, high);
}

// 传统快排
void QuickSort(int A[], int low, int high)
{
    if(low < high)
    {
        int pos = Partition(A, low, high);
        QuickSort(A, low, pos-1);
        QuickSort(A, pos+1, high);
    }
}

// 随机化快速排序
void QuickSort_Random(int A[], int low, int high)
{
    if(low < high)
    {
        int pos = Partition_Random(A, low, high);
        QuickSort_Random(A, low, pos-1);
        QuickSort_Random(A, pos+1, high);
    }
}

int main()
{
    clock_t t1, t2;
    // 初始化数组
    int A[30000];
    for(int i=0; i<30000; ++i)
        A[i] = i+1;
        
    t1 = clock();
    QuickSort(A, 0, 30000-1);
    t1 = clock() - t1;
    cout << "Traditional quicksort took "<< t1 << " clicks(about " << ((float)t1)/CLOCKS_PER_SEC << " seconds)." << endl;

    t2 = clock();
    QuickSort_Random(A, 0, 30000-1);
    t2 = clock() - t2;
    cout << "Randomized quicksort took "<< t2 << " clicks(about " << ((float)t2)/CLOCKS_PER_SEC << " seconds)." << endl;

    return 0;
}

运行结果:

[songlee@localhost ~]$ ./QuickSort 
Traditional quicksort took 1210309 clicks(about 1.21031 seconds).
Randomized quicksort took 457573 clicks(about 0.457573 seconds).
[songlee@localhost ~]$ ./QuickSort 
Traditional quicksort took 1208038 clicks(about 1.20804 seconds).
Randomized quicksort took 644950 clicks(about 0.64495 seconds).
从运行结果可以看出,对于有序的输入,随机化版本的快速排序的效率会高很多。

问题记录:

我们知道交换两个变量的值有以下三种方法:

int tmp = a;  // 方法一
a = b;
b = tmp

a = a+b;  // 方法二
b = a-b;
a = a-b;

a = a^b;  // 方法三
b = a^b;
a = a^b;

但是你会发现在本程序中,如果swap函数使用后面两种方法会出错。由于方法二和方法三没有使用中间变量,它们交换值的原理是直接对变量的内存单元进行操作。如果两个变量对应的同一内存单元,则经过两次加减或异或操作,内存单元的值已经变为了0,因而不能实现变量值交换。所以当需要交换值的变量可能是同一变量时,必须使用第三变量实现交换,否则会对变量清零。



http://www.niftyadmin.cn/n/1092404.html

相关文章

web(八)CSS选择器

标签选择器 使用html标签筛选需要渲染的网页元素。 使用场景 修改标签的默认样式&#xff0c;例如ul li 有默认的内边距&#xff0c;开发时应去掉标签的默认样式。 设定全局字体样式。 根据分辨率设定html标签的默认字体。 div {/*直接用标签作为选择器*/background-color: …

IntelliJ IDEA 2016.2 配置Tomcat 运行Web项目

以前都用MyEclipse写程序的 突然用了IDEA各种不习惯的说 借鉴了很多网上好的配置办法&#xff0c;感谢各位大神~ 前期准备 IDEA、JDK、Tomcat请先在自己电脑上装好 好么~ 博客图片为主 请多看红框框 开始 1.创建、配置项目 1.1创建项目 New Project - 【next】 1.2 给你的…

线性时间的排序算法

前面已经介绍了几种排序算法&#xff0c;像插入排序&#xff08;直接插入排序&#xff0c;折半插入排序&#xff0c;希尔排序&#xff09;、交换排序&#xff08;冒泡排序&#xff0c;快速排序&#xff09;、选择排序&#xff08;简单选择排序&#xff0c;堆排序&#xff09;、…

Java环境安装

访问网址&#xff1a; http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 下载适合的jdk版本 第一步&#xff0c;上传文件到服务器&#xff0c; 第二部&#xff0c;解压并拷贝到指定目录&#xff0c;一般安装到/usr/local 目录下面 tar -z…

Clojure学习笔记(一)——介绍、安装和语法

什么是Clojure Clojure是一种动态的、强类型的、寄居在JVM上的语言。 Clojure的特性&#xff1a; 函数式编程基础&#xff0c;包括一套性能可以和典型可变数据结构媲美的持久性数据结构由JVM提供的成熟的、高效的运行时环境&#xff1a;所以Clojure可以使用Java类库&#xff0c…

1-逻辑题一

《一》 1、有1000瓶酒&#xff0c;其中只有一瓶有毒。现在用小白鼠进行实验&#xff0c;小白鼠只要服用任意量有毒酒就会在24小时内死亡。问最少要用多少只小白鼠进行实验 才能在24小时内检测出哪瓶药水有毒&#xff1f; 这是一个二进制的问题&#xff0c;答案是用10只就…

Android下的串口通信实战之电子秤交互

本文为博主原创文章&#xff0c;未经博主允许不得转载。如有问题&#xff0c;请与我联系( QQ&#xff1a;3290985311)朱小姐。 本篇实战是在Android下的串口通信实战之控制客显的基础上修改的。 上一篇的实战demo只用了Android设备向硬件发送指令。但是串口通信是双向通信&…

第K顺序统计量的求解

一个n个元素组成的集合中&#xff0c;第K个顺序统计量&#xff08;Order Statistic&#xff09;指的是该集合中第K小的元素&#xff0c;我们要讨论的是如何在线性时间&#xff08;linear time&#xff09;里找出一个数组的第K个顺序统计量。 一、问题描述 问题&#xff1a;给…