快速排序分析及实现(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
快速排序 快排的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序.通常可选第一个记录为基准 ...
随机推荐
- 咏南树形下拉列表数据敏感控件--TYNdbTreeList
咏南树形下拉列表数据敏感控件--TYNdbTreeList 软件系统拥有自己通用的下拉列表控件可以大大地加速系统的开发和易用性. 控件支持DELPHI5及以上版本安装并使用. 控件的用法: proce ...
- TSQL--验证身份证是否有效
/****** Object: UserDefinedFunction [dbo].[udf_IsvalidIDCard] Script Date: 02/27/2014 16:03:20 ***** ...
- ASP.NET OAuth 2.0 新手上路
OAuth2.0资料 初衷:一直想整理授权系列demo,让自己项目高端大尚,列出新手授权系列,帮助小白程序员不用在为授权头疼 OAuth 允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服 ...
- driver.get()和driver.navigate().to()到底有什么不同?-----Selenium快速入门(四)
大家都知道,这两个方法都是跳转到指定的url地址,那么这两个方法有什么不同呢?遇到这种情况,第一反应就是查查官方的文档. 官方文档的说法是:Load a new web page in the cur ...
- web api 请求结果中页面显示的json字符串与json对象结果不一致
我在前端调用这个api的时候也是百思不得其解,明明看到页面上的结果ID是不一样的,但是在js中使用的时候,却一直有重复ID的情况 后来才发现原来是long这个类型的原因,JavaScript中Numb ...
- autofac JSON文件配置
autofac是比较简单易用的IOC容器.下面我们展示如何通过json配置文件,来进行控制反转. 需要用到以下程序集.可以通过nugget分别安装 Microsoft.Extensions.Confi ...
- 【转】如何成为一名优秀的web前端工程师(前端攻城师)?
[转自]http://julying.com/blog/how-to-become-a-good-web-front-end-engineer/ 程序设计之道无远弗届,御晨风而返.———— 杰佛瑞 · ...
- WPF 无边框拖动
无边框之后的拖动方法有三种. 我个人是喜欢第一和第三的方法,看个人去需求. 第三种代码比较仓促,有需要者可以立马用,或者稍作整理修改. 对于WIN10 .NET 4.5以上的框架可以使用 WIndow ...
- 基于JSP的在线考试系统-JavaWeb项目-有源码
开发工具:Myeclipse/Eclipse + MySQL + Tomcat 系统简介: 网络考试系统主要用于实现高校在线考试,基本功能包括:自动组卷.试卷发布.试卷批阅.试卷成绩统计等.本系统结构 ...
- PHP header函数设置http报文头示例详解以及解决http返回头中content-length与Transfer-Encoding: chunked的问题
最近在服务器上,多媒体与设备(摄像头)对接的时候,总是发生错误导致设备崩溃,抓包发现响应头不对,没有返回length,使得摄像头立即崩溃.找了一下资料,改了一下响应头就好了. //定义编码 heade ...