排序专题

本文主要是基于比较的排序算法。

选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。

1.快速排序

快速排序的地位在业界是不言而喻的,时间复杂度为O(nlogn),且常系数为2。在不考虑极端情况下,速度是很理想的。

(1)确定基准数,利用partition函数得到基准数在一趟排序后的位置。

int partition(vector<int> &vec, int low, int high)
{
    int i = low;
    int j = high;

    int index = i;
    int x = vec[index]; //确定基准数

    while ( i < j)
    {
        //从后向前找,找到一个小于基准数的进行交换
        while (i < j && vec[j] >= x)
            j--;
        if (i < j)
        {
            vec[i] = vec[j];
            i++;
        }

        //从前向后找,找到一个大于基准数的进行交换
        while(i < j && vec[i] < x)
            i++;
        if(i < j)
        {
            vec[j] = vec[i];
            j--;
        }
    }

    vec[i] = x;

    return i;
}

另外一种实现方法

int partition(vector<int>& vec,int left,int right)
{
    //int i = left + rand()%(right -left +1);
    //swap(&vec[i], &vec[right]);

    int pivot =  vec[right];
    int index = left;

    for(int i = left; i < right; i++)
    {
        if(vec[i] < pivot)
        {
            swap(&vec[i],&vec[index]);
            index++;
        }
    }

    swap(&vec[right],&vec[index]);

    return index;   
}

(2)分治(递归)进行上述操作,得到最终排序序列。

void quick_sort_core(vector<int> &vec,int low,int high)
{
    if (low < high)
    {
        int index = partition(vec,low,high);
        quick_sort_core(vec,low,index-1);
        quick_sort_core(vec,index+1,high);
    }
}

随机快速排序

对数学比较感兴趣的同学可以看下随机快速排序时间复杂度的证明,同样是O(nlogn)。

与普通快速排序相比,只需把partititon函数中index通过如下函数获取。

int random_index(int low,int high)
{
    srand(time(NULL));
    return low + rand()%(high - low + 1);
}

常见的优化思路

(1)基准值的确定。可以通过如些操作降低递归次数,提高效率:通过比较第一个值、中间值、最后一个值,得到中间大小的值作为基准值,从而保证一趟快速排序很快达到平衡。

(2)在快速排序算法内部,内置插入排序算法,当未排序的数字个数小于6(印象中是这个值)的时候调用插入排序算法进行排序。

下面的冒牌排序、推排序,以Top K问题作为示例

2.冒泡排序

时间复杂度O(Kn),思路不错,需要把数据一次读进内存

void bubble_sort(vector<int> vec,int k)
{
    int index = 0;
    for (int i = 0; i < vec.size()-1; i++)
    {
        if (index < k)
        {
            for(int j = i+1; j < vec.size(); j++)
            {
                if (vec[i] > vec[j])
                {
                    swap(&vec[i],&vec[j]);
                }
            }
        }
        else
            break;
    }
    output_vec(vec,k);
}

3.堆排序

注:最终取得Top K小值,需要使用最大堆

(1)下面是基本堆的一些操作实现

//最大堆
void adjust_heap(vector<int> &vec,int i,int n)
{
    int tmp;
    int child;  

    for (tmp = vec[i]; 2*i+1 < n; i = child)
    {
        child = 2*i+1;

        if(child != n-1 && vec[child] < vec[child+1])   //找到更大的子节点
            child++;

        if (tmp < vec[child])      //满足条件则交换
            vec[i] = vec[child];
        else
            break;
    }

    vec[i] = tmp;
}

void push_heap(vector<int> &vec,int num)
{
    vec.push_back(num);
    int i;

    for (i = vec.size()-1; i>0 && num > vec[(i-1)/2]; i = (i-1)/2)
        vec[i] = vec[(i-1)/2];

    vec[i] = num;
}

void pop_heap(vector<int> &vec)  //int &max
{
    if(vec.empty())
        return;

    //int max = vec[0];
    vec[0] = vec[vec.size()-1];
    vec.resize(vec.size()-1);

    adjust_heap(vec,0,vec.size());

}

void make_heap(vector<int>& vec)
{
    for(int i = vec.size()/2; i >= 0;i--)
        adjust_heap(vec,i,vec.size());
}

void sort_heap(vector<int>& vec)
{
    for(int i = vec.size()-1; i>0 ; i--)
    {
        swap(&vec[0],&vec[i]);
        adjust_heap(vec,0,i);
    }
}

(2)利用如上的数据结构,可以得到Top K数据。时间复杂度O(nlogk),不需要一次读进内存

    vector<int> need_sort_vec;
    for (i = 0; i < vec.size(); i++)
    {
        if(i < k-1)
        {
            cout << i << endl;
            need_sort_vec.push_back(vec[i]);
        }
        else if(i == k-1)
        {
            need_sort_vec.push_back(vec[i]);
            make_heap(need_sort_vec);
        }
        else
        {
            push_heap(need_sort_vec,vec[i]);
            pop_heap(need_sort_vec);
        }
    }
    sort_heap(need_sort_vec);
    output_vec(need_sort_vec,need_sort_vec.size());

4.归并排序

时间复杂度为O(nlogn),是一种稳定的基于比较排序算法

分解任务一

将有二个有序数列vec[first…mid]和vec[mid…last]合并。

void merge_array(vector<int> &vec,int first,int mid,int last,vector<int> &tmp)
{
    int i = first, j = mid+1;
    int k = 0;

    while (i <= mid && j <= last)
    {
        if(vec[i] < vec[j])
            tmp[k++] = vec[i++];
        else
        {
            tmp[k++] = vec[j++];
            //vec[j]和前面每一个数都能组成逆序数对 
            inverse_pairs_count += mid - i + 1;
        }
    }

    while (i <= mid)
        tmp[k++] = vec[i++];

    while(j <= last)
        tmp[k++] = vec[j++];

    //将排好序的tmp赋值给vec
    for (i = 0;i < k; i++)
        vec[first+i] = tmp[i];

分解任务二

分治法

void merge_sort_core(vector<int>& vec,int first,int last,vector<int>& tmp)
{
    if (first < last)
    {
        int mid = first + (last - first)/2;
        merge_sort_core(vec,first,mid,tmp);   //左边排好序
        merge_sort_core(vec,mid+1,last,tmp);  //右边排好序
        merge_array(vec,first,mid,last,tmp);  //再将两个有序数列排序
    }
}

逆序对与归并排序的关系

在进行有序序列合并过程中,一旦出现后面的数据比前面的小,则出现逆序对。

只需添加一行代码即可。

    while (i <= mid && j <= last)
    {
        if(vec[i] < vec[j])
            tmp[k++] = vec[i++];
        else
        {
            tmp[k++] = vec[j++];
            //vec[j]和前面每一个数都能组成逆序数对 
            inverse_pairs_count += mid - i + 1;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值