十大常见排序算法——附C#代码实现

1,743 阅读3分钟

前言

“排序” 很重要:

  • 需求上需要实现排序:例如有一个排行榜要按玩家的等级进行排序;
  • 是其他算法的基础:例如二分查找必须对有序的数组才有效。

排序算法很多,接下来就介绍十种经典排序算法:

冒泡排序

算法思想:相邻元素两两比较,每轮遍历得到一个最值

public void BubbleSort(int[] arr) 
{ 
    //优化点:若一次排序中未发生交换,则已是有序的,可直接返回 
    bool swapped = false; 
    for (int i = 0; i < arr.Length - 1; i++) 
    { 
        for (int j = 0; j < arr.Length - 1 - i; j++) 
        { 
            if (arr[j] > arr[j + 1]) 
            { 
                int temp = arr[j]; 
                arr[j] = arr[j + 1]; 
                arr[j + 1] = temp; 
                swapped = true; 
             } 
         } 
         if (!swapped) return; 
    } 
}

选择排序

算法思想:一个位置的元素与后续所有元素比较,遍历完毕后该位置上的元素就是最值

public void SelectionSort(int[] arr) 
{ 
    for (int i = 0; i < arr.Length - 1; i++) 
    { 
        int smallIndex = i; 
        for (int j = i + 1; j < arr.Length; j++) 
        { 
            if (arr[smallIndex] > arr[j]) 
                smallIndex = j; 
        } 
        if (smallIndex != i) 
        { 
            int temp = arr[i]; 
            arr[i] = arr[smallIndex]; 
            arr[smallIndex] = temp; 
        } 
    } 
}

插入排序

算法思想:和摸扑克牌一样,构造有序数组,每次都往里面插

public void InsertionSort(int[] arr) 
{ 
    for (int i = 1; i < arr.Length; i++) 
    { 
        int tempValue = arr[i]; 
        for (int j = i - 1; j >= 0; j--) 
        { 
            if (tempValue < arr[j]) 
            { 
                int temp = arr[j]; 
                arr[j] = arr[j + 1]; 
                arr[j + 1] = temp; 
            } 
            else 
                break; 
        } 
    } 
}

希尔排序

算法思想:将数组分为n/2,n/4,...直至1个子数组,每个数组各自用插入排序

public void ShellSort(int[] arr) 
{ 
    for (int gap = arr.Length / 2; gap >= 1; gap = gap / 2) 
    { 
        for (int i = 0; i < gap; i++) 
        { 
            for (int j = i + gap; j < arr.Length; j = j + gap) 
            { 
                int tempValue = arr[j]; 
                for (int k = j - gap; k >= i; k = k - gap) 
                { 
                    if (arr[k] > tempValue) 
                    { 
                        int temp = arr[k + gap]; 
                        arr[k + gap] = arr[k]; 
                        arr[k] = temp; 
                    } 
                    else 
                        break; 
                } 
            } 
        } 
    } 
}

归并排序

算法思想:分治法,将数组一分为二,左侧和右侧都是有序的,然后合并为一个有序的,递归实现

public List<int> MergeSort(int[] arr) 
{ 
    int mid = arr.Length / 2; 
    if (mid == 0) 
    { 
        return new List<int>() { arr[0] }; 
    } 
    List<int> left = new List<int>(); 
    List<int> right = new List<int>(); 
    for (int i = 0; i < mid; i++) 
        left.Add(arr[i]); 
    for (int i = mid; i < arr.Length; i++) 
        right.Add(arr[i]); 
    left = MergeSort(left.ToArray()); 
    right = MergeSort(right.ToArray()); 
    return MergeList(left, right); 
} 
public List<int> MergeList(List<int> a, List<int> b) 
{ 
        List<int> temp = new List<int>(); 
        while (a.Count > 0 && b.Count > 0) 
        { 
            if (a[0] < b[0]) 
            { 
                temp.Add(a[0]); 
                a.RemoveAt(0); 
            } 
            else 
            { 
                temp.Add(b[0]); 
                b.RemoveAt(0); 
            } 
        } 
        if (a.Count > 0) 
        { 
            while (a.Count > 0) 
            { 
                temp.Add(a[0]); 
                a.RemoveAt(0); 
            } 
        } 
        if (b.Count > 0) 
        { 
            while (b.Count > 0) 
            { 
                temp.Add(b[0]); 
                b.RemoveAt(0); 
            } 
        } 
        return temp; 
}

快速排序

算法思想:取一个基准值,将数组中小于与大于该基准的元素分别放于该基准值的两侧,同理再对已划分的两区域再取基准,再分布元素至其两侧

(分布两侧具体实现:左索引递增直至大于基准值,右索引递减直至小于基准值,交换两索引的元素,继续直至两索引相等)

