快速排序是排序算法中效率比较高的一种,也是面试常被问到的问题。

快速排序(Quick Sort)是对冒泡排序的一种改进。它的基本思想是,通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。(数据结构,严蔚敏)。

更具体一点的说,首先我们从待排序列中选取一个元素作为支点(pivot),这个支点的选择可以任意选取,所以经常直接拿第一个元素作为支点,更好一点的办法是三者取中法,即取第一个元素,最后一个元素,中间一个元素,这三个值中间的中值(非最大最小)作为支点。支点选好之后,把支点放到待排序序列的第一个位置,如果你选的是第一个元素作为支点的话,不用做什么,如果是其他情况,那么就把选好的支点跟第一个元素交换一下。然后通过代码将所有比支点小的记录都放在它之前的位置,将所有比它大的记录都放到它的位置之后。由此最后可以以该支点的位置作分界线,将序列分割成两个子序列。这个过程我们称为一趟快速排序(或一次划分)。

一次快速排序的具体实现:

将支点放在序列的第一个位置,最左边。然后从高位下标high(初始值为最后一个元素的位置)开始往前搜索,找到第一个小于支点的元素,将其和支点元素交换,即把一个比支点小的元素放在了支点的位置,而支点现在所处的新位置之后的元素都是比其大的,因为这个位置是我们从高位找到第一个比支点小的元素的原先位置;然后从低位low(初始值为第一个元素的位置)开始往后搜索,找到第一个大于支点的记录,将其和支点元素交换,由于前面的交换中支点所处的位置之后的元素都比其大,通过这一次的交换的话,这个较大的元素现在所在的位置往后的元素都是大于支点的,而且,支点回到的新的位置之前的元素都是比支点小的。重复这两步,直到从低位搜索和从高位搜索的两个index(也可以理解为指针)相遇,即low ==  high。

其实,上面提到的每次交换元素的操作可以精简一下,对支点记录的赋值是多余的,因为只有在一趟排序结束时,才能确定支点的最后位置。所以有一个临时变量将支点的值记录下来就可以了。每次交换时,只把需要变换位置的元素放到新的位置就可以了。

下面是代码,这段代码采用第一个元素作为支点,如果没有用第一个元素作为支点的话,那么交换一下,把支点放到第一个位置即可,然后采用下面相同的代码:

        static int Partition(int[] numbers, int low, int high)
{
int pivot = numbers[low];
while (low < high)
{
while (low < high && numbers[high] >= pivot)
{
high--;
} numbers[low] = numbers[high]; while (low < high && numbers[low] <= pivot)
{
low++;
}
numbers[high] = numbers[low];
} numbers[low] = pivot;
return low;
}

上面是一次快速排序的代码,最后返回的是支点的位置。下面就是对支点左右两边的序列分别进行快速排序,当子序列只有两个元素时,再通过一次排序该子序列就是有序的了,由此实现整个序列有序。可见,这是一个递归的操作。只有当序列长度大于1时才进行快速排序,因为只有一个元素的序列肯定是有序的,所以跳出递归的条件是low<high。只有一个元素时,low 是等于 high的。

        static void QuickSort(int[] numbers, int low, int high)
{
if (low < high)
{
int partitionLocation = Partition(numbers, low, high);
QuickSort(numbers, low, partitionLocation - );
QuickSort(numbers, partitionLocation + , high);
}
}

调用的过程就如:

        static void Main(string[] args)
{
int[] numbers = { , , , , , , , };
QuickSort(numbers, , numbers.Length - );
}

就平均时间而言,快速排序是目前认为是最好的一种内部排序方法。(数据结构,严蔚敏)。

最后,快速排序的平均时间复杂度是O(nlogn),最坏情况是O(n2),如序列本身就有序时,将蜕变为冒泡排序。空间复杂度为O(logn),这是因为递归过程要维护一个栈。

如果不好理解的话,最好的办法就是对着代码,手动走几遍,或者找些有配图的书或文章看看。

