排序总览

概述

这短时间找各种排序学了下,头晕脑胀的。猴子掰玉米,学的时候原理都理解了,可是记住了这个,忘记了前面的。。。
所以特意做个总览,把优缺点和特性梳理下。也方便后续查找。
大概描述下排序算法思路简介,和各个排序的C#实现。重点是描述下各个排序的应用场景,面对哪些特点的数据时哪种算法最优。包括时间和空间上优先级上的选择。是否需要稳定排序等等…

所需辅助空间最多:归并排序
所需辅助空间最少:堆排序
平均速度最快:快速排序


1. 冒泡

属性:属于交换排序稳定排序
时间复杂度:n ~ n2 平均:n2
空间复杂度:1

2. 直接插入排序

属性:属于插入排序稳定排序
时间复杂度:n ~ n2 平均:n2
空间复杂度:1

3. 直接选择排序

属性:属于选择排序不稳定排序
时间复杂度:n2
空间复杂度:1

4. 堆排序

属性:属于选择排序不稳定排序
时间复杂度:平均:nlog2n
空间复杂度:1

5. 计数排序

属性:,稳定排序
时间复杂度:长度(关键字个数+关键字基数)
空间复杂度:关键字基数

6. 桶排序

7. 希尔排序

属性:属于插入排序不稳定排序
时间复杂度:n1.3
空间复杂度:1

8. 快速排序

属性:属于交换排序不稳定排序
时间复杂度:nlog2n ~ n2 平均:nlog2n
空间复杂度:log2n

9. 归并排序

属性:稳定排序
时间复杂度:平均:nlog2n
空间复杂度:n


以下是C#代码实现:

具体代码实现

1. 冒泡

原理:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
private static void Sort(int[] number)
{
    for (int j = 0; j < number.Length - 1; j++)
    {
        bool ischange = true;
        for (int i = 1; i < number.Length - j; i++)
        {
            if (number[i-1] >number[i])
            {
                int temp = number[i];
                number[i] = number[i - 1];
                number[i - 1] = temp;
                ischange = false;
            }
        }
        if (ischange)
        {
            break;
        }
    }
}

2. 直接插入排序

原理:
当插入第i(i >= 1)时,前面的V[0],V[1],……,V[i-1]已经排好序。这时,用V[I]的排序码与V[i-1],V[i-2],…的排序码顺序进行比较,找到插入位置即将V[i]插入,原来位置上的元素向后顺移。

private static void InsertSort(int[] dataArray)
{
    for (int i = 1; i < dataArray.Length; i++)
    {
        bool isInsert = false;            
        int iValue = dataArray[i];          
        for (int j = i - 1; j >= 0; j--)   
        {
            if (dataArray[j] > iValue)     
            {
                dataArray[j + 1] = dataArray[j]; 
            }
            else                      
            {
                dataArray[j + 1] = iValue; 
                isInsert = true;
                break;                  
            }
        }
        if (!isInsert)                     
        {
            dataArray[0] = iValue;
        }
    }
}

3. 直接选择排序

  1. 找到数组中最小的那个元素,将它和数组的第一个元素交换位置。
  2. 在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置。如此往复。
public void ChooseSort(int[] origin)
{
    for (int i = 0; i < origin.Length - 1; i++)
    {            
        int minindex = i;
        for (int j = i; j < origin.Length; j++)
        {
            if (origin[j] < origin[minindex])
            {                  
                minindex = j;
            }
        }
        int temp = origin[minindex];
        origin[minindex] = origin[i];
        origin[i] = temp;
    }
}

4. 堆排序

采用二叉堆的自我调整原理:
1.将要排序的数组创建为一个大根堆。大根堆的堆顶元素就是这个堆中最大的元素。
2.将大根堆的堆顶元素和无序区最后一个元素交换,并将无序区最后一个位置例入有序区,然后将新的无序区调整为大根堆。
重复操作,无序区在递减,有序区在递增。
初始时,整个数组为无序区,第一次交换后无序区减一,有序区增一。
每一次交换,都是大根堆的堆顶元素插入有序区,所以有序区保持是有序的。

public static void sort(int[] array)
{
    //1. 把无序数组构建成最大堆
    //根据大顶堆的性质可知:数组的前半段的元素为根节点,其余元素都为叶节点
    //从最底层的最后一个根节点,从下至上,从右至左,进行大顶推的调整
    for (int i = array.Length / 2 - 1; i >= 0; i--)    
    {            
        downAdjust(array, i, array.Length);
    }
    // 2. 循环删除堆顶元素,移到集合尾部,调整堆产生新的堆顶
    for (int j = array.Length - 1; j > 0; j--)
    {          
        int temp = array[0];
        array[0] = array[j];
        array[j] = temp;
        downAdjust(array, 0, j);// “下沉”调整最大堆
    }
}

