交换排序--冒泡排序、快速排序

所谓交换,就是指根据序列中两个元素关键字的比较结果,来对换这两个元素在序列中的位置。基本思想就是元素两两比较,若逆序则交换,直至所有元素排序好。两个重要的交换排序算法:冒泡排序、快速排序。

  1. 冒泡排序

说白了就是,从最前面两个元素比较,逆则换,直至比到最后两个元素结束此次循环,再从前面循环的第一个元素后移一个开始比较,直到最后只剩一个元素,完成排序。

第一种从前往后进行比较:

typedef int Elemtype;
void BubbleSort(Elemtype a[],int n)//大的往后冒泡(从前往后)
{
    for(int i=0;i<n-1;i++)//外层循环n-1层 
    {
        bool flag=false;
        for(int j=0;j<n-1-i;j++)//内层循环,最后面的元素已经有序,所以n-1-i 
        {
            if(a[j]>a[j+1])
            {
                swap(a[j],a[j+1]);               
                flag=true;
            }
        }
        if(!flag)//若没进行交换操作,则说明序列已经有序 
            break;
    }
}

第二种从后往前进行比较:

typedef int Elemtype;
void BubbleSort(Elemtype a[],int n)//小的往前冒泡(从后往前)
{
    for(int i=0;i<n-1;i++)
    {
        bool flag=false;
        for(int j=n-1;j>i;j--)
        {
            if(a[j-1]>a[j])
            {
                swap(a[j-1],a[j]);//交换
                flag=true;
            }
        }
        if(!flag)//若没进行交换操作,则说明序列已经有序 
            break;
    }
}

空间效率:空间复杂度为

时间效率:最坏情况下时间复杂度为,平均时间复杂度也为

强调:冒泡排序中所产生的的有序子序列一定是全局有序的,不同于插入排序,也就是说,有序子序列中的所有元素一定大于(小于)无序序列中的所有元素,这样每一趟排序都会将一个元素放置到其最终的位置上。

2.快速排序

快排是基于分治法的思想的排序算法,这里就不再赘述分治了。基本思想是:选择一个基准数,把比它小的数放在它的左边,比它大的数放在它的右边,然后对左右两个子序列递归进行快速排序。

说白了就是填坑,先取出一个基准元素,就相当于序列的该位置形成一个坑,一前一后两个箭头往中间移动(设为1箭头和2箭头),2指向的元素比基准小就放进上一个坑中,自己这里就形成一个坑;1指向的元素比基准大就放进2形成的坑中,直到1和2指向同一个元素退出循环,完成排序。

实现过程:

1.选定一个基准数。通常情况下,我们选择序列的第一个元素作为基准数。

2.从序列的右端开始遍历,找到第一个小于基准数的元素,将其放到序列的左端。

3.从序列的左端开始遍历,找到第一个大于基准数的元素,将其放到序列的右端。

4.重复执行第2步和第3步,直到左右两个指针相遇。此时,将基准数放到相遇的位置上。

5.对基准数左右两个子序列进行递归排序。

// Partition函数用于将序列分为两个子序列,并返回基准数的下标
int Partition(vector<int> &nums,int left,int right)
{
    int tep=nums[left]; //选择第一个元素作为基准
    int i=left,j=right;
    while(i<j)
    {
        while(i<j && nums[j]>=tep)//从后往前找小于基准的元素
            j--;
        while(i<j && nums[i]<=tep)// 从前往后找大于基准的元素
            i++;
        swap(nums[i],nums[j]);// 交换i和j位置上的元素
    }
    // 因为基准数在left位置上,i位置上的元素一定是小于等于基准数的,所以要把i上的值给left
    nums[left]=nums[i];
    nums[i]=tep;// 将基准数放到相遇的位置i上

    return i;//此时的i就是下一次循环的基准
}

void QuickSort(vector<int> &nums,int left,int right)// 快速排序函数
{
    if(left>=right)
        return;
    int mid=Partition(nums,left,right); //将序列分为两个子序列
    QuickSort(nums,left,mid-1); // 递归处理左子序列
    QuickSort(nums,mid+1,right); // 递归处理右子序列
}

空间效率:因为快排是递归的,需要借助递归工作栈来保存每层递归调用的信息,最好的情况下是O(log2N);最坏的情况则需要进行N-1次调用,栈深度为O(N)。平均情况下为O(log2N)。

时间效率:最坏情况下时间复杂度为O(N^2),平均时间复杂度为O(Nlog2N) 。

强调:快排并不产生有序子序列,但为每趟排序后会将基准元素放到最终位置。快排是所有内部排序算法中平均性能最优的排序算法。

猜你喜欢

转载自blog.csdn.net/weixin_58420524/article/details/129392245