小小c#算法题 - 6 - 快速排序 (QuickSort)的更多相关文章

  1. 小小c#算法题 - 11 - 二叉树的构造及先序遍历、中序遍历、后序遍历

    在上一篇文章 小小c#算法题 - 10 - 求树的深度中,用到了树的数据结构,树型结构是一类重要的非线性数据结构,树是以分支关系定义的层次结构,是n(n>=0)个结点的有限集.但在那篇文章中,只 ...

  2. 算法实例-C#-快速排序-QuickSort

    算法实例 ##排序算法Sort## ### 快速排序QuickSort ### bing搜索结果 http://www.bing.com/knows/search?q=%E5%BF%AB%E9%80% ...

  3. 排序算法四:快速排序(Quicksort)

    快速排序(Quicksort),因其排序之快而得名,虽然Ta的平均时间复杂度也是O(nlgn),但是从后续仿真结果看,TA要比归并排序和堆排序都要快. 快速排序也用到了分治思想. (一)算法实现 pr ...

  4. 小小c#算法题 - 7 - 堆排序 (Heap Sort)

    在讨论堆排序之前,我们先来讨论一下另外一种排序算法——插入排序.插入排序的逻辑相当简单,先遍历一遍数组找到最小值,然后将这个最小值跟第一个元素交换.然后遍历第一个元素之后的n-1个元素,得到这n-1个 ...

  5. 小小c#算法题 - 12 - Joseph Circle(约瑟夫环)

    约瑟夫环是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围.从编号为k的人开始报数(从1开始报数),数到m的那个人出列:他的下一个人又从1开始报数,数到m的那个人又 ...

  6. 小小c#算法题 - 10 - 求树的深度

    树型结构是一类重要的非线性数据结构,树是以分支关系定义的层次结构,是n(n>=0)个结点的有限集.关于树的基本概念不再作过多陈述,相信大家都有了解,如有遗忘,可翻书或去其他网页浏览以温习. 树中 ...

  7. 小小c#算法题 - 9 - 基数排序 (Radix Sort)

    基数排序和前几篇博客中写到的排序方法完全不同.前面几种排序方法主要是通过关键字间的比较和移动记录这两种操作来实现排序的,而实现基数排序不需要进行记录项间的比较.而是把关键字按一定规则分布在不同的区域, ...

  8. 小小c#算法题 - 8 - 归并排序 (Merging Sort)

    “归并”的含义是将两个或两个以上的有序序列组合成一个新的有序序列.这个“归并”可以在O(n+m)的数量级上实现,但这同时也需要O(n+m)的空间复杂度.具体为:首先分配一个新的长度为n+m的空序列,然 ...

  9. 快速排序(quicksort)算法实现

    快速排序(quicksort)是分治法的典型例子,它的主要思想是将一个待排序的数组以数组的某一个元素X为轴,使这个轴的左侧元素都比X大,而右侧元素都比X小(从大到小排序).然后以这个X在变换后数组的位 ...

随机推荐

  1. <Perl算法小菜>排序加速--Schwatzian变换及Guttman-Rosler变换

    原创博客,转载请联系博主! perl里的数据都是以双精度为单元存储的,也就是相当于C/Cpp中的double型,而正则的解析是由perl内置的正则引擎完成的,那么除了重写一个属于自己的排序方法之外,我 ...

  2. POJ 之 Is the Information Reliable?

    B - Is the Information Reliable? Time Limit:3000MS     Memory Limit:131072KB     64bit IO Format:%I6 ...

  3. svn及git使用笔记

    这周发生好几件大事: 谷歌发布SHA-1安全加密碰撞实例 Cloudflare 泄露网络会话中的加密数据 linux内核漏洞 CVE-2017-6074 加密在网络中越来越受关注,目前github的提 ...

  4. 介绍几款Web服务器性能压力测试工具

    一.http_load 程序非常小,解压后也不到100K http_load以并行复用的方式运行,用以测试web服务器的吞吐量与负载. 但是它不同于大多数压力测试工具,它可以以一个单一的进程运行,一般 ...

  5. Windows- 改变cmd控制台默认显示编码

    在中国的电脑会要求兼容gbk编码,所以微软进入中国市场也顺应中国的要求,其中最明显的就是在cmd上默认的显示就是GBK .当开发人员在运行一些有打印中文的程序时,由于编码采用国际兼容版本的utf_8等 ...

  6. js中的可枚举属性与不可枚举属性

    在JavaScript中,对象的属性分为可枚举和不可枚举之分,它们是由属性的enumerable值决定的.可枚举性决定了这个属性能否被for…in查找遍历到. 一.怎么判断属性是否可枚举 js中基本包 ...

  7. django 链接地址匹配流程

    前提: 代码结构 步骤一: 下面为某个网页的链接地址 <body> {% if latest_article_list %} <ul> {% for article in la ...

  8. WebElement接口获取值

    通过WebElement接口获取值 size 获取元素的尺寸 text 获取元素的文本 get_attribute(name) 获取属性值 location 获取元素坐标,先找到要获取的元素,再调用该 ...

  9. jmeter--简单使用

    1.启动jmeter 2.创建线程组 2.点击线程组,选择添加,选择sampler(采样器),选择http请求 3.在添加的请求页面中,填写服务器名称或IP,端口,路径,请求的方法 4.添加请求的参数 ...

  10. centos 静态拨号

    本人系统centos6.5:虚拟机太丑,固ssh. centos的与联网相关的配置文件在 $ /etc/sysconfig/network-scripts DHCP方式-联网 打开文件 $ vim / ...