排序算法粗略总结

以整数数组递增排序为例:

1. 选择排序 selection sort

从第一个元素开始,在全数组里寻找最小的数,和第一个元素交换;

前进至第二个元素,在第二个元素为起始的余下数组中寻找最小的数,和第二个元素交换;

可以递归实现:

SelectionSortRecursion( data, start )

{

    if( start < data.length - 1 )

    {

        swap( data, start, findMinimumIndex(data, start) );

        selectionSortRecursion( data, start+1 );

    }

}

也可循坏实现。

(n-1)+(n-2)+ . . . + 1 =》n( n -1 )/2 => O(n^2) in best, average, worst cases;

特点:数组初始顺序对于比较次数没有影响。选择排序是in-place排序,即不需辅助存储空间。通常的选择排序实现都是非稳定的(not stable),即值相同的元素的初始顺序可能会被改变。

优点:最多只需n-1次交换操作,在移动数组元素需要很大代价,而比较代价很小的情况下,选择排序可能会比其他排序算法表现更好。


2. 插入排序 Insersion sort

第二个元素和第一个元素比较,若小于第一个元素,插入至第一个元素前面,若大,不变换两个元素位置。得到排好序的两个元素。第三个元素和前两个元素比较,插入到正确的位置,以此类推,直至最后一个元素。

InserstionSort( data )

{

    for( pos = 1; pos < data.length; pos++)

    {

        current = data[pos];

        for( i = 0; i < pos; i++ )

        {

            if( data[i] > current)

            {

                //将从 i 开始的 current - i 个元素向数组后方移动一个位置

                arrayCopy( data, i, data, i+1, pos - i);

                data[i] = current;

                break;

            }

        }

    }

}

best-case running time is O(n) when the original array is sorted。这意味着插入排序对于在已排序数组中插入新元素并排序效果较好。然而,插入排序的average和worst均是O(n^2)这意味着当n比较大且初始数组为随机无序状态时,插入排序的性能不能保证。

特点:插入排序是稳定的, in-place排序算法,对于排序小规模数组效果较好。插入排序经常被当做子模块(building block)用于构建更复杂的排序算法。


3. 快速排序

快速排序属于divide-and-conquer算法范畴,基本流程是:

选择pivot(可以随机也可以指定某一固定位置)-> 根据数组元素与pivot的值的比较(大于等于 或 小于),将数组分为左右两部分。

递归上述流程直到只剩一个元素无法分割。

Literate Programming 上的一种实现:

struct pivot_random {
    pivot_random() {
        srand(static_cast<unsigned int>(time(0)));
    }
    
    int operator() (int low, int high)
    {
        return low + (rand()%(high-low));
    }
};

int Partition(int* input, int low, int high, int pivot)
{
    swap(input[low], input[pivot]);
    pivot = low;
    while(low <= high)
    {
        while(input[high] >= input[pivot] && high >= low)
            high--;
        if( high < low )
            break;
        else {
            swap(input[high], input[pivot]);
            pivot = high--;
        }
        
        while(input[low] <= input[pivot] && high >= low)
            low++;
        if( low > high )
            break;
        else {
            swap(input[low], input[pivot]);
            pivot= low++;
        }
    }
    
    return pivot;
}

void QuickSort(int* input, int low, int high, pivot_random pf)
{
    if(high > low)
    {
        int pivot = pf(low, high);
        pivot = Partition(input, low, high, pivot);
        
        QuickSort(input, low, pivot-1, pf);
        QuickSort(input, pivot+1, high, pf);
    }
    
    return;
}

最坏情况O(n^2),最优情况O(nlog(n)),平均O(nlog(n)),大多数的快排实现不稳定(not stable)。


4. 归并排序 Merge sort

归并排序也是divide-and-conquer分治算法的一种。

void Merge(int* input, int low, int high)
{
    int* temp = new int[high-low+1];
    int start1 = low, end1 = (low+high)/2, start2 = (low+high)/2+1, end2 = high;
    int i;
    for(i = 0; start1 <= end1 && start2 <= end2; i++)
    {
        if(input[start1] <= input[start2])
            temp[i] = input[start1++];
        else
            temp[i] = input[start2++];
    }
    while(start1 <= end1)
        temp[i++] = input[start1++];
    while(start2 <= end2)
        temp[i++] = input[start2++];
    i--;
    while( i>= 0 )
    {
        input[i+low] = temp[i];
        i--;
    }
    
}

void MergeSort(int* input, int low, int high)
{
    if(low < high)
    {
        MergeSort(input, low, (low+high)/2);
        MergeSort(input, (low+high)/2+1, high);
        Merge(input, low, high);
    }
}
归并排序在划分子数组时,若子数组的大小小于一定值时,如10,可以直接调用其他排序算法,如插入排序:

if( high - low < 10 )

{

    InsertionSort(data, low, high);

    return data;

}

这是对归并排序常用的优化方法,因为插入排序的overhead比归并排序要小,而且插入排序在小规模数组上效果较好。

归并排序对于数据量大到无法完全装入内存的数组是个很好的选择。在通常情况下,将大文件分割成许多小文件,每一个小文件都可以装入内存,排好序后再写回文件,然后调用Merge流程,输入来自各个排好序的小文件的一部分,输出直接写到最终的文件。

归并排序的best,average,worst均是O(nlog(n)),可以保证性能的上界,但是归并排序需要额外的O(n)的存储空间,远大于其他排序算法堆空间的要求。通常的归并排序算法实现是stable的但不是in-place的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值