题目:归并排序、快速排序、堆排序
时间复杂度&空间复杂度:
快速排序 及其时间复杂度和空间复杂度
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
void swap(int &a, int &b); // 交换
void mergeSort(int num[],int left,int right); // 归并排序
void merge(int num[],int left,int right,int mid); // 归并排序合并
void quickSort(int num[], int left, int right); // 快速排序
int partition(int num[], int left, int right); // 快速排序轴值
void heapSort(int num[], int N); // 堆排序
void heapify(int num[], int i, int N); // 堆的维护
// 计算时间时,将数组输出具体数值去掉
int main()
{
int N,num[100000];
clock_t start,finish;
while (1) {
srand((unsigned)time(NULL));
cout<<endl<<"**********"<<endl;
cout<<"输入问题规模:"<<endl;
cin>>N;
// 随机数
for (int i=0; i<N; i++) {
num[i] = rand()%10000 + 1; // max+1
}
// 输出
cout<<"排序前:";
for (int i=0; i<N; i++) {
if (i%20 == 0) {
cout<<endl;
}
cout<<num[i]<<" ";
}
// 选择
cout<<endl<<"归并排序选1,快速排序选2,堆排序选3:" <<endl;
int choose;
cin>>choose;
switch(choose) {
case 1 : {
cout<<"归并排序";
start = clock();
mergeSort(num, 0, N-1);
finish = clock();
break;
}
case 2 : {
cout<<"快速排序";
start = clock();
quickSort(num, 0, N-1);
finish = clock();
break;
}
case 3 : {
cout<<"堆排序";
start = clock();
heapSort(num, N-1);
finish = clock();
break;
}
default : {
cout<<"输入错误!!"<<endl;
return 0;
}
}
cout<<"后:";
for (int i=0; i<N; i++) {
if (i%20 == 0) {
cout<<endl;
}
cout<<num[i]<<" ";
}
cout<<endl<<"时间为:"<<(double)(finish-start)/CLOCKS_PER_SEC<<"s";
}
return 0;
}
// 交换
void swap(int &a, int &b)
{
int z = a;
a = b;
b = z;
}
// 归并排序
void mergeSort(int num[],int left,int right)
{
if (left < right) {
int mid = (left + right)/2;
mergeSort(num,left,mid); // 递归划分左半区域
mergeSort(num,mid+1,right); // 递归划分右半区域
merge(num,left,right,mid); // 合并
}
}
// 归并排序合并
void merge(int num[],int left,int right,int mid)
{
int i=left; // 标记左右未排序元素
int j=mid+1;
int num_[100000]; // 设置临时数组
int i_= left;
// 合并
while (i <= mid && j <= right) {
if (num[i] < num[j])
num_[i_++] = num[i++];
else
num_[i_++] = num[j++];
}
// 一边先合并完,剩余另一边,直接放入
while (i <= mid)
num_[i_++] = num[i++];
while (j <= right)
num_[i_++] = num[j++];
// 把临时数组复制回原数组
while (left <= right) {
num[left] = num_[left];
left++;
}
}
// 快速排序
void quickSort(int num[], int left, int right)
{
if (left < right) {
int mid = partition(num, left, right);
quickSort(num, left, mid-1);
quickSort(num, mid+1, right);
}
}
// 快速排序轴值
int partition(int num[], int left, int right)
{
// 设置左右指针
int i = left;
int j = right;
// 设置默认轴值
int pivot = num[right];
while (i != j) {
while (i < j && num[i] <= pivot) {
i++;
}
while (i < j && num[j] >= pivot) {
j--;
}
if (i != j) {
swap(num[i], num[j]);
}
}
swap(num[i], num[right]);
return i;
}
// 堆排序
void heapSort(int num[], int N)
{
// 堆的维护:变成大顶堆,从末尾第一个父结点开始
for (int i=(N-1)/2; i>=0; i--) {
heapify(num, i, N);
}
// 排序
for (int i=N; i>0; i--) {
swap(num[i], num[0]);
heapify(num, 0, i-1);
}
}
// 堆的维护
void heapify(int num[], int i, int N)
{
// 设i就是最大的
int largest = i;
// 左右孩子
int lson = i*2+1;
int rson = i*2+2;
// 找这三个中最大的,下标放到largest里
if (lson <= N && num[lson] > num[largest]) {
largest = lson;
}
if (rson <= N && num[rson] > num[largest]) {
largest = rson;
}
if (largest != i) {
swap(num[i], num[largest]);
heapify(num, largest, N);
}
}
运行时间与输入规模之间的关系曲线图:
根据曲线图,三种排序优劣:快速排序 > 堆排序 > 归并排序。
时间复杂度和空间复杂度:
归并排序、快速排序、堆排序都采用了分治法的思想,分治法是将问题分解为子问题,子问题分别求解再合并,涉及递归调用。
归并排序把一个规模为n的问题分解成两个规模为n/2的子问题解决,假设T(n)表示对n个数进行归并排序,则2T(n/2)表示将n个数分成两部分分别进行归并排序,分解子问题所花的时间是问题规模n,所以T(n)=2T(n/2)+n,时间复杂度为O(nlogn)。归并排序比较元素大小时,需要一个与待排序序列同样长度的存储空间来存放结果,所以空间复杂度为O(n)。
快速排序根据轴值分解子问题,最好的情况就是每一次取到的元素都刚好平分整个数组,扫描了整个待排序数组找到轴值,所需时间为n,所以2T(n/2)+n,总时间复杂度为O(nlogn)。最坏的情况就是每一次取到的元素是数组中最小/最大的,也就是冒泡排序,时间复杂度为:O(n^2)。快速排序空间复杂度为:O(logn)。
堆排序建堆的时间复杂度为O(n),给堆重新排序变成顺序数组的时间复杂度为O(nlogn),所以总的时间复杂度为O(n+nlogn)=O(nlogn)。堆排序是就地排序,空间复杂度为常数:O(1)。
题目:计数排序
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
void swap(int &a, int &b); // 交换
int countingSort(int num[], int N,int i); // 计数排序
void quickSort(int num[], int left, int right); // 快速排序
int partition(int num[], int left, int right); // 快速排序轴值
// 计算时间时,将输出的数值去掉
int main()
{
srand((unsigned)time(NULL));
int N,i,num[100000];
clock_t start,finish;
while (1) {
cout<<endl<<"**********"<<endl;
cout<<"输入问题规模:"<<endl;
cin>>N;
cout<<"输入i:"<<endl;
cin>>i;
// 随机数
for (int j=0; j<N; j++) {
num[j] = rand()%10000 + 2;
}
// 输出
cout<<"排序前:";
for (int j=0; j<N; j++) {
if (j%20 == 0) {
cout<<endl;
}
cout<<num[j]<<" ";
}
cout<<endl<<"选择方法:"<<endl;
cout<<"计数排序选择1,快速排序选择2:"<<endl;
int input,z;
cin>>input;
switch(input) {
case 1 :
start = clock();
z = countingSort(num, N, i);
finish = clock();
break;
case 2 :
start = clock();
quickSort(num, 0, N-1);
z = num[i-1];
finish = clock();
break;
default :
cout<<"输入错误!!"<<endl;
return 0;
}
cout<<"第"<<i<<"小的元素是:"<<z<<endl;
cout<<"时间为:"<<(double)(finish-start)/CLOCKS_PER_SEC<<"s";
}
return 0;
}
// 交换
void swap(int &a, int &b)
{
int z = a;
a = b;
b = z;
}
// 计数排序
int countingSort(int num[], int N,int i)
{
// 找出最大最小值
int max = num[0], min = num[0];
for (int j=0; j<N; j++) {
if (num[j] > max) {
max = num[j];
}
if (num[j] < min) {
min = num[j];
}
}
// 设置数组
int num_[max-min+1] = {0};
// 计数累加
for (int j=0; j<N; j++) {
num_[num[j]-min]++;
}
int k=0;
while (i > 0) {
i=i-num_[k++];
}
return k-1+min;
}
// 快速排序
void quickSort(int num[], int left, int right)
{
if (left < right) {
int mid = partition(num, left, right);
quickSort(num, left, mid-1);
quickSort(num, mid+1, right);
}
}
// 快速排序轴值
int partition(int num[], int left, int right)
{
// 设置左右指针
int i = left;
int j = right;
// 设置默认轴值
int pivot = num[right];
while (i != j) {
while (i < j && num[i] <= pivot) {
i++;
}
while (i < j && num[j] >= pivot) {
j--;
}
if (i != j) {
swap(num[i], num[j]);
}
}
swap(num[i], num[right]);
return i;
}
数据查找:
快速排序时间复杂度是O(nlogn)。
计数排序是建立一个新数组,遍历原数组,每一个旧元素按照其值对应新数组下标对号入座,新数组的元素进行加1操作。如果原数组的规模是N,最大最小整数的差值是M,也就是新数组长度为M,计数排序只涉及到数组的遍历,所以时间复杂度是O(N+M)。