C++分治策略实现快速排序
问题描述:
给定一个未知顺序的n个元素组成的数组,现要利用快速排序算法对这n个元素进行非递减排序。
细节须知:
(1)代码实现了利用递归对数组进行快速排序,其中limit为从已有的随机数文件中输入的所要进行排序的数据的数量(生成随机数并写入文件的过程已在前篇中写出)。
(2)算法主要利用哨兵元素对数据进行分块,递归无限细分之后实现排序。
(3)代码同样利用clock函数对算法的执行时间进行计算以进行算法的效率评估。
(4)为了验证排序结果,代码实现了将排序后的内容输出到同文件夹下的sort_number.txt文件中。
算法原理:
它的完成过程主要是将数组分解为两部分,然后分别对每一部分排序。在划分数组时,是将所有小于某个哨兵元素的项目放到该项目之前,将所有大于该哨兵元素的项目放到该项目之后。哨兵元素可以是任意项目,为方便起见,通常直接选择第一个项目。因而可以总结为三步:(1)分解;(2)递归求解;(3)合并。其中,算法的核心部分为对数组进行划分,将小于x的元素放在原数组的左半部分,将大于x的元素放在原数组的右半部分。
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <ctime>
#include <algorithm>
using namespace std;
#define limit 100000 void quicksort(int a[], int low ,int high)
{
if(low<high){ //递归的终止条件
int i = low, j = high; //使用i,j在对应区间内对数组进行排序;
int x = a[low]; //将数组的第一个元素作为哨兵,通过这种方式取出哨兵元素 while(i < j){
while(i < j && a[j] >= x)
j--; //从右向左寻找第一个比哨兵元素小的元素
if(i < j){
a[i] = a[j];
i++; //把找到的第一个小于哨兵元素的元素值赋值给第一个元素,并把下界(i)向后移一位
} while(i < j && a[i] <= x)
i++; //从左向右寻找第一个比哨兵元素大的元素
if(i < j){
a[j] = a[i];
j--;
} //把找到的第一个大于哨兵元素的元素值赋值给下标为j的元素,并把上界(j)向前移一位
}
a[i] = x; //把哨兵赋值到下标为i的位置,i前的元素均比哨兵元素小,i后的元素均比哨兵元素大 quicksort(a, low ,i-); //递归进行哨兵前后两部分元素排序
quicksort(a, i+ ,high);
}
}
int main(void)
{
ifstream fin;
ofstream fout;
int x;
int i;
int a[limit]; fin.open("random_number.txt");
if(!fin){
cerr<<"Can not open file 'random_number.txt' "<<endl;
return -;
}
time_t first, last; for(i=; i<limit; i++){
fin>>a[i];
}
fin.close(); first = clock(); quicksort(a,,limit-); last = clock(); fout.open("sort_number.txt"); if(!fout){
cerr<<"Can not open file 'sort_number.txt' "<<endl;
return -;
}
for(i=; i<limit; i++){
fout<<a[i]<<endl;
} fout.close(); cout<<"Sort completed (already output to file 'sort_number.txt')!"<<endl;
cout<<"Time cost: "<<last-first<<endl; return ;
}
程序设计思路:
(1)分解:以a[p]为基准元素将a[p:r]划分成3段a[p:q-1],a[q]和a[q+1:r],使得a[p:q-1]中任何元素小于等于a[q],a[q+1:r]中任何元素大于等于a[q]。下标q在划分过程中确定。
(2)递归求解:通过递归调用快速排序算法,分别对a[p:q-1]和a[q+1:r]进行排序。
(3)合并:由于对a[p:q-1]和a[q+1:r]的排序是就地进行的,所以在a[p:q-1]和a[q+1:r]都已排好序后不需要执行任何计算,a[p:r]就已排好序。
结果数据格式为time_t格式相减得到的长整型以及输出到文件的整形数据。
时间复杂性分析:
对于输入序列a[p:r],算法的计算时间显然为O(r-p-1).
快速排序的运行时间与划分是否对称有关,其最坏情况发生在划分过程中产生的两个区域分别包含n-1个元素和1个元素的时候。由于算法的计算时间为O(n),所以如果算法的每一步都出现这种不对称划分,则其计算时间复杂性T(n)满足
T(n)= O(1),n≤1
T(n)= T(n-1)+O(n),n>1
解此递归方程可得T(n)=O(n²)。
在最好情况下,每次划分所取的基准都恰好为中值,即每次划分都产生两个大小为n/2的区域,此时,算法的计算时间T(n)满足
T(n)= O(1),n≤1
T(n)= 2T(n/2)+O(n),n>1
其解为T(n)=O(nlogn)。
可以证明,快速排序算法在平均情况下的时间复杂性也是O(nlogn)。
C++分治策略实现快速排序的更多相关文章
- 小旭讲解 LeetCode 53. Maximum Subarray 动态规划 分治策略
原题 Given an integer array nums, find the contiguous subarray (containing at least one number) which ...
- 【技术文档】《算法设计与分析导论》R.C.T.Lee等·第4章 分治策略
分治策略有一种“大事化小,小事化了”的境界,它的思想是将原问题分解成两个子问题,两个子问题的性质和原问题相同,因此这两个子问题可以再用分治策略求解,最终将两个子问题的解合并成原问题的解.有时,我们会有 ...
- 【从零学习经典算法系列】分治策略实例——高速排序(QuickSort)
在前面的博文(http://blog.csdn.net/jasonding1354/article/details/37736555)中介绍了作为分治策略的经典实例,即归并排序.并给出了递归形式和循环 ...
- 递归与分治策略之循环赛日程表Java实现
递归与分治策略之循环赛日程表 一.问题描述 设有n=2^k个运动员要进行网球循环赛.现要设计一个满足以下要求的比赛日程表: (1)每个选手必须与其他n-1个选手各赛一次: (2)每个选手一天只能参赛一 ...
- 递归与分治策略之棋盘覆盖Java实现
递归与分治策略之棋盘覆盖 一.问题描述 二.过程详解 1.棋盘如下图,其中有一特殊方格:16*16 . 2.第一个分割结果:8*8 3.第二次分割结果:4*4 4.第三次分割结果:2*2 5.第四次分 ...
- C++分治策略实现二分搜索
问题描述: 给定已排好序的n个元素组成的数组,现要利用二分搜索算法判断特定元素x是否在该有序数组中. 细节须知: (1)由于可能需要对分治策略实现二分搜索的算法效率进行评估,故使用大量的随机数对算法进 ...
- [原]C语言实现的快速排序,采用分治策略,递归实现
#include<stdio.h> #define LEN 8 int a[LEN] = { 5, 2, 4, 7, 1, 3, 2, 6 }; int Partition(int a[] ...
- [图解算法]线性时间选择Linear Select——<递归与分治策略>
#include <ctime> #include <iostream> using namespace std; template <class Type> vo ...
- [图解算法] 归并排序MergeSort——<递归与分治策略>
#include"iostream.h" void Merge(int c[],int d[],int l,int m,int r){ ,k=l; while((i<=m)& ...
随机推荐
- final和static语句
final关键字 final的概念 关键字final,final的意思为最终,不可变.final是个修饰符,它可以用来修饰类,类的成员,以及局部变量.不能修饰构造方法. final的特点 当final ...
- cube.js 集成cratedb 的尝试
cratedb 提供了pg协议的兼容,我们可以直接使用pg client 连接,但是也不是完整实现pg 协议的 以下是 cube.js 集成cratedb 的一些尝试 环境准备 docker-comp ...
- select函数及fd_set介绍
1. select函数 1. 用途 在编程的过程中,经常会遇到许多阻塞的函数,好像read和网络编程时使用的recv, recvfrom函数都是阻塞的函数,当函数不能成功执行的时候,程序就会一直阻塞在 ...
- 关于windows使用git警告LF will be replaced by CRLF
由于windows平台的换行符是CRLF,但是我们引用别人的类库可能是在unix平台开发的,那么代码中的换行符是LF,而git默认会做这个转换,所以在用git提交这些代码时会有警告:LF will b ...
- map访问key不存在的情况下,用find。比[]直接访问的意思不一样,map[key]不返null
key不存在的话则创建一个pair并调用默认构造函数 map<CGuid, CLibItem>::iterator iterItem = m_world->m_library_sce ...
- AMPPZ2014 The Captain
传送门 BZOJdown Solution 比较妙,考虑按照给出的式子,只有\(x\)相邻或者\(y\)相邻的才会走,不然一定会走到相邻的再走\(x\)或\(y\),所以直接排序两边然后最短路即可. ...
- 在error日志打印异常
在日志中打印异常,经常会看到以下的写法: logger.error(e.getMessage()); 或者是: e.printStackTrace(); 这两种其实都不太好. e.getMessage ...
- Java编程思想之三 操作符
在底层,Java中的数据是通过使用操作符来操作的. 3.2 使用Java操作符 操作符接收一个或多个参数,并生成一个新值. 操作符作用于操作数,生成一个新值.有些操作符可能会改变操作数自身的值,这被称 ...
- Java虚拟机解释器与JIT编译器
一.JAVA编译相关概念 1.动态编译(dynamic compilation)指的是“在运行时进行编译”:与之相对的是事前编译(ahead-of-time compilation,简称AOT),也叫 ...
- C#实体类对应SQL数据库的自增长ID怎么设置?
/// <summary> /// 自增长ID /// </summary> [DatabaseGenerated(DatabaseGeneratedOption.Identi ...