8种排序算法

首先来看看排序算法有哪八种:

          

 
1.直接插入排序

原理 :每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序。

         稳定的排序,最坏时间复杂性为O(n^2),空间复杂度为O(1)。

           public void InsertSort<T>(T[] arry,Comparison<T> comparison)
           {
                //直接插入排序是将待比较的数值与它的前一个数值进行比较,所以外层循环是从第二个数值开始的
                for (int i = 1; i < arry.Length; i++)
                {
                    T temp = arry[i];
                    int j = i - 1;
     
                    // 在已排好序的数列段中找到比新数值小的数值
                    while (j >= 0 && comparison(arry[j] , temp) == 1)
                    {
                        //将比新数值大的数值向后移
                        arry[j + 1] = arry[j];
                        j--;
                    }
                    // 如果在已排好序的数列段中找到了比新数值小的数值
                    // 将数值替换到此位置
                    arry[j + 1] = temp;
                }
            }

 
2.希尔排序(有点疑惑网上的希尔排序都只用三个循环,博主是没有理解透彻,按照原理 应该至少有四个循环,因为插入排序就有两个循环,希望各位看官有知道的能够为博主解惑)

原理 : 将待排序数组按照步长gap进行分组,然后将每组的元素利用直接插入排序的方法进行排序;每次将gap折半减小,循环上述操作;当gap=1时,利用直接插入,完成排序。

        是非稳定排序算法

扫描二维码关注公众号,回复: 5690252 查看本文章

 public void ShellSort<T>(T[] arry,Comparison<T> comparison)
       {
            int Length = arry.Length;
            for (int gap = Length / 2; gap > 0; gap = gap / 2) //分组,得到组数
            {
                for (int i = gap; i < gap * 2; i++)//对每一组进行插入排序
                {
                    for (int j = i; j < Length; j += gap)
                    {
                        T temp = arry[j];
                        int k = j - gap;
                        while (k >= 0 && comparison(temp , arry[k]) == -1)
                        {
                            arry[k + gap] = arry[k];
                            k = k - gap;
                        }
                        arry[k + gap] = temp;
 
                    }
                }
            }
        }
---------------------

 

 
3.简单选择排序

原理 : 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

        进行移动操作的时间复杂度为O(n)。进行比较操作的时间复杂度为O(n^2)

        public void SimpleSelectSort<T>( T[] arry , Comparison<T> comparison)
        {
                int k = 0;
                for (int i=0;i<arry.Length;i++)
                {
                    k = i;
                    for (int j = i + 1;j<arry.Length;j++)
                    {
                        if(comparison(arry[k] , arry[j]) == 1)
                        {
                            k = j;
                        }
                    }
                    Exchange(ref arry[i],ref arry[k]);
                }
            }

 
4.堆排序

原理 : 是利用一种被称作二叉堆的数据结构进行排序的排序算法。

          二叉堆中,有两个与所维护数组相关的属性。Length表示数组的元素个数,而HeapSize则表示二叉堆中所维护的数组中的元素的个数(并不是数组中的所有元素都一定是二叉堆的有效元素)。因此,根据上述定义有: 0 <= HeapSize <= Length。

         二叉堆可分为最大堆和最小堆两种类型。在最大堆中,二叉树上所有的节点都不大于其父节点,即 A[Parent(i)] >= A[i]。最小堆正好相反:A[Parent(i)] <= A[i]。

         因为在调用MaxHeapify(MinHeapify)方法使根节点为A[i]的二叉堆满足最大(小)堆性质时我们有其左右子堆均已满足最大(小)堆性质这个假设,所以如果我们在将一个待排序的数组构造成最大(小)堆时,需要自底向上地调用 MaxHeapify(MinHeapify)方法。

       在利用最大堆进行排序时,我们先将待排序数组构造成一个最大堆,此时A[0](根节点)则为数组中的最大元素,将A[0]与A[n - 1]交换,则把A[0]放到了最终正确的排序位置。然后通过将HeapSize减去1,将(交换后的)最后一个元素从堆中去掉。然后通过MaxHeapify方法将余下的堆改造成最大堆,然后重复以上的交换。重复这一动作,直到堆中元素只有2个。则最终得到的数组为按照升序排列的数组。

            public void HeapSort<T>(T[] array, Comparison<T> comparison)
            {
                BuildMHeap<T>(array, comparison);
                for (int i = array.Length - 1; i > 0; i--)
                {
                    Exchange(ref array[i], ref array[0]);
                    MHeapify<T>(array, 0, i, comparison);
                }
            }
            //计算节点的父节点和子节点
            private int Parrent(int i)
            {
                return (i - 1) / 2;
            }
            private int Left(int i)
            {
                return 2 * i + 1;
            }
            private int Right(int i)
            {
                return 2 * i + 2;
            }
            //构建最大堆/最小堆
            private void BuildMHeap<T>(T[] array, Comparison<T> comparison)
            {
                for (int i = array.Length / 2 - 1; i >= 0; i--)
                {
                    MHeapify<T>(array, i, array.Length, comparison);
                    
                }
            }
            private void MHeapify<T>(T[] array, int i, int heapSize, Comparison<T> comparison)
            {
                int left = Left(i);
                int right = Right(i);
     
                int extremumIndex = i;
                if (left < heapSize && comparison(array[left], array[i]) > 0)
                {
                    extremumIndex = left;
                }
     
                if (right < heapSize && comparison(array[right], array[extremumIndex]) > 0)
                {
                    extremumIndex = right;
                }
     
                if (extremumIndex != i)
                {
                    Exchange<T>(ref array[extremumIndex], ref array[i]);
                    MHeapify<T>(array, extremumIndex, heapSize, comparison);
                }
            }

 
