算法笔记(排序算法)

  比较常见的比较排序有插入排序,选择排序,冒泡排序,合并排序,快速排序,堆排序等方法,这些排序的期望时间T(n)>O(nlgn).线性排序的方法有计数排序,基数排序,桶排序。排序方法的选择应该考虑几个比较重要的特性:时间复杂度,空间复杂度,稳定性,常数因子,算法实现难度,输入依赖性等。综合这些因素来讲,在实际应用中,线性排序未必就比比较排序要好。
下面是常见的排序实现(计数排序和快速排序可参见前面的博文):

  public class MySort
    {
        #region 插入排序
        /// <summary>
        /// 插入排序算法
        /// 原地置换:是
        /// 平均性能:O(n^2)
        /// 最好性能O(n):已符合目标排序;
        /// 最坏情况O(n^2):目标排序的倒排;
        /// </summary>
        /// <param name="Source">待排数组</param>
        public static void InsertionSort(int[] Source)
        {
            int theLength = Source.Length;
            //从第2个开始,将当前数插入到0..i-1数组中去.
            for (int i = 1; i < theLength; i++)
            {
                int theCurrVal = Source[i];
                int j = i - 1;
                //找到插入位置,并后移元素.
                while (j>=0 && theCurrVal < Source[j])
                {
                    Source[j + 1] = Source[j];
                    j--;
                }
                Source[j + 1] = theCurrVal;
            }
        }
        #endregion
        #region  合并排序.
        /// <summary>
        /// 归并排序
        /// 性能:O(nlog(n)),很稳定,不依赖于输入
        /// 原地置换:非原地置换排序,需要O(n)辅助空间.
        /// 稳定性:稳定的.
        /// </summary>
        /// <param name="Source">待排序数组</param>
        /// <param name="Start">开始位置</param>
        /// <param name="End">结束位置</param>
        public static void MergeSort(int[] Source, int Start, int End)
        {
            if (Start + 1 == End)
            {
                if (Source[Start] > Source[End])
                {
                    int theTmp = Source[End];
                    Source[End] = Source[Start];
                    Source[Start] = theTmp;
                }
                return;
            }
            if (Start == End)
            {
                return;
            }
            int theCenter = (Start + End) / 2;
            MergeSort(Source, Start, theCenter);
            MergeSort(Source, theCenter + 1, End);
            MergeArray(Source, Start, theCenter, theCenter + 1, End);
        }
        /// <summary>
        /// 归并两组.
        /// </summary>
        /// <param name="Source">源数组</param>
        /// <param name="S1">数组1开始索引</param>
        /// <param name="E1">数组1结束索引</param>
        /// <param name="S2">数组2开始索引</param>
        /// <param name="E2">数组2结束索引</param>
        private static void MergeArray(int[] Source, int S1, int E1, int S2, int E2)
        {
            int theLen = E2 - S1 + 1;
            int[] theTemp = new int[theLen];
            int i = S1;
            int j = S2;
            int k=0;
            while (i <= E1 && j <= E2)
            {
                if (Source[i] < Source[j])
                {
                    theTemp[k] = Source[i];
                    i++;
                }
                else
                {
                    theTemp[k] = Source[j];
                    j++;
                }
                k++;
            }
            while (i <= E1)
            {
                theTemp[k] = Source[i];
                i++;
                k++;
            }
            while (j <= E2)
            {
                theTemp[k] = Source[j];
                j++;
                k++;
            }
            for (i = 0; i < theLen; i++)
            {
                Source[S1 + i] = theTemp[i];
            }
        }
        #endregion
        #region 堆排序
        /// <summary>
        /// 堆排序:
        /// 平均性能:O(n*lgn),性能依赖于输入,性能最好是输入倒序排列(最大堆,目标为升序)
        /// 稳定性:不稳定;
        /// 原地置换:原地置换排序;
        /// </summary>
        /// <param name="A"></param>
        public static void HeapSort(int[] A)
        {
            //建堆O(n)
            HeapBuilder(A);
            int theHeapSize = A.Length;
            //循环取当前堆的最大值,并整理剩余堆
            //O(n*lgn)
            for (int i = A.Length-1; i > 0; i--)
            {
                int theTmp = A[0];
                A[0] = A[i];
                A[i] = theTmp;
                theHeapSize--;
                HeapMax(A, 0, theHeapSize);
            }
        }
        /// <summary>
        /// 取当前节点的左儿子
        /// </summary>
        /// <param name="i">当前节点索引</param>
        /// <returns>左儿子节点索引</returns>
        private static int HeapL(int i)
        {
            return i * 2 + 1;
        }
        /// <summary>
        /// 取当前节点的右儿子
        /// </summary>
        /// <param name="i">当前节点索引</param>
        /// <returns>右儿子节点索引</returns>
        private static int HeapR(int i)
        {
            return i * 2 + 2;
        }
        /// <summary>
        /// 取当前节点的父节点索引
        /// </summary>
        /// <param name="i">当前节点索引</param>
        /// <returns>父节点索引</returns>
        private static int HeapP(int i)
        {
            return (i + 1) / 2 - 1;
        }
        /// <summary>
        /// 向下调整指定节点的,使得当前堆保持最大堆性质。最多调整lgn次.
        /// </summary>
        /// <param name="A">要调整的堆数组</param>
        /// <param name="i">要调整的节点</param>
        /// <param name="HeapSize">堆大小</param>
        private static void HeapMax(int[] A, int i, int HeapSize)
        {
            int theL = HeapL(i);
            int theR = HeapR(i);
            int thelargest = i;
            if (theL < HeapSize && A[theL] > A[thelargest])
            {
                thelargest = theL;
            }
            if (theR < HeapSize && A[theR] > A[thelargest])
            {
                thelargest = theR;
            }
            if (thelargest != i)
            {
                int theTmp = A[i];
                A[i] = A[thelargest];
                A[thelargest] = theTmp;
                HeapMax(A, thelargest, HeapSize);
            }
        }
        /// <summary>
        /// 构建最大堆
        /// </summary>
        /// <param name="A">待排序数组</param>
        private static void HeapBuilder(int[] A)
        {
            int theHeapSize = A.Length;
            //满2叉树的性质,内节点数2^(H)-1,外节点数:2^H (H为树的高度)
            int theP = A.Length / 2 - 1;
            for (int i = theP; i >= 0; i--)
            {
                HeapMax(A,i,theHeapSize);
            }
        }
        #endregion
        #region 桶排序
        /// <summary>
        /// 桶排序:
        /// 性能:桶排序的期望时间为O(n),最坏时间为O(n^2).桶排序的性能依赖于输入序列的分布情况
        /// 如果重复元素或者分布不均匀的情况下,桶排序就不太适合.另外桶排序需要O(n)额外存储空间。
        /// 原地置换:非原地置换排序.
        /// 稳定性:桶排序的稳定性依赖于桶内排序算法。
        /// </summary>
        /// <param name="A"></param>
        public static void BucketSort(double[] A)
        {
            //double theMax = double.MinValue;   
            //for (int i = 0; i < A.Length; i++)
            //{
            //    if (theMax < A[i])
            //    {
            //        theMax = A[i];
            //    }
            //}
            int theLength = A.Length;
            //准备桶.O(n)
            List<List<double>> theBuckets = new List<List<double>>();
            for (int i = 0; i < theLength; i++)
            {
                theBuckets.Add(new List<double>());
            }
            //分桶O(n)
            for (int i = 0; i < theLength; i++)
            {
                int theIndex =Convert.ToInt32(Math.Floor(A[i] * theLength));
                theBuckets[theIndex].Add(A[i]);
            }
            //桶内排序期望:O(n),如果分布均衡,则每个桶的元素都在个位数的情况下,
            //总的桶内排序时间可认为是线性的。
            for (int i = 0; i < theLength; i++)
            {
                InsertionSort(theBuckets[i]);
            }
            //桶合并.O(n)
            int theCurrIndex = 0;
            for (int i = 0; i < theLength; i++)
            {
                for (int j = 0; j < theBuckets[i].Count; j++)
                {
                    A[theCurrIndex] = theBuckets[i][j];
                    theCurrIndex++;
                }
                theBuckets[i].Clear();
            }
            theBuckets.Clear();
        }
        /// <summary>
        /// 插入排序算法,原地置换排序,O(n^2)
        /// </summary>
        /// <param name="Source">待排数组</param>
        private static void InsertionSort(List<double> Source)
        {
            int theLength = Source.Count;
            //从第2个开始,将当前数插入到0..i-1数组中去.
            for (int i = 1; i < theLength; i++)
            {
                double theCurrVal = Source[i];
                int j = i - 1;
                //找到插入位置,并后移元素.
                while (j >= 0 && theCurrVal < Source[j])
                {
                    Source[j + 1] = Source[j];
                    j--;
                }
                Source[j + 1] = theCurrVal;
            }
        }
        #endregion
    }


 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值