归并排序
算法原理
归并算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。(百度百科)
本文主要实现二路归并。
实质上归并排序就是一个 (不断划分子序列)– (子序列长度为1时停止递归) – (合并子序列) 的过程。
那么如何合并有序子序列?长度为1的子序列当然是有序的。对于长度大于一的序列的合并,我们引用算法导论的扑克的例子,桌上有两堆从小的在上,大的在下的扑克牌,我们要把它们合并为有序的一堆,只需每次把两堆牌顶中小的那张拿下来放在新堆中即可。
复杂度
时间复杂度 O(nlogn)
空间复杂度 O(n)
IO复杂度 (2nlogn)
算法实现
#include <iostream>
#include <cstdio>
#include <cstdlib>
#define LEN 11
using namespace std;
void printarray(int *data,int size) //打印序列
{
for(int i = 0; i < size ;++i)
{
cout << data[i] << " " ;
}
cout << endl;
}
void merge(int *data,int start,int mid,int end) //排序子序列
{
int ptr = 0;
int origin_mid = mid;
int origin_start = start; //记录原来的界限
int buffer[end-origin_start+1];
int lenth = end - start +1;
/* cout << "start:" << start << " mid:"<<mid << " end:" << end <<endl;
cout << "Data: ";
printarray(data,11);*/
while(start <= origin_mid || mid <= end) //当start指针和mid指针均没有越界时,即为两个子序列都不是空序列时循环
{
if ((data[start] < data[mid+1] || mid >= end ) && start <= origin_mid)
//把前半子序列数据压入缓冲区的情况有 1.前半段头数据较大 2.后半子序列为空 (保证前段不为空)
{
buffer[ptr] = data[start];
start++; //压入缓冲区,前半段指针后移
ptr++;
}else{
buffer[ptr] = data[mid+1];
mid++;
ptr++;
}
}
/* cout << "Buffer: ";
printarray(buffer,lenth);*/
for(int i = origin_start; i <= end ;++i)
{
data[i] = buffer[i-origin_start]; //用缓冲区数据代替没有排序的序列
}
/* cout <<"merged data:";
printarray(data,11);
cout << "----------" << endl;*/
}
void mergesort(int *data,int start,int end) //归并
{
int mid = (start + end )/2; //分治的分界
if(start < end)
{
mergesort(data,start,mid); //分治
mergesort(data,mid+1,end); //分治
merge(data,start,mid,end); //归并
}
}
int main(int argc, char const *argv[])
{
int data []= {1,5,7,10,2,3,4,8,12,4,13};
mergesort(data,0,LEN-1);
printarray(data,LEN);
return 0;
}
简洁一点的Merge写法:
void Merge(int data[],int start,int mid,int end)
{
int i = start;
int j = mid+1;
int k = start;
int merged_data[LEN];
while(i <= mid && j <= end)//均不为空堆
{
if(data[i]<=data[j])
merged_data[k++]=data[i++];
else
merged_data[k++]=data[j++];
}
while(i <= mid) //后半段为空堆
merged_data[k++]=data[i++];
while(j <= end) //前半段为空堆
merged_data[k++]=data[j++];
for(int l=0 ; l<8 ; l++) //记录数组
data[l]=merged_data[l];
}