5.冒泡排序

原理 : 首先将一个记录的关键字和第二个关键字进行比较,若为逆序,则将两个记录交换,然后比较第2个记录和第3个记录的关键字。依次类推,直至第N-1个记录和第n个记录的关键字进行过比较为止。上述过程称为第一趟冒泡排序,执行n-1次上述过程后,排序完成。

优缺点 :

      优点:稳定 时间复杂度:理想情况下(数组本来就是有序的),此时最好的时间复杂度为o(n),最坏的时间复杂度(数据反序的),此时的时间复杂度为o(n*n) 。  冒泡排序的平均时间负责度为o(n*n).  

       缺点:慢,每次只移动相邻的两个元素。

           public void BubbleSort<T>( T[] array,Comparison<T> comparison)
            {
                for (int i = 0; i < array.Length; i++)
                {
                    for (int j = 0; j < array.Length - 1 - i; j++)
                    {
                        //比较相邻的两个元素,如果前面的比后面的大,则交换位置
                        if (comparison(array[j] , array[j + 1]) == 1)
                        {
                            Exchange(ref array[j],ref array[j + 1]);
                        }
                    }
                }
            }

6.快速排序

     原理 : 快速排序法是采用递归的方式对待排序的数列进行若干次的操作,每次操作使得被操作的数列部分以某个元素为分界值分成两部分,一部分小于该分界值,另一部分大于该分界值.该分界值一般被称为"枢轴". 一般先以左边第一个数作为分界值,将数列按该分界值分成左右两部分,左边部分小于该分界值,右边部分大于该分界值,然后再对左右两部分做重复的操作,直到最后完成排序。

      快速排序是一种不稳定的排序算法

        public void QuickSort<T>(T[] array, Comparison<T> comparison, int left, int right)
        {
                //左边小于右边说明排序还没有完成
                if (left < right)
                {
                    T middle = array[(left + right) / 2];
                    //注意初始化
                    int j = right + 1;
                    int i = left - 1;
                    while (true)
                    {
                        while (comparison(middle, array[++i]) > 0 && i < right) ;//左边,先加的原因是防止找到的最左边会超出界限
                        while (comparison(middle, array[--j]) < 0 && j > 0) ; //右边
                        if (i >= j)
                            break;
                        Exchange<T>(ref array[i], ref array[j]);
                    }
                    for (int m = 0; m < array.Length; m++)
                    {
                        Console.Write(array[m] + " ");
                    }
                    Console.ReadLine();
                    QuickSort<T>(array, comparison, left, i - 1);
                    QuickSort<T>(array, comparison, j + 1, right);
                }
            }

7.归并排序

         是分治法(Divide and Conquer)的一个非常典型的应用

