现在的位置: 首页 > 综合 > 正文

排序算法之归并排序

2013年12月19日 ⁄ 综合 ⁄ 共 2170字 ⁄ 字号 评论关闭

一.归并排序原理    

      有很多算法都是建立在递归操作的基础上,为了达到一个特定的目的把问题分解为若干个小问题并调用自身以解决相关的问题。这些算法都隐含的包含一个分治的思想:把大问题逐渐细化,递归求解这些问题,然后合并所得的解。分治在每次递归都有三个步骤:

1> 把原来的问题分解为若干个子问题。
2> 解决这些子问题,递归求解这些子问题,直到子问题足够小则直接求解。
3> 合并子问题所得的解。

    归并排序是分治思想的一个典型。其排序过程大致为:
1> 把待排序的长度为n的序列分解为各具n/2个元素的子序列。
2> 使用递归排序递归地排序两个子序列。
3> 合并已经排序好的子序列。

    当待排序的子序列长度为1的时候,递归开始返回。归并排序的关键步骤在于合并两个已经排序好的子序列。我们暂且把这一个过程叫做merge(A, p, q, r)。其中A是待合并序列的数组,p <= q < r,这里假设A[p, q]和A[q+1, r ]都已经是排序好的子序列。我们可以计算出和并的子序列总长度为:r - p + 1 = n。我们先来看看归并排序的merge(A, p, q, r)实现过程。

static void _merge(void** ppvData, int nLeft, int nMid, int nRight)
{
	int nLengthLeft  = nMid - nLeft + 1;
	int nLengthRight = nRight - nMid;
	void** ppvLeft   = NULL;
	void** ppvRight  = NULL;
	int nIndex1 = 0;
	int nIndex2 = 0;

	do
	{
		ppvLeft  = (void**)malloc(sizeof(void*) * (nLengthLeft + 1));
		if(ppvLeft == NULL)
		{
			break;
		}

		ppvRight = (void**)malloc(sizeof(void*) * (nLengthRight + 1));
		if(ppvRight == NULL)
		{
			break;
		}
		
		for(nIndex1 = 0; nIndex1 < nLengthLeft; nIndex1++)
		{
			ppvLeft[nIndex1] = ppvData[nLeft + nIndex1];
		}
		for(nIndex2 = 0; nIndex2 < nLengthRight; nIndex2++)
		{
			ppvRight[nIndex2] = ppvData[nMid + nIndex2 + 1];
		}
		ppvLeft[nIndex1]  = INT_MAX;
		ppvRight[nIndex2] = INT_MAX;
		nIndex1 = 0;
		nIndex2 = 0;
		
		while(nLeft <= nRight)
		{
			if(ppvLeft[nIndex1] < ppvRight[nIndex2])
			{
				ppvData[nLeft] = ppvLeft[nIndex1];
				++nIndex1;

			}
			else
			{
				ppvData[nLeft] = ppvRight[nIndex2];
				++nIndex2;
			}
			++nLeft;
		}

	}while(0);
}

    上述过程可以描述为:合并一个长度为nRight - nLeft + 1的序列,左半部分子序列长度为nMid - nLeft + 1右半部分长度为nRight - nMid。归并排序需要额外的空间,分别把子序列分别拷入各自空间,同时这里设置一个哨兵节点,为了避免后面在合并的过程频繁的判断数组是否越界,提高效率。依次比较把两个子序列中比较小的数据存入源地址。直到一个子序列等于哨兵这个时候另外一个节点如果非空则其元素的值一定小于哨兵节点的值,只需要另外一个子序列剩余的数据全部存入即可。

    归并排序算法:

static void _merge_sort(void** ppvData, int nLeft, int nRight)
{
	if(nLeft < nRight)
	{
		int nMid = (nLeft + nRight) / 2;
		_merge_sort(ppvData, nLeft, nMid);
		_merge_sort(ppvData, nMid + 1, nRight);
		_merge(ppvData, nLeft, nMid, nRight);
	}
}

   上述过程可以描述为:把一个需要排序的序列分解为两个子序列。依次递归调用,直到子序列只剩下一个元素则递归开始返回,最后合并两个排序好的子序列即可。写一个测试程序:

二.测试

int merge_sort(void** ppvData, size_t nSize)
{
	 RET_VAL_IF_FAIL(ppvData != NULL && nSize >= 2, -1);

	_merge_sort(ppvData, 0, nSize - 1);
}

int main(int argc, char** argv)
{
	int anTest[] = {5, 3, 7, 1, 10, 2};
	int nIndex = 0;

	merge_sort((void**)anTest, sizeof(anTest) / sizeof(anTest[0]));

	for(nIndex = 0; nIndex < sizeof(anTest) / sizeof(anTest[0]); nIndex++)
	{
		printf("%d ", anTest[nIndex]);
	}
	printf("\n");
	return (0);
}

    测试结果:

    最后把排序算法用前面说过的测试模块测试一遍。

_______________The End。

抱歉!评论已关闭.