• 常用排序算法总结


    1.冒泡排序

    要说冒泡应该是非常简单的一种排序了,思路就如其名,数据像是泡泡一样逐个上升。

    /*
     * 冒泡排序
     */
    void bubbleSort(int *array , int length)
    {
        //设置flag来标示是否已经有序,用于优化冒泡排序
        int flag;
        //共执行n-1趟
        for(int i = 0 ; i < length - 1; i++)
        {
            //默认为已经有序
            flag = 0;
            //从最后一个元素开始判断,因为每一趟都会排好一个元素,所以每一趟都会少比较一次
            for(int j = length - 1 ; j > i ; j--)
            {
                //比较是否比前面的元素小
                if(array[j-1] > array[j])
                {
                    //如果小就交换位置并且标示为非有序数组
                    swip(&array[j-1], &array[j]);
                    flag = 1;
                }
            }
            //如果已经默认有序就不在进行
            if(!flag)
                break;
        }
    }

    这里说一下标示flag,如果已经有序比如1、2、3、4、5、6数组,但是内层循环依然会执行,只是不交换元素而已。当执行到没有元素交换的时候也就说明该数组已经有序了,这时就可以退出循环。

    2.简单选择排序

    简单选择排序就是先找当前位置为最小,判断后面是否有比当前元素小的,如果存在就和当前元素交换位置。相比冒泡排序简单选择的比较次数较多而交换最多就n-1次。

    /*
     * 简单选择排序
     */
    void selectSort(int *array, int length)
    {
        //和冒泡排序一样,也是执行n-1次
        for(int i = 0 ; i < length-1 ; i++)
        {
            //猜测当前位置为最小元素
            int min = i;
            //执行n-i-1次,用于找到最小的元素
            for(int j = i+1 ; j < length ;j++)
            {
                //如果当前元素比min位置小,那么当前位置为min
                if(array[min] > array[j])
                    min = j;
            }
            //如果min的位置发生变化,即当前位置不是min,那么交换位置
            if(min != i)
                swip(&array[min], &array[i]);
        }
    }

    3. 直接插入排序

    直接插入排序思路也很简单,就不多说了。

    /*
     * 直接插入排序
     */
    void insertSort(int *array ,int length)
    {
        //有几个元素就执行几次,默认最少两个元素,从第二个元素开始
        for(int i = 1 ; i < length ; i++)
        {
            //如果当前位置比前一位置元素小
            if(array[i] < array[i-1])
            {
                //保存当前元素
                int temp = array[i];
                int j;
                //当前元素前面的所有比当前元素大的元素全部后移一位
                for(j = i-1 ; array[j] > temp && j >= 0; j--)
                {
                    array[j+1] = array[j];
                }
                //把空出来的位置赋上保存的元素
                array[j+1] = temp;
            }
        }
    }

    4.希尔排序

    希尔排序就是直接插入排序的一个升级,让序列先相对有序,然后在不断减小间隔重新分组使整个有序。

    /*
     * 希尔排序
     */
    void shellSort(int *array ,int length)
    {
        //设置希尔排序的间隔(每次/2)
        int increment = length/2;
        //当间隔变为0时结束
        while(increment >= 1)
        {
            //从第一个间隔位置开始,到最后,分好组对每一组进行直接插入排序
            for(int i = increment ; i < length ; i++)
            {
                //和直接插入排序相同,只是间隔从直接插入的1变为了increment
                if(array[i] < array[i-increment])
                {
                    int j;
                    int temp = array[i];
                    for(j = i - increment ; array[j] > temp && j >= 0 ; j -= increment)
                    {
                        array[j+increment] = array[j];
                    }
                    array[j+increment] = temp;
                }
            }
            //更新间隔
            increment /= 2;
        }
    }

    5.堆排序

    堆排序相对复杂一些,主要思路就是把线性表当做完全二叉树去处理,然后构造大顶堆。

    /*
     * 调整堆结构
     */
    void heepAdjust(int *array, int loc , int length)
    {
        //保存当前位置数据
        int temp = array[loc];
        //得到左子节点位置,每次都得到子节点
        for(int i = loc * 2 ; i <= length ; i *= 2)
        {
            //如果当前位置<长度,就说明父节点有两个子节点,如果等于长度就只有一个
            //如果有两个,把位置调整到较大的位置
            if(i < length && array[i] < array[i+1])
                i++;
            //判断父节点是否比较大的大
            if(temp > array[i])
                break;
            //把较大的赋值给父节点
            array[loc] = array[i];
            //移动父节点位置到当前节点
            loc = i;
        }
        //最终位置赋值为保存的数据
        array[loc] = temp;
    }
    
    /*
     * 堆排序
     * array 中的0号元素空过去,所以length是array长度-1
     */
    void heepSort(int *array , int length)
    {
        //长度/2得到最后一个有子节点的节点位置,循环到根节点
        for(int i = length / 2 ; i > 0 ; i--)
        {
            //调整堆结构,始终保持大顶堆
            heepAdjust(array, i, length);
        }
        //从最后一个元素开始,逐个和根节点交换并调整堆结构
        for(int i = length ; i > 1 ; i--)
        {
            swip(&array[1], &array[i]);
            heepAdjust(array, 1, i-1);
        }
    }

    6.归并排序

    归并排序应该算是最不好理解的了,归并排序分为拆分递归和合并回朔两个过程,其中在拆分的时候会建立一个数组用于保存下一层的回朔。

    递归实现:

    /*
     * 合并排序当前层的数据
     */
    void Merge(int SR[], int TR[], int l , int m , int r)
    {
        //左边的初始位置
        int i = l;
        //右边的初始位置
        int j = m+1;
        //TR的位置
        int k = l;
        //如果比较排序没有完成就循环
        while(i <= m && j <= r)
        {
            //因为左右相对有序,所以只需要每次找左边和右边较小的,然后让找的位置+1
            if(SR[i] < SR[j])
            {
                TR[k] = SR[i];
                i++;
            }
            else
            {
                TR[k] = SR[j];
                j++;
            }
            //保存的数组位置+1
            k++;
        }
        //把剩下没有归并的数据全部存入TR
        if(i <= m)
        {
            for(int n = 0 ; n <= m - i ; n++)
            {
                TR[k] = SR[i+n];
                k++;
            }
        }
        if(j <= r)
        {
            for(int n = 0; n <= r - j ; n++)
            {
                TR[k] = SR[j+n];
                k++;
            }
        }
    }
    /*
     * 递归归并过程
     */
    void MSort(int SR[], int TR1[] ,int l , int r)
    {
        //定义一个数组用来存放该层归并的结果
        int TR2[20] = {0};
        //如果已经不能在拆分,把数据赋值给TR1回朔,注意这里TR1回朔后就是上一层的TR2了
        if(l == r)
        {
            TR1[l] = SR[l];
        }
        else
        {
            //归并的界限计算
            int m = (l+r)/2;
            //拆分左右,TR2是回朔合并用的
            MSort(SR, TR2, l, m);
            MSort(SR, TR2, m+1, r);
            //把该层的TR2重新排序并赋值给上一层的TR2
            Merge(TR2 ,TR1, l, m, r);
        }
    }
    
    void MergeSort(int *array, int length)
    {
        MSort(array, array, 0 ,length - 1);
    }

    非递归实现:Merge方法和递归版的一样

    void MergePass(int SR[], int TR[], int k, int length)
    {
        int i;
        //判断是否还有间隔能容纳数据,也就是判断当最后不够两组的时候结束
        for(i = 0 ; i < length-2*k+1 ; i+=2*k)
        {
            //合并
            Merge(SR, TR, i, i+k-1, i+2*k-1);
        }
        //判断是不是在1组合2组数据之间
        if(i < length - k)
        {
            Merge(SR, TR, i, i+k-1, length-1);
        }
        //1组数据以下或者间隔超过了数组长度
        else
        {
            for(int j = i ; j < length ;j++)
            {
                TR[j] = SR[j];
            }
        }
        print(TR,length);
    }
    /*
     * 归并排序(非递归)
     */
    void MergeSort(int *array , int length)
    {
        //开辟一个和数据一样大的空间
        int *TR = (int *)malloc(sizeof(int) * length);
        //初始合并间隔为1
        int k = 1;
        //k < length就不断地合并
        while (k < length) {
            //把array合并到TR
            MergePass(array,TR,k,length);
            //间隔*2
            k *= 2;
            //把TR合并回来
            MergePass(TR, array, k, length);
            //间隔*2
            k *= 2;
        }
    }

    7.快速排序

    快速排序是冒泡排序的一个升级,通过不断调整元素位置来排序,是排序算法中最为高效的

    int Partition(int *array , int low , int high)
    {
        //取第一个元素作为目标位置
        int pivotKey = array[low];
        while (low < high) {
            //如果high比目标小,交换
            while (low < high && array[high] > pivotKey) {
                high--;
            }
            swip(&array[low], &array[high]);
            //如果low比目标大,交换
            while (low < high && array[low] < pivotKey) {
                low++;
            }
            swip(&array[low], &array[high]);
        }
        return low;
    }
    
    void Qsort(int *array , int low, int high)
    {
        //目标值的位置
        int pivot;
        if(low < high)
        {
            //返回目标值位置
            pivot = Partition(array ,low ,high);
            //对前面进行快速排序
            Qsort(array, low, pivot-1);
            //对后面进行快速排序
            Qsort(array, pivot+1, high);
        }
    }
    
    void QuickSort(int *array , int length)
    {
        Qsort(array,0,length-1);
    }

    总结

    插入排序类 直接插入排序 希尔排序
    选择排序类 简单选择排序 堆排序
    交换排序类 冒泡排序 快速排序
    归并排序类 归并排序  

    排序算法指标

    排序方法 平均情况 最好情况 最坏情况 辅助空间 稳定性
    冒泡排序 O(n2) O(n) O(n2) O(1) 稳定
    简单选择排序 O(n2) O(n2) O(n2) O(1) 稳定
    直接插入排序 O(n2) O(n2) O(n2) O(1) 稳定
    希尔排序 O(nlogn)-o(n2) O(n1.3) O(n2) O(1) 不稳定
    堆排序 O(nlogn) O(nlogn) O(nlogn) O(1) 不稳定
    归并排序 O(nlogn) O(nlogn) O(nlogn) O(n) 稳定
    快速排序 O(nlogn) O(nllogn) O(n2) O(logn)~O(n) 不稳定
  • 相关阅读:
    python-文件操作
    python之-字符编码
    课程总结
    IO流文件输出流的应用
    字符串的基本操作
    数据结构字符串实训报告
    窗口的切换
    事件处理
    Java异常处理
    二维数组实现转置
  • 原文地址:https://www.cnblogs.com/madpanda/p/4719432.html
Copyright © 2020-2023  润新知