排序算法的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}. 在算法导 ...
随机推荐
- RabbitMQ消息确认机制
文章目录 1. 事务机制2. Confirm模式2.1 生产者2.1.1 普通Confirm模式2.1.2 批量Confirm模式2.1.3 异步Confirm模式2.2 消费者3. 其他 消费者如何 ...
- RabbitMQ六种队列模式-工作队列模式
前言 RabbitMQ六种队列模式-简单队列RabbitMQ六种队列模式-工作队列 [本文]RabbitMQ六种队列模式-发布订阅RabbitMQ六种队列模式-路由模式RabbitMQ六种队列模式-主 ...
- DS18B20温度获取
https://detail.tmall.com/item.htm?id=40083203373&spm=a1z09.2.0.0.31cd2e8d1sb06V&_u=e1qf7bf56 ...
- vue文件夹上传组件选哪个好?
一. 功能性需求与非功能性需求 要求操作便利,一次选择多个文件和文件夹进行上传:支持PC端全平台操作系统,Windows,Linux,Mac 支持文件和文件夹的批量下载,断点续传.刷新页面后继续传输. ...
- es6 Class类的使用
es6新增了一种定义对象实例的方法,使用class关键字定义类,与class相关的知识点也逐步火热起来,但是部分理解起来相对抽象,简单对class相关的知识点进行总结,更好的使用class. 关于类有 ...
- zy的日志报表执行有问题。crontab显示执行了任务,代码中应该有问题
crontab定时任务在日志记录中是执行了 但是在执行脚本的过程中报错了, 找不到问题原因,以后也要在脚本中加入日志记录, 但是奇怪的是在中午和晚上是正常的, 应该是那个时间段的判断逻辑有问题,导致程 ...
- NOIP动态规划大合集
1.前言 NOIP2003-2014动态规划题目大合集,有简单的也有难的(对于我这种动态规划盲当然存在难的),今天就把这些东西归纳一下,做一个比较全面的总结,方便对动态规划有一个更深的理解. 2.NO ...
- Koa帮我们做了什么
整理web渲染思路,与KOA作比较 1.开启服务器并监听端口,注册监听事件 // 原生 let http = require('http') const server = http.createSer ...
- haproxy 配置文件详解 之 frontend
配置示例: frontend www bind *: mode http option httplog option forwardfor option httpclose log global #a ...
- Java编程思想之十 内部类
可以将一个类定义放在另一个类的定义内部,这就是内部类. 10.1 创建内部类 创建内部类就是把类的定义置于外部类里面. public class Parcell { class contents{ i ...