代码改变世界

算法学习之合并排序

2010-10-30 20:02  MichaelYin  阅读(510)  评论(0编辑  收藏  举报

在我的前面的一篇Post中我讲了排序算法中很基本的插入排序,插入排序实现的算法思想是增量方法,即排好子数组之后,加入一个新的元素,然后再进行一个排序操作,保证重新生成按序排列的数组。

这篇Post我将简单的讲讲排序算法另外一种算法,合并算法,合并算法采用分治法的思路,即问题划分成n个规模较小而结构和原来问题相似的子问题,递归解决这些子问题,然后合并结果,最终得到原来问题的解。

合并算法主要分为三个部分,第一个部分是分解,将运来的问题分解成两个包含n/2个元素的数组的排序的问题,然后分别递归调用函数解决这两个数组的排序问题,最后一步,就是将已经排好序的数组重新进行组合,使其成为按序排列的数组。

这三步中代码量较多的就是合并的代码了,合并的问题其实我们也可以把它想象成把两堆已经按从小到大的顺序排好的扑克牌排成一堆扑克牌的问题,每次比较最上面的那个牌,小的就拿走放到输出堆中,重复这个比较过程直到某一堆牌为空,然后把另外一堆牌直接放到输出堆中这个比较过程就结束了,想清楚了了这个过程的话理解代码就不难了。

下面我将合并的代码贴出来

 //将两个数组按照顺序重新进行排列
        public static void Merge(int[] rawArray, int firstIndex, int middleIndex, int lastIndex)
        {
            //把middle指向的元素划分到前面的一个数组去
              int firstArrayCount = middleIndex - firstIndex + 1;
            int secondArrayCount = lastIndex - middleIndex;

            int[] firstArray = new int[firstArrayCount];
            int[] secondArray = new int[secondArrayCount];

            //将原来数组的元素分别复制到两个分开的数组当中去
             for (int i = 0; i < firstArrayCount; i++)
            {
                firstArray[i] = rawArray[firstIndex + i];
            }

            for (int i = 0; i < secondArrayCount; i++)
            {
                secondArray[i] = rawArray[middleIndex + i + 1];
            }

            int firstArrayIndex = 0;
            int secondArrayIndex = 0;
            int rawArrayIndex = firstIndex;

           //开始进行合并
            //通过while条件判断是否有数组已经全部遍历完毕,如果已经有一个数组输出完毕那么会跳出while循环
              while (firstArrayIndex < firstArrayCount && secondArrayIndex < secondArrayCount)
            {
                if (firstArray[firstArrayIndex] <= secondArray[secondArrayIndex])
                {
                    //将值赋给原来的数组对应的位置,完毕后index都加1
                    rawArray[rawArrayIndex++] = firstArray[firstArrayIndex++];
                }
                else
                {
                    rawArray[rawArrayIndex++] = secondArray[secondArrayIndex++];
                }
            }
            //跳出while循环后,这时有一个数组已经全部输出完毕,由于另外一个数组一定是按大小排序的数组,所以将剩下的元素直接循环赋值到原来的数组中去\
            while (firstArrayIndex < firstArrayCount)
            {
                rawArray[rawArrayIndex++] = firstArray[firstArrayIndex++];
            }

            while (secondArrayIndex < secondArrayCount)
            {
                rawArray[rawArrayIndex++] = secondArray[secondArrayCount++];
            }
        }

看懂了上面的合并,下面这个递归调用就没什么了,函数将数组进行拆分,然后递归调用函数进行子数组的排序,子数组排序完成后调用合并函数将数组进行合并

        /// <summary>
        /// 
        /// </summary>
        /// <param name="rawArray">需要排序的数组</param>
        /// <param name="firstIndex">左边界</param>
        /// <param name="lastIndex">右边界</param>
        public static void MergeSort(int[] rawArray, int firstIndex, int lastIndex)
        {
            if (firstIndex < lastIndex)
            {
                int middleIndex = (firstIndex + lastIndex) / 2;
                //递归调用函数
                  MergeSort(rawArray, firstIndex, middleIndex);
                MergeSort(rawArray, middleIndex + 1, lastIndex);
                //合并
                  Merge(rawArray, firstIndex, middleIndex, lastIndex);
            }
        }

合并排序体现的主要是一个将问题进行拆分解决的思路,通过递归调用函数来解决子问题,最后合并结果,从而得到原问题的解。

题外话:身边很多人在学数据结构和算法的时候对算法的语言有着特殊的要求,个人觉得学习算法学习的是一种解决问题的思路,而不是具体的代码实现,this is the point….