排序算法的c++实现——快速排序
快速排序是分治思想的又一典型代表,是应用最广的排序算法。分治思想就是把原问题的解分解为两个或多个子问题解,求解出子问题的解之后再构造出原问题的解。
在快速排序算法中,它的思想是把一个待排序的数组分成前半部分和后半部分,并且要求前半部分的值都大于等于或都小于等于后半部分的解, 当前半部分与后半部分都变成有序(通过递归调用快速排序来实现)后,我们就不需要合并两个子问题的解就已经得到了原问题的解。这也是为什么要求前半部分都大于等于或都小于等于后半部分的原因。所以呢,快速排序的核心在于如何把一个待排序的数组分成两部分!
说明几点:
1. 如何把待排序的数组划分为符合要求的两部分!
2. 期望的时间复杂度为O(NlogN), 最坏的时间复杂度为O(N*N)
3. 快速排序为原址排序,不需要额外的内存空间.
4. 快速排序不是稳定排序, 在交换过程中会破坏稳定性。
代码如下:
/***********************************************************************
* Copyright (C) 2019 Yinheyi. <chinayinheyi@163.com>
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the GNU General Public License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version. * Brief:
* Author: yinheyi
* Email: chinayinheyi@163.com
* Version: 1.0
* Created Time: 2019年05月08日 星期三 21时54分04秒
* Modifed Time: 2019年05月10日 星期五 22时16分17秒
* Blog: http://www.cnblogs.com/yinheyi
* Github: https://github.com/yinheyi
*
***********************************************************************/ // 1. 快速排序是分治思想的又一典型代表,是应用最广的排序算法。
// 2. 分治思想就是把原问题的解分解为两个或多个子问题解,求解出子问题的解之后再构造出原
// 问题的解。
// 3. 在快速排序算法中,它的思想是把一个待排序的数组分成前半部分和后半部分,并且要求
// 前半部分的值都大于等于或都小于等于后半部分的解, 当前半部分与后半部分都变成有序(通
// 过递归调用快速排序来实现)后,我们就不需要合并两个子问题的解就已经得到了原问题的解。
// 这也是为什么要求前半部分都大于等于或都小于等于后半部分的原因。
// 4.所以呢,快速排序的核心在于如何把一个待排序的数组分成两部分!
//
// 核心点:
// 1. 如何把待排序的数组划分为符合要求的两部分!
// 2. 期望的时间复杂度为O(NlogN), 最坏的时间复杂度为O(N*N)
// 3. 快速排序为原址排序,不需要额外的内存空间.
// 4. 快速排序不是稳定排序, 在交换过程中会破坏稳定性。
//
#include<cassert>
#include <stdexcept>
#include <iostream>
static inline void swap(int&, int&);
bool less(int lhs, int rhs);
bool greate(int lhs, int rhs);
static void PrintArray(int array[], int nLength_);
typedef bool (*Compare)(int, int); /**************** 版本一:使用数组的长度作为参数 ***************/
// 该函数实现对数组数列的划分;
// 输入值为数组指针/数组的长度/比较函数指针,
// 返回值为划分点的下标, 也就是后半部分第一个元素的下标;
int Partition(int array[], int nLength_, Compare CompFunc)
{
if (array == nullptr || nLength_ <= || CompFunc == nullptr)
{
assert(false);
throw std::invalid_argument("参数不合法!");
} int _nBoundValue = array[]; // 划分区间的边界值
int _nBoundIndex = ; // 指向边界的下标, 即第二部分第一个元素的下标;
for (int i = ; i < nLength_; ++i)
{
if (CompFunc(array[i], _nBoundValue))
{
swap(array[i], array[_nBoundIndex]);
++_nBoundIndex;
}
} // 如果第一个元素正好是最大或最小元素时,把返回值加1, 也就是把数组划分为第一个元素
// 和剩余的其它元素两部分。
if ( == _nBoundIndex)
return _nBoundIndex + ;
else
return _nBoundIndex;
} // 快速排序的功能函数
void QuickSort(int array[], int nLength_, Compare CompFunc)
{
if (array == nullptr || nLength_ <= || CompFunc == nullptr)
return; int _nPartionIndex = Partition(array, nLength_, CompFunc);
QuickSort(array, _nPartionIndex, CompFunc);
QuickSort(array + _nPartionIndex, nLength_ - _nPartionIndex, CompFunc);
} /**************** 版本二:使用数组的下标区间作为参数 ***************/
// 该函数实现对数组的划分。
// 输入参数为数组指针/半闭半开区间[start, end)表示的数组范围/比较谓词
// 返回值为划分点的下标, 也即后半部分第一个元素的下标。
int Partition_Version2(int array[], int nStart_, int nEnd_, Compare CompFunc)
{
if (array == nullptr || nEnd_ - nStart_ <= || CompFunc == nullptr)
{
assert(false);
throw std::invalid_argument("参数不合法!");
} int _nBoundValue = array[nStart_]; // 划分区间的边界值
int _nBoundIndex = nStart_; // 指向边界的下标, 即第二部分第一个元素的下标;
for (int i = nStart_ + ; i < nEnd_; ++i)
{
if (CompFunc(array[i], _nBoundValue))
{
swap(array[i], array[_nBoundIndex]);
++_nBoundIndex;
}
} // 如果第一个元素正好是最大或最小元素时,把返回值加1, 也就是把数组划分为第一个元素
// 和剩余的其它元素两部分。
if (_nBoundIndex == nStart_)
return _nBoundIndex + ;
else
return _nBoundIndex;
} void QuickSort_Version2(int array[], int nStart_, int nEnd_, Compare CompFunc)
{
if (array == nullptr || nEnd_ - nStart_ <= || CompFunc ==nullptr)
return; int _nPartionIndex = Partition_Version2(array, nStart_, nEnd_, CompFunc);
QuickSort_Version2(array, nStart_, _nPartionIndex, CompFunc);
QuickSort_Version2(array, _nPartionIndex, nEnd_, CompFunc);
} // 测试函数
/*************** main.c *********************/
int main(int argc, char* argv[])
{
int array[] = {-, , , , , -, , , , };
std::cout << "原数组的顺序为:" << std::endl;
PrintArray(array, );
std::cout << "版本一的快速排序:" << std::endl;
std::cout << "从小到大:" << std::endl;
QuickSort(array, , less);
PrintArray(array, );
std::cout << "从大到小:" << std::endl;
QuickSort(array, , greate);
PrintArray(array, );
std::cout << std::endl; int array2[] = {-, , , , , -, , , , };
std::cout << "版本二的快速排序:" << std::endl;
std::cout << "从小到大:" << std::endl;
QuickSort_Version2(array2, , , less);
PrintArray(array2, );
std::cout << "从大到小:" << std::endl;
QuickSort_Version2(array2, , , greate);
PrintArray(array2, ); return ;
} inline void swap(int& lhs, int& rhs)
{
int _nTemp = lhs;
lhs = rhs;
rhs = _nTemp;
} // 小于比较函数
bool less(int lhs, int rhs)
{
return lhs < rhs;
} // 大于比较函数
bool greate(int lhs, int rhs)
{
return lhs > rhs;
} // 打印数组函数
static void PrintArray(int array[], int nLength_)
{
if (nullptr == array || nLength_ <= )
return; for (int i = ; i < nLength_; ++i)
{
std::cout << array[i] << " ";
} std::cout << std::endl;
}
排序算法的c++实现——快速排序的更多相关文章
- 排序算法<No.2>【快速排序】
最近因为项目需要,研究AI相关的东西,主要是算法相关的. 有感触,所以决定,来一个系列的博文,可能会耗时很久,那就是要完成算法系列.起点,从最常用最基本的排序开始.后续会跟进其他类型的,比如树,图等领 ...
- 排序算法五:随机化快速排序(Randomized quicksort)
上一篇提到,快速排序的平均时间复杂度是O(nlgn),比其他相同时间复杂度的堆排序.归并排序都要快,但这是有前提的,就是假定要排序的序列是随机分布的,而不是有序的.实际上,对于已经排好的序列,如果用快 ...
- java排序算法之冒泡排序和快速排序
总结一下Java排序算法,以便记忆. 各类排序的时间复杂度: 排序方法 时间复杂度(平均) 时间复杂度(最坏) 时间复杂度(最好) 空间复杂度 稳定性 复杂性 直接插入排序 O(n2)O(n2) O( ...
- 排序算法Java实现(快速排序)
算法描述:对于一组给定的记录,通过一趟排序后,将原序列分为两部分,其中前一部分的所有记录均比后一部分的所有记录小,然后再依次对前后两部分的记录进行快速排序,递归该过程,直到序列中的所有记录均有序为止. ...
- 排序算法C语言实现——快速排序的递归和非递归实现
/*快排 - 递归实现nlogn*//*原理: 快速排序(Quicksort)是对冒泡排序的一种改进. 快速排序由C. A. R. Hoare在1962年提出.它的基本思想是:通过一趟排 ...
- 排序算法Nb三人组-快速排序
核心思想: 将列表中第一个元素拿出来,放到一边,左右两个循环,左面的大于拿出来的数,就把他挪到右面, 右面的小于拿出来的数就把他放在左面,这是列表被第一个元素''分''为两个列表,在对两个列表进行同样 ...
- Java中的排序算法(2)
Java中的排序算法(2) * 快速排序 * 快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists). * 步骤为: * 1. 从数 ...
- JavaScript版排序算法
JavaScript版排序算法:冒泡排序.快速排序.插入排序.希尔排序(小数据时,希尔排序会比快排快哦) //排序算法 window.onload = function(){ var array = ...
- 《算法导论》读书笔记之排序算法—Merge Sort 归并排序算法
自从打ACM以来也算是用归并排序了好久,现在就写一篇博客来介绍一下这个算法吧 :) 图片来自维基百科,显示了完整的归并排序过程.例如数组{38, 27, 43, 3, 9, 82, 10}. 在算法导 ...
随机推荐
- CanvasRenderingContext2D.fillText(text, x, y [, maxWidth]);
CanvasRenderingContext2D.fillText(text, x, y [, maxWidth]); [, maxWidth]的意思是,方括号代表可有可无,有fillText(tex ...
- L3865
一,写题 1,2^j 判断不了? 2,好像是有一个数据不对.. 3,debug是生命之源. 4,反正就对了30分
- 使用plv8+ shortid npm包构建一个短唯一id服务
plv8 是一个很强大的pg 扩展插件,我们可以直接额使用js 增强sql ,shortid 是一个用来生成短连接id 很方便的类库 因为shortid 是一个npm 模块,我们需要使用一种方法使用r ...
- luogu p2622关灯问题II
luogu p2622关灯问题II 题目描述 现有n盏灯,以及m个按钮.每个按钮可以同时控制这n盏灯--按下了第i个按钮,对于所有的灯都有一个效果.按下i按钮对于第j盏灯,是下面3中效果之一:如果a[ ...
- luogu P2430 严酷的训练 题解
By:Soroak 知识点:DP 思路:就是一道简单的DP 一开始我想用二维数组做 做着做着发现,没有那么难啊啊啊 完全可以用一维数组来做 我们先开两个一维数组来存每个题目的时间 一个是老王的时间,另 ...
- sparksql基础知识二
目标 掌握sparksql操作jdbc数据源 掌握sparksql保存数据操作 掌握sparksql整合hive 要点 1. jdbc数据源 spark sql可以通过 JDBC 从关系型数据库中读取 ...
- Android Studio 之 Navigation【2.数据的传递】
Android Studio 之 Navigation[2.数据的传递和过渡动画] 在资源navigation资源的xml文件中,在[目标界面] detialFragment中点击,在右边 Argum ...
- IBM X3650 m4 面板指示灯
- 【RabbitMQ学习之一】RabbitMQ入门
环境 win7 rabbitmq-server-3.7.17 Erlang 22.1 RabbitMQ使用Erlang语言开发消息中间件.RabbitMQ基于AMQP(高级消息队列协议)协议,更适合业 ...
- spark 读取 ftp
class FtpShow(spark: SparkSession, map: Map[String, String]) { private val path = map(FtpOptions.PAT ...