快速排序分析及实现(C++)
快速排序算法分析及实现(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++)的更多相关文章
- 浅谈C++之冒泡排序、希尔排序、快速排序、插入排序、堆排序、基数排序性能对比分析之后续补充说明(有图有真相)
如果你觉得我的有些话有点唐突,你不理解可以想看看前一篇<C++之冒泡排序.希尔排序.快速排序.插入排序.堆排序.基数排序性能对比分析>. 这几天闲着没事就写了一篇<C++之冒泡排序. ...
- 快速排序原理、复杂度分析及C语言实现
本文作者华科小涛:@http://www.cnblogs.com/hust-ghtao/,参考<算法导论>,代码借用<剑指offer> 快速排序是一种最坏情况时间复杂度为的排序 ...
- 八大排序算法——快速排序(动图演示 思路分析 实例代码Java 复杂度分析)
一.动图演示 二.思路分析 快速排序的思想就是,选一个数作为基数(这里我选的是第一个数),大于这个基数的放到右边,小于这个基数的放到左边,等于这个基数的数可以放到左边或右边,看自己习惯,这里我是放到了 ...
- 快速排序算法思路分析和C++源代码(递归和非递归)
快速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用,再加上快速排序思想----分治法也确实实用,因此很多软件公司的笔试面试喜欢考这个. 快速排序是C.R.A.Hoar ...
- 9, java数据结构和算法: 直接插入排序, 希尔排序, 简单选择排序, 堆排序, 冒泡排序,快速排序, 归并排序, 基数排序的分析和代码实现
内部排序: 就是使用内存空间来排序 外部排序: 就是数据量很大,需要借助外部存储(文件)来排序. 直接上代码: package com.lvcai; public class Sort { publi ...
- Javascript中的冒泡排序,插入排序,选择排序,快速排序,归并排序,堆排序 算法性能分析
阿里面试中有一道题是这样的: 请用JavaScript语言实现 sort 排序函数,要求:sort([5, 100, 6, 3, -12]) // 返回 [-12, 3, 5, 6, 100],如果你 ...
- c++实现快速排序详细分析
快速排序坑挺多的,今天有空记录一下自己的实现,并加上详细的注释和举例 #include<iostream> using namespace std; int partion(int num ...
- PAT 1045 快速排序(25)(STL-set+思路+测试点分析)
1045 快速排序(25)(25 分) 著名的快速排序算法里有一个经典的划分过程:我们通常采用某种方法取一个元素作为主元,通过交换,把比主元小的元素放到它的左边,比主元大的元素放到它的右边. 给定划分 ...
- 对快速排序的分析 Quick Sort
快速排序 快排的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序.通常可选第一个记录为基准 ...
随机推荐
- Python入门基础学习 一
Python入门基础学习 一 Python下载及安装 下载地址:https://www.python.org/,选择最新的版本下载 稍等一会,安装完成. 简单语句 从idle启动Python:IDLE ...
- centos 中 mongodb 启动失败的修复
mongodb是使用centos的yum命令安装的,整个的安装过程如下: 1. 运行 yum info mongo-10gen查看是否有mongodb源,如有跳至第3步. 2. 运行 vim /etc ...
- win10 数字许可证激活被 KMS激活覆盖
打开cmd(管理员身份),依次执行以下命令: slmgr/upk slmgr/ckms slmgr/rearm 重启设备后联网登录Microsoft账号,转设置-激活-疑难解答,windows会找到与 ...
- Linux下.Net Core+Nginx环境搭建小白教程
前言 对于接触.Net Core的我们来说之前从未接触过Linux,出于资源和性能及成本的考虑我们可能要将我们的环境搬到Linux下,这对于我们从未接触过Linux的童鞋们来说很棘手,那么我今天将带你 ...
- 管道/FIFO注意事项
管道 1. 其本质是一个伪文件(实为内核缓冲区) 2. 由两个文件描述符引用,一个表示读端,一个表示写端. 3. 规定数据从管道的写端流入管道,从读端流出. 管道的原理: 管道实为内核使用环形队列机制 ...
- Remoteland HDU - 4196
题意: 给出一个n,在[1, n] 中挑选几个不同的数相乘,求能的到的最大完全平方数 解析: 最大的肯定是n!, 然后n!不一定是完全平方数 (我们知道一个完全平方数,质因子分解后,所有质因子的质数均 ...
- 问题 E: YK的书架
点击打开链接 问题 E: YK的书架 时间限制: 1 秒 内存限制: 128 MB 提交: 596 解决: 138 提交 状态 题目描述 YK新买了2n+1本相同的书,准备放在家里的3层书 ...
- 本机号码认证黑科技:极光(JG)开发者服务推出“极光认证”新产品
近日,中国领先的大数据服务商极光(JG)推出全新产品--极光认证JVerification.极光认证是极光针对APP用户注册登陆,二次安全验证等身份验证场景打造的一款本机号码认证SDK,验证用户提供的 ...
- 前后端分离——token超时刷新策略
前言 记录一下前后端分离下————token超时刷新策略! 需求场景 昨天发了一篇记录 前后端分离应用——用户信息传递 中介绍了token认证机制,跟几位群友讨论了下,有些同学有这么一个疑惑:toke ...
- eclipse安装SonarLint插件,sonarLint客户端的使用
一.在线安装sonarLint 打开eclipse里的help->Eclipse Marketplace,搜索“sonar”关键字,目前sonar的插件是sonarlint,如下图: 点击“In ...