排序【3】交换排序

冒泡排序

基本思想:为什么叫冒泡排序呢,,是因为每一次排序就像是鱼儿吐泡泡,一个个泡泡从水底到水面会变得越来越大,泡泡就像元素,随着算法的进行,较大的元素就会到后面去。如果升序排列,那么就会对相邻两个元素进行比较,如果前者大于后者,那么就交换这了两个元素,直到最大的元素被排到最后一个位置,第一趟冒泡完成,进行第二趟冒泡,把次大的元素排到倒数第二的位置,重复这个过程。直到所有的元素都被冒泡到合适的位置,停止。
具体步骤:

这里写图片描述
代码实现

template<class T>//仿函数,本质是函数对象,对()进行重载,功能类似于函数  
struct Less  
{  
    bool operator ()(const T& left,const T&right)  
    {  
        return left < right;  
    }  

};  
template<class T>  
struct Greater  
{  
    bool operator ()(const T& left, const T&right)  
    {  
        return left > right;  
    }  
};  
template < class T,class compare=Less<T>>//使用模板参数,处理不同类型数据,以及指定排序方式,这里默认降序  
void bubblesort(T* array,int sz)  
{  
    bool IsChanged = false;//  
    for (int i = 0; i < sz - 1; i++)  
    {  
        bool IsChanged = false;  
        for (int j = 0; j < sz - 1 - i; j++)  
        {  
            if (compare()(array[j], array[j + 1]))//仿函数  
                swap(array[j], array[j + 1]);  
                IsChanged = true;  
        }  
        if (IsChanged == false)//如果一次都没交换,结束循环,提高程序效率  
            break;  
    }  

} 

算法性能
时间复杂度:平均时间复杂度O(N^2)最好和最坏情况下。
空间复杂度:O(1)
稳定性:稳定


快速排序

基本思想:首先,在待排序列中选择一个key值,然后将其他元素以此和key值比较,比key值小的放到key的前面,比key大的放到key的后面,这样就吧序列分成两个区间,然后递归起左右区间,直到区间只剩一个元素的时候,那么停止递归,此时可以认为子区间是已经有序,然后一层一层的往上返回,结束后,整个区间就已经排好序。具体看下图。
这里写图片描述
具体步骤

  1. 首先,在待排序的区间选出来一个基准值key.
  2. 用key依次跟去他元素比较,把比key小的元素放到key的左边,比key大的放到key的右边,此时key就像是一个分水岭,将数据分成两部分。
  3. 递归排序key的左右区间,重复上述1,2步骤。
  4. 直到区间元素只剩一下的时候,停止递归。

实现方法
我目前了解的实现快排的方法有三种,分别是左右指针法,挖坑法,和前后指针法。
1.左右指针法
实现步骤:

  1. 定义两个指针left和right分别指向待排序区间的第一个元素和最后一个元素,这里我们选择left指向的元素为key,那么左指针就需要先走。
  2. 左指针向右走,当寻找到比key大的元素就停下,右指针向左走,找到比key小的元素就停下来。
  3. 如果两个指针没有相遇,就交换左右指针指向的元素。
  4. 直到两个指针相遇就结束,然后把左指针指向的位置赋值为key。
    思想流程图:
    这里写图片描述
    实现代码
int PartSort1(int *array, int left,int right)//区间左毕右毕
{
    int key = right;
    while (left < right)
    {
        //左指针寻找比key大的元素
        while (left < right&&array[left] < key)
        {
            left++;
        }
        while(left<right&&array[right]>key)//右指针寻找比key小的元素
        {
            right--;
        }
        if (array[left] != array[right])
        {
            swap(array[left], array[right]);
        }
    }
    array[left] = key;
    return left;
}   

2.挖坑法
具体步骤:

  1. 首先用left和right两个指针标记待排序列第一个和最后一个元素,这里选择key为right指向的元素,并且第一个坑设置在key的位置.
  2. 让left向右寻找比key大的元素,找到了就停下来,把这个元素放到坑的位置,把left指向的位置设为新的坑。
  3. 接下来right向左走,找比key小的元素,找到了就把这个元素填入坑中,并把right现在的位置设为新的坑。
  4. 重复上述2,3步骤,直到left和right相遇停止,然后最后把坑的位置设置成key.
    思想流程图:
    这里写图片描述
    实现代码:
