排序算法的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}. 在算法导 ...
随机推荐
- 创建、查看、删除计划任务at命令举例
1.三天后的下午 5 点执行 /bin/ls : at 5pm + 3 days at> /bin/ls 结束按ctrl+d 查看计划任务:at -l 之后 at -c ...
- WebForm 打开默认页
原文:https://www.cnblogs.com/lionden/p/3728716.html <configuration> <system.webServer> < ...
- [PHP] 一个免费、开源的基于tp5+layui2.1.5开发的快速开发框架
推荐 一个免费.开源的基于tp5+layui2.1.5开发的快速开发框架,既可以用来学习,也可以用来实际项目的快速开发: 码云下载:https://gitee.com/eduaskcms/eduask ...
- 浅谈BST(二叉查找树)
目录 BST的性质 BST的建立 BST的检索 BST的插入 BST求前驱/后继 BST的节点删除 复杂度 平衡树 BST的性质 树上每个节点上有个值,这个值叫关键码 每个节点的关键码大于其任意左侧子 ...
- 洛谷 P3374 【模板】树状数组 1 题解
P3374 [模板]树状数组 1 题目描述 如题,已知一个数列,你需要进行下面两种操作: 1.将某一个数加上x 2.求出某区间每一个数的和 输入格式 第一行包含两个整数N.M,分别表示该数列数字的个数 ...
- 每日一问:不一样的角度吐槽下 DataBinding
我们项目采用的是 kotlin && DataBinding 处理的,可能你会疑问,既然用的是 kotlin,为啥没有用 kotlinx?新的页面当然是用的 kotlinx 啦,但我们 ...
- Docker&持续集成与容器管理--系列教程
一 Docker简介 Docker介绍 Docker架构 二 Docker安装 Ubuntu Docker 安装 CentOS Docker 安装 Windows Docker 安装 MacOS Do ...
- ReentrantLock源码简析
概念 ReentrantLock,可重入锁.在多线程中,可以通过加锁保证线程安全. 加锁和解锁 加锁: public void lock() { sync.lock(); } 解锁 public vo ...
- 2018-2019-2 20165313 《网络对抗技术》 Exp 9 Web安全基础
一.实验要求 本实践的目标理解常用网络攻击技术的基本原理,做不少于7个题目,共3.5分.包括(SQL,XSS,CSRF).Webgoat实践下相关实验. 二.实验问题回答 (1)SQL注入攻击原理,如 ...
- cad.net 复制图元的时候按下多次esc导致复制中断的bug,令REGEN,REGENALL更新图元无效.
浩辰没有这个bug !!!!!!! 如上述动图所示,cad在复制一个多图元的操作时候,多次按下esc键中断复制操作, **注意例子要有足够多的图元(大概一万个图元),才能很好展示这个bug,而且这个b ...