快速排序算法分析及实现(C++)

算法思想

​ 把n个元素划分为三段:左端Left,中间段middle和右端right。中段仅有一个元素。左端的元素都不大于中间段的元素,右端的元素都不小于中间段的元素。因此可以对lefe和right对立排序,所以,快速排序是一种分治思想,把大问题分为了若干个小问题。middle的元素称为支点或分割元素。

​ 举例。考察元素【4,8,3,7,1,5,6,2】。假设选择元素6作为支点。因此6属于middle;4,3,1,5,2属于left,8和7属于right。left排序结果为1,2,3,4,5;right排序为7,8。把右端的元素放在支点之后,左端left元素放在支点之前,即可得到有序序列。这个例子仅仅是对快速排序的一个简单的描述,实际操作要比这复杂。

快速排序步骤

  • 指定一个支点

    注意,是“指定”,并没有明确的约束条件,也就是说这个支点是任意一个元素,一般我们选择两种支点:当前序列首元,或者随机选取

    ​ 两种方式各有优劣,前者胜在简单,但可能影响算法效率

    ​ 快排中,支点的最终位置越靠近中间位置效率越高,读起来可能有点怪怪的,注意支点是一个值(具体元素),而不是字面意思的位置,当支点在最终序列中的位置靠前或者靠后时算法效率都不高(类似于“最坏情况”)。

    ​ 因此,后者在一定程度上减少了最坏情况的发生次数,但随机选取需要耗费额外的时间

    所以在具体应用中一般采用第一种方式来指定“支点”,也就是直接把当前序列的首元作为“支点”。

  • 进行一趟快排

    ​ 快排中,一趟操作的最终目的是把“支点”放到它应该去的地方,同时,支点左边的元素都小于支点,右边的元素都大于支点,举个例子,已知序列{7, -1, 5, 23, 100, 101},那么第一趟快排的结果是{_, _, 7, _, _, _}

    ​ 可以看到,首元(支点)已经去了它该去的地方(在最终的结果序列中,7就在中间位置,没错吧)。

  • 对子序列进行快排。

    第二步我们已经成功用支点将序列分成了3个部分,left,middle right,这三个部分总体是有序的,但是每个元素内部确实无序的,因此我们需要让这3个部分的内部也有序,对left和right继续使用快速排序就好(递归思想)。

优点分析

​ 在最坏情况下,例如,数据段left总是空的,这快速排序用时O(n^2)。在最好情况下,即数据段的left和right规模大致相同,这时快速排序用时为O(nlogn)。而快速排序的平均复杂度也是O(nlogn),这是令人惊奇的速度,amazing!

​ 快排的前身是归并,而正是因为归并存在不可忽视的缺点,才产生了快排。归并的最大问题是需要额外的存储空间,并且由于合并过程不确定,致使每个元素在序列中的最终位置上不可预知的。针对这一点,快速排序提出了新的思路:把更多的时间用在“分”上,而把较少的时间用在“治”上。从而解决了额外存储空间的问题,并提升了算法效率。

​ 快排之所以被称为“快”排,是因为它在平均时间上说最快的,主要原因是硬件方面的,每趟快排需要指定一个“支点”(也就是作为分界点的值),一趟中涉及的所有比较都是与这个“支点”来进行比较的,那么我们可以把这个“支点”放在寄存器里,如此这般,效率自然大大提高。除此之外,快排的高效率与分治思想也是分不开的。

C++语言实现

首先把数组a的最大元素移动到数组的最右端(这样可以避免支点为最大的元素),然后调用函数执行排序。



#include <iostream>
using namespace std;
template <class T>
int indexOfMax(T* a, int n);
template <class T>
void quickSort(T * a, int n);
template <class T>
void quickSort(T* a, int leftEnd, int rightEnd);
int main()
{ int* a =new int[5];
a[0] = 4, a[1] = 3, a[2] = 1, a[3] = 5, a[4] = 2;
quickSort(a, 5);
for (int i = 0; i < 5; i++) {
cout<<a[i]<<"\t";
}
delete[] a;
return 0;
}
/*
快速排序的驱动程序
*/
template <class T>
void quickSort(T *a, int n) {
if (n <= 1) { return; }
int max = indexOfMax(a, n);
swap(a[max], a[n - 1]);
quickSort(a, 0, n - 2);
}
/*
快速排序函数
*/
template <class T>
void quickSort(T * a, int leftEnd, int rightEnd) {
if (leftEnd >= rightEnd) { return; }
//这里我们把支点选择为序列的第一个元素
T privot = a[leftEnd];
int leftCursor= leftEnd;//从左到右移动的索引
int rightCursor = rightEnd + 1;//从右到左移动的索引、
//将位于左侧不小于支点的元素和位于右侧不大于支点的元素进行交换
while (true) {
do
{//寻找左侧不小于支点的元素
leftCursor++;
} while (a[leftCursor]<privot);
do
{//寻找右侧不大于支点的元素
rightCursor--;
} while (a[rightCursor]>privot);
if (leftCursor >= rightCursor) { break; }
swap(a[leftCursor], a[rightCursor]);
}
a[leftEnd] = a[rightCursor];
a[rightCursor] = privot;
quickSort(a, leftEnd, rightCursor - 1);//对左侧的数段进行快速排序
quickSort(a, rightCursor + 1, rightEnd);//对右侧的数段进行快速排序
}
/*
找到最大值
*/
template< class T>
int indexOfMax(T* a, int n) {
int index = 0;
for (int i = 0; i < n; i++) {
if (a[index] < a[i]) {
index = i;
}
}
return index;
}