int PartSort2(int *array, int left, int right)//挖坑法
{
    int key = right;
    int keng = right;
    while (left < right)
    {
        while (left < right&&array[left] < key)
        {
            left++;
        }
        array[keng] = array[left];
        keng = left;
        while (left<right&&array[right]>key)
        {
            right--;
        }
        array[keng] = array[right];
        keng = right;
    }
    array[keng] = key;
    return keng;

}

3.前后指针法
具体步骤:

  1. 先让pcur指向第一个元素,prev指向pcur的前一个元素,此时选取最后一个元素为key。
  2. pcur向后走找到比key小的元素就停止,++prev,当prev和pcur不相等的时候,交换两个位置的元素,相等不交换。
  3. pcur一直往后走,重复2过程,当走到右边界的时候,++prev,并且交换prev和pcur位置的元素。
    思想流程图:
    代码实现:
int PartSort(int *array, int left, int right)
{
    assert(array);
    int key = right;
    int pcur = left;
    int prev = left - 1;
    while (pcur<right)
    {

        if (array[++prev] !=array[ pcur]&&array[pcur]<key)
        {
            swap(array[prev], array[pcur]);
        }
    }
    ++prev;
    swap(array[prev], array[pcur]);
}

快排的性能

时间复杂度:快排是一种已知道的最快的排序算法,它的平均时间复杂度为O(NlogN),但是它也是一种不稳定的算法,当它的基准值选取的不合理的时候,那么就会造成O(N^2)的时间复杂度
空间复杂度:O(logN)
稳定性:不稳定
所以说,选择合理的基准值key,影响着快排的效率,上述的基准值都是取的是最后一个元素,下面我们将算法进行改进,通过几种合理的取数法,使得快排左右区间尽可能的分配均匀。
1.三数取中法
基本思想:我们为了把区间尽可能的分配均匀,在取基准值的时候,选取数组最左边,最中间,最右边的元素三个元素中中间大小的元素作为基准值。

int GetMidIndex(int *array, int left, int right)
{
    int mid = left + ((right - left) >> 1);
    if (array[left] < array[right])
    {
        if (array[mid] < array[left])
            return left;
        else if (array[mid] > array[right])
            return right;
        else
            return mid;

    }
    else
    {
        if (array[mid] > array[left])
            return left;
        else if (array[mid] < array[right])
            return right;
        else
            return mid;
    }
}

2.小区间优化
基本思想:当区间较小时候,一般认为有13个元素以下,这个时候,快速排序就没有直接插入的性能好了,因为当区间比较小的时候,区间划分的就比较多,快排就像一颗二叉树一样,每一次递归都相当于增加一层高度,当区间比较小的时候,就会快速增加二叉树高度,降低了效率,因此我们在划分到小区间的时候就改为插入排序。
代码实现:

void InsertSort(int *array,int size)
 {
    for (int i=1;i<size;i++)
    {
        int end = array[i];
        int j = i - 1;
        while (j >= 0 && array[j] > end)
        {
            array[j + 1] = array[j];
            j--;
        }
        array[j + 1] = end;

    }

3.非递归实现
基本思想:我们前面讲的快排都是基于递归子区间来实现的,而当数据元素比较多的时候,难免递归的次数会非常多,而每次函数的递归都是一个函数的栈帧过程,十分消耗时间,因此可以借助栈这种方式,来实现递归转非递归。
代码实现:

void QuickSortNoeR(int*array, int left, int right)
{
    stack<int> s;
    s.push(left);
    s.push(right);
    while (!s.empty())
    {
        int start = s.top();
        s.pop();
        int finish = s.top();
        s.pop();
        int div = PartSort1(array, start, finish);
        if (start < div - 1)
        {
            s.push(div - 1);
            s.push(start);
        }
        if (finish > div + 1)
        {
            s.push(finish);
            s.push(div + 1);
    }

    }

}

这就是对于交换排序,本人的学习之谈。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值