排序算法总结

  相信排序算法大家都比较熟悉,这是我的个人心得以及实现,链接https://github.com/yxiao1994/Sort。这里我主要说一下一些实现细节。

  对于快速排序,一定不要忘记终止条件,否则会无限制的递归导致程序崩溃,代码如下:

void Sort::QuickSort(vector<int> & vec, int p, int r) {
     if(p<r){//一定要注意这里的判断,不然会无限递归下去,另外这里是小于,不是小于等于
        int q = PARTITION(vec, p, r);//q是数组中第q-p+1大的元素
        QuickSort(vec, p, q - 1);
        QuickSort(vec, q + 1, r);
    }
}

对于归并排序,主要是归并过程的实现,需要注意设置哨兵,这一点可以参见算法导论。代码如下:

void Sort::Merge(vector<int> & vec, int p, int q, int r) {//归并排序的merge过程,参见算法导论
    int n1 = q - p + 1;
    int n2 = r - q;
    int *L = new int[n1 + 1];
    int *R = new int[n2 + 1];
    L[n1] = MAX;//设置哨兵
    R[n2] = MAX;//设置哨兵
    for (int i = 0; i < n1; i++)
        L[i] = vec[p + i];
    for (int j = 0; j < n2; j++)
        R[j] = vec[j + q + 1];
    for (int i = 0, j = 0, k = p; k <= r; k++) {
        if (L[i] <= R[j]) {
            vec[k] = L[i];
            i++;
        }
        else {
            vec[k] = R[j];
            j++;
        }
    }
    delete[]L;
    delete[]R;
}

对于堆排序,参见数据结构与算法分析,个人认为这本书的堆排序版本要比算法导论上面的好,因为对于堆的维护过程没有每次都交换数据,而是维护一个"空穴"进行下滤操作,避免了每次都要与孩子节点进行数据交换操作。下面是堆排序的实现:

void Sort::HeapSort(vector<int>& vec)
{
    for (int i = vec.size() / 2; i >= 0; i--) {//建堆过程
        percDown(vec, i,vec.size());
    }
    for (int i = vec.size() - 1; i > 0; i--) {//维护过程
        exchange(vec[i], vec[0]);
        percDown(vec,0,i);
    }
}
int Sort::leftchild(int i)
{
    return 2*i+1;
}
void Sort::percDown(vector<int>& vec, int i,int n)
{
    int child;
    int key = vec[i];
    for (; leftchild(i) < n; i = child) {
        child = leftchild(i);
        if ((child!=n-1)&&(vec[child] < vec[child + 1]))
            child++;//找到左右孩子节点当中较大的节点
        if (key < vec[child])
            vec[i] = vec[child];//如果当前节点大于key,需要交换
        else break;    //否则表示key已经在适当的位置,停止循环
    }
    vec[i] = key;
}

 

下面是一些测试结果,这里我分为一般情况(即随机数组)和特殊情况(已排好序的数组)进行测试。我设置的数组大小是10000.

(1)一般情况下的排序性能比较

      

排序算法 冒泡排序 插入排序 快速排序 随机版本快速排序 归并排序 堆排序
所需时间(单位:s) 57.516 18.538 0.083 0.087 0.075 0.16

     大家可以看到,时间复杂度为o(nlog n)的版本和时间复杂度为o(n2)的算法确实不是一个级别的。

(2)已排序数组的排序性能比较

排序算法 冒泡排序 插入排序 快速排序 随机版本快速排序 归并排序 堆排序
所需时间(单位:s) 32.65 0.009 38.309 21.641 0.07 0.167

     可以看到,在已排好序的数组中,快速排序算法的性能会将至o(n2),这是因为现在每一次的划分都是将数组划分成n-1个元素的数组和1个元素的数组,而partition过程的复杂度为o(n),于是得到下式:

                                                                                                               T(n)=T(n-1)+O(n)

     这样就很容易看出为何快速排序算法的性能会下降到o(n2)级别了。采用随机版本的快速排序效果会好一些,然而根据测试效果好些并不能解决这个问题,这一点我还没有理解,希望各位赐教。而归并排序和堆排序的算法效率依然很高,另外值得注意的是此时插入排序的时间复杂度已经降为O(n),因此其速度比归并排序还要快。

     另外还有一个排序神器可以参见编程珠玑,不过条件限制比较多,需要数组中的数据没有重复,输入数据限制在较小的范围内。这一问题在面试中考的比较多,对于大文件的排序非常适合,例如对于一百万个电话号码进行排序。

     大概就总结这么多,第一次写博客,写的不好希望见谅。

 

posted on 2017-04-07 11:56  yxiaows  阅读(249)  评论(0)    收藏  举报

导航