算法导论------快速排序

当我们学习研究完各种开发语言,操作系统,软件设计等方面软件知识之后,发现程序设计的本质还是在算法设计上。算法设计的好坏直接影响程序性能。于是,最近学习绕了一圈之后又重新拾掇起程序的核心部分------------算法(程序=数据结构+算法),掌握常见的数据结构与算法再去研读优秀的开源项目(比如linux内核)时会做到游刃有余。算法中最基础的就是查找和排序。当然这里谈算法 是与数据结构紧密相连的 。数据结构无非是将现有的信息以一种特定的组织结构(比如线性表,队列,堆栈,树,图等)存储在计算机中,其目的也是为了将来方便查找(算法)。而如果将信息存储在指定的数据结构中之前排序的话,则使得将来 查找信息更为高效。排序是将现有的信息项按照特定规律(比如按照大小或者字典序等)存储于计算机中。排序算法目前常见的就有十多种,其时间复杂度从O(N*N)到O(N*lg(N))。信息的组织形式(数据结构)也是多种多样,线性表,二叉树等都是常用的数据存储结构。今天主要研究快速排序,快速排序是目前是最常用最实用的一种排序算法,在各大公司中都有使用。快速排序和合并排序一样,也是用了分治法思想,所不同的是,快速排序是原地数据交换,没有额外的辅助空间,这个称之为Patition,这也是快速排序的核心部分。其平均时间复杂度达到了 O(N*lg(N))。另外值得一提的是,算法中至少有80%的都可以体现递归的思想(循环也是一种特殊的递归而已)。许多数据结构或者算法其本身都是采用递归的方式描述,比如链式线性表,二叉树等。掌握好递归的思想对理解算法实现及其重要。下面是对一个典型子数组A[p..r]排序的分治过程的三个步骤:
数组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[q]。然后递归调用快速排序。下面是实现快速排序的伪代码:
QuickSort(A, p, r)
1 if p < r
2    then q ←Partition(A, p, r)
                QuickSort(A, p, q - 1)
                QuickSort(A, q+1, r)

其实快速排序算法的关键在于第一步Partition过程,它是对子数组 
A[p..r]进行就地重排:

Partition(A, p, r)
1 x← A[p] // 以第一个数据作为pivot element
2 i← p - 1
3 for j← p to r-1
4      do if A[j] ≦ x
5           then i←i + 1
6           exchange A[i]↔A[j]
7 exchange A[i+1]
↔A[p]
8 return i + 1

下面举个例子:
 

 
 经过一轮划分之后,比6小的子数组放在了左边,比6大的子数组放在了右边,接下来直接递归调用快速排序,一层层递归之后,数组会在原地排序好,不像合并排序那样,还需要线性的辅助空间来排列2个排序好的子数组。

由伪代码实现就比较简单了,下面是C语言代码实现:

#include <stdio.h>
void exchange(int &i, int &j)
{
    int temp = i;
    i = j;
    j = temp;
}
int Patition(int A[], int p, int r)
{
    int x = A[p];
    int i = p;
    for (int j = p + 1; j <= r; j++)
    {
        if (A[j] <= x)
        {
            i++;
            exchange(A[i], A[j]);
        }
    }
    exchange(A[i], A[p]);
    return i;
}
void QuickSort(int A[], int p, int r)
{
    if (p < r)
    {
        int q  = Patition(A, p, r);
        QuickSort(A, p, q - 1);
        QuickSort(A, q + 1, r);
    }
}
int main()
{
    int a[] = {6, 10 , 13, 5, 8, 3, 2, 11};
    QuickSort(a, 0, 7);
    for (int i = 0; i < 8; i++)
    {
        printf("%d ", a[i]);
    }
    return 0;
}


运行结果如下:

 

基本的快速排序算法思想大致是这样,当然现在出现了许多优化的排序算法,比如随机快速排序等,这样能保证时间复杂度为O(N*lg(N)),基本的快速排序在最差情况下时间复杂度为O(N*N)。事实上,其他许多算法设计上都利用了分治与递归的思想。之所以分治与递归紧密联系,其主要原因在于,分治算法是把一个大的问题划分为N个子问题(一般等分为2部分),减少问题规模,将一个复杂问题分解成几个简单的子问题,而这些子问题的求解方法与原问题方法一致,这就用到了递归的思想。比如二分查找,二叉树遍历等等。掌握了分治与递归的思想,的确为我们今后程序设计带来了非常大的便利。当然,递归算法一般会对程序效率产生一定影响,如果不是为了追求算法效率的极致,一般用递归算法也算可以了(很多要求解递归式其实就是大学学的差分方程式,可以直接根据差分方程理论得到通用解,这样时间复杂度就更简单,达到O(1)级别了)。要注意的是数据递归次数不能太多,太多的话可能会堆栈溢出,后面的结果就不确定的了。下次继续研究堆排序算法!



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值