归并排序基本思想是:将已有序的子序合并,从而得到完全有序的序列,即先使每个子序有序,再使子序列段间有序。但归并的空间开销很大,要申请很多空间。
稳定性:稳定排序
时间复杂度:O(NlogN)
适用场景:一般运用在链表的排序中,因为其在链表的排序中就不用申请新的空间,可以通过断链在链接的方式实现。
如图
代码实现(递归)
实现步骤:
1、把区间分为两块,申请两块合适大小的动态数组,把分好的两块区间拷贝进申请的两个数组中;
2、对其分好块的进行递归。直到块的大小为2的时候开始进行比较
3、前块和后块都从第一个元素开始比较,按大小关系插入原数组中,插入的下标就往后移,为插入的就不动,与另一块的移动后的下标元素进行比较,若其中一块已经全部插入另一块中还有剩余,把剩余部分直接插入原数组。
template<class T>
void MergeSort(T *pData, size_t size, bool IsUp = true)
{
if (size > 1)
{
int fsize, bsize;
fsize = size / 2;
bsize = size - fsize;
T *pFrontData = new T[fsize];
T *pBackData = new T[bsize];
memcpy(pFrontData, pData, sizeof(T)*fsize);
memcpy(pBackData, pData + fsize, sizeof(T)*bsize);
MergeSort(pFrontData, fsize, IsUp);
MergeSort(pBackData, bsize, IsUp);
//归并流程
int dataindex = 0;
int findex = 0;
int bindex = 0;
if (IsUp)
{
while ((findex < fsize) && (bindex < bsize))
{
if (pFrontData[findex] < pBackData[bindex])
{
pData[dataindex] = pFrontData[findex];
++dataindex;
++findex;
}
else
{
pData[dataindex] = pBackData[bindex];
++dataindex;
++bindex;
}
}
}
else
{
while ((findex < fsize) && (bindex < bsize))
{
if (pFrontData[findex] > pBackData[bindex])
{
pData[dataindex] = pFrontData[findex];
++dataindex;
++findex;
}
else
{
pData[dataindex] = pBackData[bindex];
++dataindex;
++bindex;
}
}
}
while (findex < fsize)
{
pData[dataindex] = pFrontData[findex];
++dataindex;
++findex;
}
while (bindex < bsize)
{
pData[dataindex] = pBackData[bindex];
++dataindex;
++bindex;
}
SAFE_DELARR(pBackData);
SAFE_DELARR(pFrontData);
}
}
代码实现(非递归)
块的大小从2开始,不断往上乘2;直到块的大小超过元素大小的两倍,停止循环
申请的空间大小比递归的少,时间效率也比递归的高
template<class T>
void MergeSort2(T *pData, size_t size, bool IsUp = true)
{
int blocksize = 2;
int smallblocksize;
int dataindex;
int findex;
int bindex;
T *_pTempData = new T[size]; //创建一个用于合并原数据缓冲区,大小和原数据缓冲大小一致
while (blocksize < (int)size * 2)
//块大小最大可以是接近整个数据的2倍
//因为自下而上的流程并不一定blocksize*2会等于size,可以会比size小很多
//所以要强制再扩大一次,只要不大于2倍大小都是可以的
{
smallblocksize = blocksize / 2; //一个块就是叶节点的父亲节点
//所以叶节点大小就为smallblocksize
for (int blocks = 0; blocks < (int)size; blocks += blocksize) //取每个大块的头
{
dataindex = blocks;
findex = blocks; //取每个大块的前小块的头
bindex = blocks + smallblocksize; //取每个大块的后小块的头
//下面while循环为判定大块中的前后小块没有超出小块大小
//并判断有没有超出所有元素的界限
//_pTempData为合并的缓冲区
//pData为原数据区
while (((findex - blocks) < smallblocksize) &&
(findex < (int)size) &&
((bindex - (blocks + smallblocksize)) < smallblocksize) &&
(bindex < (int)size))
{
if (pData[findex] < pData[bindex])
{
_pTempData[dataindex++] = pData[findex++];
}
else
{
_pTempData[dataindex++] = pData[bindex++];
}
}
//同上,合并剩余的前小块
while (((findex - blocks) < smallblocksize) &&
(findex < (int)size))
{
_pTempData[dataindex++] = pData[findex++];
}
//同上,合并剩余的后小块
while (((bindex - (blocks + smallblocksize)) < smallblocksize) &&
(bindex < (int)size))
{
_pTempData[dataindex++] = pData[bindex++];
}
}
//将合并好的缓冲区数据拷回原数据区
memcpy(pData, _pTempData, sizeof(T)*size);
blocksize *= 2;
}
SAFE_DELARR(_pTempData);
}