/// <summary>
/// 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
/// </summary>
/// <param name="array"></param>
/// <param name="parentIndex">要“下沉”的父节点</param>
/// <param name="length">堆的有效大小</param>
public static void downAdjust(int[] array, int parentIndex, int length)
{
    // temp 保存父节点值,用于最后的赋值
    int temp = array[parentIndex];
    int childIndex = 2 * parentIndex + 1;
    while (childIndex < length)
    {
        // 如果有右孩子,且右孩子大于左孩子的值,则定位到右孩子
        if (childIndex + 1 < length && array[childIndex + 1] > array[childIndex])
        {
            childIndex++;
        }
        // 如果父节点大于任何一个孩子的值,则直接跳出
        if (temp >= array[childIndex]) break;

        //无须真正交换,单向赋值即可
        array[parentIndex] = array[childIndex];
        parentIndex = childIndex;
        childIndex = 2 * childIndex + 1;
    }
    array[parentIndex] = temp;
}

5. 计数排序

计数排序是一个非基于比较的排序算法

public int[] CountSort(int[] origin)
{
    int max = origin[0];
    int min = origin[0];
    int[] fina = new int[origin.Length];
    foreach (int item in origin)
    {
        max = item > max ? item : max;
        min = item < min ? item : min;
    }
    int[] count = new int[max - min + 1];
    for (int i = 0; i < origin.Length; i++)
    {
        count[origin[i] - min] += 1;
    }

    for (int i = 1; i < count.Length; i++)
    {
        count[i] += count[i - 1];
    }
    for (int i = origin.Length - 1; i >= 0; i--)
    {
        //b[--o[a[i] - min]] = a[i];
        fina[count[origin[i] - min] - 1] = origin[i];
        count[origin[i] - min]--;
    }
    return fina;
}

6. 桶排序

排一系列有序的“桶”,然后把元素按照区间填入,规模减小后各自排序,最后组合排列。

public static void BucketSortInt(int[] arr)
{
    if (arr == null || arr.Length < 2) return;
    int n = arr.Length;
    int max = arr[0];
    int min = arr[0];
    // 寻找数组的最大值与最小值
    for (int i = 1; i < n; i++)
    {
        if (min > arr[i])
            min = arr[i];
        if (max < arr[i])
            max = arr[i];
    }
    //和优化版本的计数排序一样,弄一个大小为 min 的偏移值
    int d = max - min;
    //创建 d / 5 + 1 个桶,第 i 桶存放  5*i ~ 5*i+5-1范围的数
    int bucketNum = d / 5 + 1;
    List<List<int>> bucketList = new List<List<int>>(bucketNum);        
    //初始化桶
    for (int i = 0; i < bucketNum; i++)
    {
        bucketList.Add(new List<int>());
    }
    //遍历原数组,将每个元素放入桶中
    for (int i = 0; i < n; i++)
    {
        int index = (arr[i] - min) / d;
        bucketList[index].Add(arr[i] - min);
    }
    //对桶内的元素进行排序,我这里采用系统自带的排序工具
    for (int i = 0; i < bucketNum; i++)
    {
        bucketList[i].Sort();
    }
    //把每个桶排序好的数据进行合并汇总放回原数组
    int k = 0;
    for (int i = 0; i < bucketNum; i++)
    {
        foreach (var item in bucketList[i])
        {
            arr[k++] = item + min;
        }          
    }        
}

7. 希尔排序

8. 快速排序

原理:
快速排序采用分治法:找到一个基准数,把数组划分为比它大和比它小的两部分,然后对这两部分继续划分大小,直到两部分只有一枚时结束。

//实现一:
private static void QuickSort2(int[] arr, int startIndex, int endIndex)
{
    if (startIndex >= endIndex)
    {
        return;
    }
    int stand = arr[startIndex];
    int left = startIndex;
    int right = endIndex;
    while (left != right)
    {
        for (; right > left; right--)
        {
            if (arr[right] < stand)
            {
                break;
            }
        }
        for (; right > left; left++)
        {
            if (arr[left] > stand)
            {
                break;
            }
        }           
        if (left < right)
        {
            int temp1 = arr[left];
            arr[left] = arr[right];
            arr[right] = temp1;
        }
    }
    arr[startIndex] = arr[left];
    arr[left] = stand;

    QuickSort2(arr, startIndex, left - 1);
    QuickSort2(arr, left + 1, endIndex);
}


//实现二:
private static void QuickSort(int[] arr, int startIndex, int endIndex)
{
    if (startIndex >= endIndex)
    {
        return;
    }
    int stand = arr[startIndex];
    int left = startIndex;
    int right = endIndex;        
    while (left != right)
    {
        while (left < right && arr[right] > stand)
        {
            right--;
        }
        while (left < right && arr[left] <= stand)
        {
            left++;
        }                            
        if (left < right)
        {
            int temp1 = arr[left];
            arr[left] = arr[right];
            arr[right] = temp1;
        }
    }        
    arr[startIndex] = arr[left];
    arr[left] = stand;        

    QuickSort(arr, startIndex, left - 1);
    QuickSort(arr, left + 1, endIndex);
}

9. 归并排序

通过递归的方式将大的数组一直分割,直到数组的大小为 1,此时只有一个元素,那么该数组就是有序的了,之后再把两个数组大小为1的合并成一个大小为2的,再把两个大小为2的合并成4的 … 直到全部小的数组合并起来。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值