经常使用排序算法总结

1.冒泡排序算法

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

/*
 * 冒泡排序
 */
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,若是已经有序好比一、二、三、四、五、6数组,可是内层循环依然会执行,只是不交换元素而已。当执行到没有元素交换的时候也就说明该数组已经有序了,这时就能够退出循环。数组

 

2.简单选择排序优化

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

/*
 * 简单选择排序
 */
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. 直接插入排序spa

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

/*
 * 直接插入排序
 */
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.希尔排序blog

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

/*
 * 希尔排序
 */
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) 不稳定
相关文章
相关标签/搜索