原理 : 设归并排序的当前区间是R[low..high],分治法的三个步骤是:
①分解:将当前区间一分为二,即求分裂点
②求解:递归地对两个子区间R[low..mid]和R[mid+1..high]进行归并排序;
③组合:将已排序的两个子区间R[low..mid]和R[mid+1..high]归并为一个有序的区间R[low..high]。
  递归的终结条件:子区间长度为1(一个记录自然有序)。

      public void MergeSortFunction<T>(T[] array, Comparison<T> comparison, int first, int last)
            {
                try
                {
                    if (first < last)   //子表的长度大于1,则进入下面的递归处理
                    {
                        int mid = (first + last) / 2;   //子表划分的位置
                        MergeSortFunction(array,comparison, first, mid);   //对划分出来的左侧子表进行递归划分
                        MergeSortFunction(array, comparison, mid + 1, last);    //对划分出来的右侧子表进行递归划分
                        MergeSortCore(array, comparison, first, mid, last); //对左右子表进行有序的整合(归并排序的核心部分)
                    }
                }
                catch (Exception ex)
                { }
            }
     
            //归并排序的核心部分:将两个有序的左右子表(以mid区分),合并成一个有序的表
            private void MergeSortCore<T>(T[] array, Comparison<T> comparison, int first, int mid, int last)
            {
                try
                {
                    int indexA = first; //左侧子表的起始位置
                    int indexB = mid + 1;   //右侧子表的起始位置
                    T[] temp = new T[last + 1]; //声明数组(暂存左右子表的所有有序数列):长度等于左右子表的长度之和。
                    int tempIndex = 0;
                    while (indexA <= mid && indexB <= last) //进行左右子表的遍历,如果其中有一个子表遍历完,则跳出循环
                    {
                        if (comparison(array[indexA] , array[indexB]) <= 0) //此时左子表的数 <= 右子表的数
                        {
                            temp[tempIndex++] = array[indexA++];    //将左子表的数放入暂存数组中,遍历左子表下标++
                        }
                        else//此时左子表的数 > 右子表的数
                        {
                            temp[tempIndex++] = array[indexB++];    //将右子表的数放入暂存数组中,遍历右子表下标++
                        }
                    }
                    //有一侧子表遍历完后,跳出循环,将另外一侧子表剩下的数一次放入暂存数组中(有序)
                    while (indexA <= mid)
                    {
                        temp[tempIndex++] = array[indexA++];
                    }
                    while (indexB <= last)
                    {
                        temp[tempIndex++] = array[indexB++];
                    }
     
                    //将暂存数组中有序的数列写入目标数组的制定位置,使进行归并的数组段有序
                    tempIndex = 0;
                    for (int i = first; i <= last; i++)
                    {
                        array[i] = temp[tempIndex++];
                    }
                }
                catch (Exception ex)
                { }
            }

8.基数排序

        属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,算法的时间复杂度是O(n)

     public static void RadixSort( int[] array, int array_x = 10, int array_y = 100)
            {
                /* 最大数字不超过999999999...(array_x个9) */
                for (int i = 0; i < array_x; i++)
                {
                    int[,] bucket = new int[array_x, array_y];
                    foreach (var item in array)
                    {
                        int temp = (item / (int)Math.Pow(10, i)) % 10;
                        for (int l = 0; l < array_y; l++)
                        {
                            if (bucket[temp, l] == 0)
                            {
                                bucket[temp, l] = item;
                                break;
                            }
                        }
                    }
                    for (int o = 0, x = 0; x < array_x; x++)
                    {
                        for (int y = 0; y < array_y; y++)
                        {
                            if (bucket[x, y] == 0) continue;
                            array[o++] = bucket[x, y];
                        }
                    }
                }
            }

辅助函数:

           #region 交换值
            public void Exchange<T>(ref T x, ref T y)
            {
                T temp = x;
                x = y;
                y = temp;
            }
            #endregion
            #region 比较两个int的值
            public int ComparisonInt(int x,int y)
            {
                if(x > y)
                {
                    return 1;
                }
                else if(x == y)
                {
                    return 0;
                }
                else
                {
                    return -1;
                }
            }
            #endregion

源码地址:

           https://github.com/webloverand/Interview
参考列表:

          1.数据结构常见的八大排序算法(详细整理): https://www.cnblogs.com/hokky/p/8529042.html

          2.堆排序——C#实现 : https://blog.csdn.net/zhuo_wp/article/details/78251777

          3.归并排序算法(C#实现): https://www.cnblogs.com/mingmingruyuedlut/archive/2011/08/18/2144984.html
 

猜你喜欢

转载自blog.csdn.net/iamdll/article/details/88719849