快速排序算法分析及实现(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. 让FIREDAC记录数据库的异常日志

    默认FIREDAC不会记录数据库的异常. 比如典型的,提交的时候,非空字段没有给值. 某些人还以为FIREDAC不能捕获数据库的异常,其实FIREDAC是可以捕获并处理数据库的异常事件的. 方法异常简 ...

  2. test4 结对项目

    [必做 1] 基于作业3的结果,读取一个较小的文本文件A_Tale_of_Two_Cities.txt,统计该文件中的单词的频率,并将统计结果输出到当前目录下的 Result1.txt 文件. 结对对 ...

  3. [C#]DES再一例(转)

    public class Encrypt { internal string ToEncrypt(string encryptKey, string str) { try { byte[] P_byt ...

  4. struts2拦截器demo

    按照网上的一些资料配置的,期间也出现过几个错误. 其中有个错误,是关于struts.xml里面package配置的问题,因为里面的几个标签是有顺序的. 顺序是: result-types interc ...

  5. Javascript设计模式理论与实战:单例模式

    在Javascript中,单例模式是一种最基本又经常用到的设计模式,可能在不经意间就用到了单例模式. 本文将从最基础的理论开始,讲述单例模式的基本概念和实现,最后用一个例子来讲述单例模式的应用. 理论 ...

  6. mysql5.7 创建新表时提示时间戳非法

    # 背景 mysql版本5.7.8,需要创建新表,研发提供的sql文件,执行后报错如下: ERROR (): Invalid default value for 'deleted_at' 就猜测到时因 ...

  7. C#中哈希表(HashTable)的用法详解

    描述: 哈希表存放 key.values ,key值可以用于快速调取用,values 对应object类型,也就是说所有类型. 实例: 1.HashTable存放学生的成绩 Hashtable ht1 ...

  8. Mysql 中日期类型bigint和datetime互转

    MySql数据库中字段类型bigint 长度是10位的 mysql> select (from_unixtime(1554047999))as datatime;+--------------- ...

  9. python---基本数据类型 dict(字典)

    1. 什么是字典 字典是python中唯一的映射类型, 由{ } 括起来的键值对组成,在dict中key是唯一的.字典是以key:value的形式来保存数据, 字典存储数据的时候是用的hash值来存储 ...

  10. 数论入门2——gcd,lcm,exGCD,欧拉定理,乘法逆元,(ex)CRT,(ex)BSGS,(ex)Lucas,原根,Miller-Rabin,Pollard-Rho

    数论入门2 另一种类型的数论... GCD,LCM 定义\(gcd(a,b)\)为a和b的最大公约数,\(lcm(a,b)\)为a和b的最小公倍数,则有: 将a和b分解质因数为\(a=p1^{a1}p ...