C#之ArraySort(一)

C#之ArraySort (一)

本文主要记录一下自己的学习过程,如有误,请指正

C#里的 Array.Sort 主要采用了 3 种排序算法,根据每种算法的特性,合理使用,达到更优。

文章目录

  • C#之ArraySort (一)
    • 1. 插入排序(InsertionSort)
    • 2. 快速排序(QuickSort)
    • 3. 堆排序(HeapSort)
    • 4. 简单测试
    • 5. 全部代码


1. 插入排序(InsertionSort)

插入排序在数据量少,且大多有序的情况下,效率不错。

  • 时间复杂度 :

    • best - case :O(n).
    • worst - case : O(n2).
  • wiki上的算法图解:
    C#之ArraySort(一)_第1张图片


2. 快速排序(QuickSort)

快速排序,如其名字一样,它很快。

  • 基本思想

    采用分治思想,选区一个pivot作为基准,左边元素都小于它,右边则大与它,分割成两个区间。再按此方法进行分割,直到排序完成。

  • 时间复杂度 :

    • best - case : O(nlogn).
    • worst - case : O(n2).
    • avg - case : O(nlogn).
  • 算法图解 :
    C#之ArraySort(一)_第2张图片

    1. 选取一个pivot作为基准,这里取最右边的元素。
    2. left指针右移,直到 T[left] >= pivot;right指针左移,直到T[right] <= pivot。
      交换 T[right] 、T[left]。
    3. 重复步骤 2,直到 left >= right。交换T[left]、 pivot。T[left]作为新基准,分割区间。
    4. 重复步骤2 步骤3
  • 优化:三数中值法
    C#之ArraySort(一)_第3张图片

    对最左,最右,以及中间的元素进行排序,如红框所示,pivot-> M,right->红箭头。

    此外,还进行了一次额外交换,保证pivot的位置在右端,如蓝框所示,pivot值不变(仍然为5),位置改变,right->蓝箭头。

    细心的读者,参考源码后可能会发现,.net中Sort采用的递归方式与一些教科书的写法有一点差异。有兴趣的童鞋,可以自行研究。


3. 堆排序(HeapSort)

堆排利用了堆这种数据结构。

  • 堆的性质

    • 总是一颗完全二叉树
    • 大顶堆、小顶堆 满足任意一个。
      • 大顶堆,堆中节点的值 总大于或等于 其孩子节点的值。
      • 小顶堆,堆中节点的值 总小于或等于 其孩子节点的值。
  • 性质简析

    • 非叶节点个数 n = arr.Length / 2, 每个非叶节点元素则为 arr[n - 1], 其孩子节点则为 arr[2n - 1]、 arr[2n]。 当第n个节点 只有一个孩子时, arr[2n] 不存在
    • 大顶堆: arr[n - 1] >= arr[2n - 1] , arr[n - 1] >= arr[2n]
    • 小顶堆: arr[n - 1] <= arr[2n - 1] , arr[n - 1] <= arr[2n]
  • 基本思想

    构建大顶堆,交换首尾元素。断尾,重构大顶堆,再交换首尾元素。直到没有尾巴去断。

  • 时间复杂度:

    • any - case : O(nlogn).
  • 算法图解:

    • 无序堆和大根堆

    C#之ArraySort(一)_第4张图片C#之ArraySort(一)_第5张图片

    • 构建大根堆
      C#之ArraySort(一)_第6张图片
      C#之ArraySort(一)_第7张图片
    • 排序过程
      C#之ArraySort(一)_第8张图片
    1. 构建大顶堆。从最后一个非叶节点arr[n - 1]开始。
      判断两个 孩节点 的大小,其中最大的孩节点 继续与 非叶节点比较,若大于,则交换。接着调整 arr[n -2],直到arr[0],即根节点。
    2. 交换首元素和末元素,去除末元素。
    3. 重构大顶堆,由于交换的是根节点,所以我们从 arr[0] 开始, 判断方法与 步骤 1 类似。但这里,由于是从根节点arr[0]开始的,所以需要执行到到最后一个非叶节点,即 arr[n - 1]。
    4. 重复 步骤2 步骤3 直到完成排序。

4. 简单测试

  • 测试条件
    模式: release
    次数:2k
    数组:随机数组,可重复,大小N(100,1000,5000,10000,100000,1000000)
    时间单位:s

  • C#之ArraySort(一)_第9张图片

5. 全部代码

using System;
namespace Sort {
    public class Sorts<T> where T : IComparable<T> {
        
        #region  插排
        public static void InsertionSort (T[] keys, int l, int r) {
            T d;
            int i, j;
            for (i = l; i < r; i++) {
                j = i;
                d = keys[i + 1];
                while (j >= l && d.CompareTo (keys[j]) < 0) {
                    keys[j + 1] = keys[j];
                    j--;
                }
                keys[j + 1] = d;
            }
        }
        #endregion

        #region 快排
        public static void QSort (T[] keys, int l, int r) {
            //标准写法
            if (l >= r) return;
            int p = PickPivotAndPartition (keys, l, r);
            QSort (keys, p + 1, r);
            QSort (keys, l, p - 1);

            //尾递归写法
            // while (l < r) {
            //     int p = PickPivotAndPartition (keys, l, r);
            //     QSort (keys, p + 1, r);
            //     r = p - 1;
            // }
        }

        static int PickPivotAndPartition (T[] keys, int l, int r) {
            //三点中值法
            int m = l + ((r - l) >> 1);
            SwapIfGreater (keys, l, m);
            SwapIfGreater (keys, l, r);
            SwapIfGreater (keys, m, r);

            T pivot = keys[m];
            Swap (keys, m, r - 1);
            int left = l, right = r - 1;

            while (left < right) {

                while (pivot.CompareTo (keys[++left]) > 0);
                while (right > l && pivot.CompareTo (keys[--right]) < 0);

                if (left >= right)
                    break;
                Swap (keys, left, right);
            }
            SwapIfGreater (keys, left, r - 1);
            return left;
        }
        #endregion

        #region  堆排
        public static void HeapSort (T[] keys, int l, int r) {
            int len = r - l + 1;
            for (int n = len >> 1; n > 0; n--) {
                //构建大顶堆
                DownHeap (keys, n, l, len);
            }

            for (int i = len; i > 1; i--) {
                //交换首尾元素
                Swap (keys, l, l + i - 1);
                //重构大顶堆
                DownHeap (keys, 1, l, i - 1);
            }
        }

        static void DownHeap (T[] keys, int n, int l, int length) {
            T d = keys[l + n - 1];
            int child; 
            //判断 是否为 非叶节点
            while (n <= length >> 1) {
                child = n << 1;
                if (child < length && keys[l + child - 1].CompareTo (keys[l + child]) < 0)
                    child++;
                if (d.CompareTo (keys[l + child - 1]) >= 0)
                    break;
                keys[l + n - 1] = keys[l + child - 1];
                n = child;//child是否为非叶节点,若是,则还要判断
            }
            keys[l + n - 1] = d;
        }
        #endregion

        static void SwapIfGreater (T[] k, int a, int b) {
            if (a != b) {
                if (k[a].CompareTo (k[b]) > 0) {
                    T t = k[a];
                    k[a] = k[b];
                    k[b] = t;
                }
            }
        }

        static void Swap (T[] k, int a, int b) {
            if (a != b) {
                T t = k[a];
                k[a] = k[b];
                k[b] = t;
            }
        }
   }

你可能感兴趣的:(C#,算法)