public void QuickSort(int[] arr, int left = 0, int right = -1) 
{ 
    right = right == -1 ? arr.Length - 1 : right; 
    int midIndex = left; 
    if (left >= right) 
        return; 
    int leftIndex = left + 1; 
    int rightIndex = right; 
    while (leftIndex < rightIndex) 
    { 
        while (leftIndex < rightIndex && arr[leftIndex] < arr[midIndex]) 
            leftIndex++; 
        while (leftIndex < rightIndex && arr[rightIndex] >= arr[midIndex]) 
            rightIndex--; 
        swap(arr, leftIndex, rightIndex); 
    } 
    if (arr[leftIndex] < arr[midIndex]) 
    { 
        swap(arr, midIndex, leftIndex); 
        midIndex = leftIndex; 
    } 
    else 
    { 
        swap(arr, midIndex, leftIndex - 1); 
        midIndex = leftIndex - 1; 
    } 
    QuickSort(arr, left, midIndex); 
    QuickSort(arr, midIndex + 1, right); 
} 
public void swap(int[] arr, int i, int j) 
{ 
    int temp = arr[i]; 
    arr[i] = arr[j]; 
    arr[j] = temp; 
}

堆排序

算法思想:构造大顶堆/小顶堆,取最值与堆末尾元素互换,重构长度减一的堆,重复上述操作

(构造大顶堆:取根节点,其×2+1与×2为其孩子节点,取最大的值替换其根节点,从树的末尾arr/2依次这么操作)

public void HeapSort(int[] arr)
{
    int[] tempTree = new int[arr.Length + 1];
    for (int i = 0; i < arr.Length; i++)
    {
        tempTree[i + 1] = arr[i];
    }
    // 构造大顶堆
    for (int j = arr.Length / 2; j > 0; j--)
    {
        Restore(tempTree, j, arr.Length);
    }
    for (int k = arr.Length; k > 1; k--)
    {
        int temp = tempTree[k];
        tempTree[k] = tempTree[1];
        tempTree[1] = temp;
        Restore(tempTree, 1, k - 1);
    }
    for (int h = 0; h < arr.Length; h++)
    {
        arr[h] = tempTree[h + 1];
    }
}

public void Restore(int[] arr, int root, int length)
{
    if (root > length / 2) return;
    int maxChildIndex = root * 2 + 1 > length ? root * 2 : (arr[root * 2] > arr[root * 2 + 1] ? root * 2 : root * 2 + 1);
    if (arr[maxChildIndex] > arr[root])
    {
        int temp = arr[maxChildIndex];
        arr[maxChildIndex] = arr[root];
        arr[root] = temp;
        root = maxChildIndex;
        Restore(arr, root, length);
    }
}

计数排序

算法思想:拿到数组中最大的值,构造一个长度为最大值的数组,每个元素都为0次,遍历待排序数组,给新数组对应的索引的值加1,最终遍历新数组反推出排序数组

桶排序

算法思想:其实算是计数排序的优化版,将一个数组分为n个桶,桶的分法要保证一个桶中的最小元素大于另一个桶中的最大元素,对每个桶内部单独进行排序,比如:按十位来分桶,每个桶内用计数排序

public int[] BucketSort(int[] arr, int bucketMax, int bucketCount)

{
    List<List<int>> bucket = new List<List<int>>();
    for (int i = 0; i < bucketCount; i++)
    {
        bucket.Add(new List<int>());
    }
    for (int j = 0; j < arr.Length; j++)
    {
        int index = Math.Min(arr[j] / (bucketMax / bucketCount), bucketCount - 1);
        bucket[index].Add(arr[j]);
        int tempValue = arr[j];
        for (int k = bucket[index].Count - 2; k >= 0; k--)
        {
            if (bucket[index][k] > tempValue)
            {
                int temp = bucket[index][k];
                bucket[index][k] = tempValue;
                bucket[index][k + 1] = temp;
            }
            else
                break;
        }
    }

    List<int> result = new List<int>();
    for (int m = 0; m < bucket.Count; m++)
    {
        result.AddRange(bucket[m].ToArray());
    }
    return result.ToArray();
}

基数排序

算法思想:其实也是桶排序的一种,桶的划分方式为基数(个位,十位,百位..)罢了

public int[] RadixSort(int[] arr, int max)
{
    int radix = 0;
    while (Math.Pow(10, radix) < max)
    {
        List<List<int>> bucket = new List<List<int>>();
        for (int i = 0; i < 10; i++)
        {
            bucket.Add(new List<int>());
        }
        for (int j = 0; j < arr.Length; j++)
        {
            int index = arr[j] / (int)Math.Pow(10, radix) % 10;
            bucket[index].Add(arr[j]);
        }

        List<int> temp = new List<int>();
        for (int k = 0; k < bucket.Count; k++)
        {
            temp.AddRange(bucket[k].ToArray());
        }
        arr = temp.ToArray();
        radix++;
    }
    return arr;
}

参考资料

菜鸟教程:十大经典排序算法