快速排序分析及实现(C++)的更多相关文章

  1. 浅谈C++之冒泡排序、希尔排序、快速排序、插入排序、堆排序、基数排序性能对比分析之后续补充说明(有图有真相)

    如果你觉得我的有些话有点唐突,你不理解可以想看看前一篇<C++之冒泡排序.希尔排序.快速排序.插入排序.堆排序.基数排序性能对比分析>. 这几天闲着没事就写了一篇<C++之冒泡排序. ...

  2. 快速排序原理、复杂度分析及C语言实现

    本文作者华科小涛:@http://www.cnblogs.com/hust-ghtao/,参考<算法导论>,代码借用<剑指offer> 快速排序是一种最坏情况时间复杂度为的排序 ...

  3. 八大排序算法——快速排序(动图演示 思路分析 实例代码Java 复杂度分析)

    一.动图演示 二.思路分析 快速排序的思想就是,选一个数作为基数(这里我选的是第一个数),大于这个基数的放到右边,小于这个基数的放到左边,等于这个基数的数可以放到左边或右边,看自己习惯,这里我是放到了 ...

  4. 快速排序算法思路分析和C++源代码(递归和非递归)

    快速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用,再加上快速排序思想----分治法也确实实用,因此很多软件公司的笔试面试喜欢考这个. 快速排序是C.R.A.Hoar ...

  5. 9, java数据结构和算法: 直接插入排序, 希尔排序, 简单选择排序, 堆排序, 冒泡排序,快速排序, 归并排序, 基数排序的分析和代码实现

    内部排序: 就是使用内存空间来排序 外部排序: 就是数据量很大,需要借助外部存储(文件)来排序. 直接上代码: package com.lvcai; public class Sort { publi ...

  6. Javascript中的冒泡排序,插入排序,选择排序,快速排序,归并排序,堆排序 算法性能分析

    阿里面试中有一道题是这样的: 请用JavaScript语言实现 sort 排序函数,要求:sort([5, 100, 6, 3, -12]) // 返回 [-12, 3, 5, 6, 100],如果你 ...

  7. c++实现快速排序详细分析

    快速排序坑挺多的,今天有空记录一下自己的实现,并加上详细的注释和举例 #include<iostream> using namespace std; int partion(int num ...

  8. PAT 1045 快速排序(25)(STL-set+思路+测试点分析)

    1045 快速排序(25)(25 分) 著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边. 给定划分 ...

  9. 对快速排序的分析 Quick Sort

    快速排序 快排的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序.通常可选第一个记录为基准 ...

随机推荐

  1. Tomcat 系统架构与设计模式1

    从 Tomcat 如何分发请求.如何处理多用户同时请求,还有它的多级容器是如何协调工作的角度来分析 Tomcat 的工作原理,这也是一个 Web 服务器首要解决的关键问题 Tomcat 总体结构 To ...

  2. Reverting back to the R12.1.1 and R12.1.3 Homepage Layout

    Reverting back to the 12.1.1 Homepage Layout Set the following profiles: FND: Applications Navigator ...

  3. 如何注册GitHub

    一.个人介绍 姓名:张志龙 学号:1413042026 班级:网工141 爱好:宅物 能力:c++编程 二.注册 注册GitHub其实很简单 首先我们要做的是打开官网 www.github.com(如 ...

  4. java程序练习

    数组求和作业 开发环境:java 工具:eclipse 两种数据类型excel和csv 在同学建议下,我选择用csv文件打开,这就引来了第一个问题,在java中如何调用csv文件.以下是我百度的结果 ...

  5. 史上最全的Python学习现线路视频教程(转)

    首先,由于各方面压力,不得不学习现在的主流技术,深度学习,人工智能,机器学习各方面的,python又重新的进入了更多的程序猿的圈子,原以为java就差不多可以干到退休了,但是没办法,学....已经成功 ...

  6. kubernetes yaml

    apiVersion: v1 #指定api版本,此值必须在kubectl apiversion中 kind: Deployment #指定创建资源的角色/类型 metadata: #资源的元数据/属性 ...

  7. CHARPTER 3--INDEX DMVs

    1.查找最重要的缺失的索引 --======================================================= --查找最重要的缺失的索引 ) DB_NAME() AS ...

  8. WPF文字间距

    代码: <ItemsControl ItemsSource="{Binding Info}" FontFamily="微软雅黑" FontSize=&qu ...

  9. C#基础笔记(第二十一天)

    1.FIle类.Path类.Directory类复习操作文件的File 操作文件,静态类,对文件整体操作.拷贝.删除.剪切等.Directory 操作目录(文件夹),静态类.Path 对文件或目录的路 ...

  10. Android 中 LayoutParams 的用法

    一个控件应当使用它的父控件的 LayoutParams 类型.因此,一个 TableVow 应该使用 TableLayout.Params . 所以,以一个 TableRow 为例: TableRow ...