数据结构--排序之交换排序

本节将两种交换排序冒泡排序和快速排序

一、冒泡排序(Bubble Sort)

冒泡排序是最简单的交换排序方法,比较相邻两个记录的关键字,将大的放到右边,小的放到左边,如图所示:

从而使关键字小的左移、大的右移;每一次循环最右边的必定是关键字最大的元素,外面再加一层循环即可得到有序序列。

代码实现:

#include <bits/stdc++.h>
using namespace std;

#define MAXSIZE 100
typedef int KeyType;
typedef struct///定义每个节点信息
{
    KeyType key;
//    InfoType otherinfo;
}RType;
typedef struct///定义顺序表
{
    RType r[MAXSIZE + 1];
    int length;
}SqList;

void BubbleSort(SqList &L)
{
    int flag;
    for(int i = L.length; i >= 2; i --)
    {
        flag = 1;
        ///每一趟循环结束r[i] 就是r[1...i]中最大的数
        for(int j = 2; j <= i; j ++)
        {
            if(L.r[j-1].key > L.r[j].key)
            {
                flag = 0;
                L.r[0] = L.r[j];
                L.r[j] = L.r[j-1];
                L.r[j-1] = L.r[0];
            }
        }
        if(flag)break;   ///当flag为1时,表明序列已经有序,可以结束循环
    }
}

int main()
{
    SqList L;
    printf("请输入序列中输的个数:");
    scanf("%d", &L.length);
    printf("\n请输入序列元素:");
    printf("\n\n");
    for(int i = 1; i <= L.length; i ++)
    {
        scanf("%d", &L.r[i].key);
    }
    BubbleSort(L);
    for(int i = 1; i <= L.length; i ++)
    {
        printf("%5d", L.r[i].key);
    }
    return 0;
}


/***
10
70   30   52   29   50   62   49   86    2   13
***/

复杂度分析:

时间复杂度为O(n^2),空间复杂度为O(1)。

补:冒泡排序的交换次数即数列的逆序对数。

二、快速排序(Quick Sort)

之所以称其为快速排序,他快嘛!(一般(nlog n)就可以解决问题甚至更快)

大体思想:序列r[1...n]

 

a.选取序列中一个元素作为标杆X(或者标记,怎么记都行,╮(╯▽╰)╭),一般选取第一个元素。

b.遍历序列中的元素,将比X小的元素放置X的左边,比X的元素放置X的右边。

c.然后用与a、b同样的分别操作处理X左边和右边的序列。

先感性理解一下ε=(´ο`*))),

看起来有没有像二分

我们先看图说话O(∩_∩)O哈哈~

我们假定排序的是一段区间r[left...right],X选定为区间第一个元素X=r[left]

由经过操作b,我们知道,标杆X左边的元素都比他小,标杆右边的元素都比他大(当然如何将小的移到左边、大的移到右边,需要一些技巧,我们先不管O(∩_∩)O~~,稍后接着说说如何实现)

将标杆所在序列位置记为mid(名义上的,并不是真正意义上的mid,不要在意(#^.^#)),然后就可以最区间r[left, mid-1]r[mid+1, right]进行同样的操作。

经过上面的分析,一顿操作猛如虎,突然发现可以用递归实现。

万事俱备,只差如何实现操作b了,来薛薇的说说

随机给定一个序列: 70   30   52   29   50   62   49    86    2    13 

我们继续看图说话,

X = r[left = 1] = 70,r[0] = r[left](将r[0]空出来,方便操作)

left = 1 的位置空了,从right = 10开始向左遍历,直到有小于X的元素,r[right = 10] = 13 < X,将13赋值给r[left], left加一;

现在right = 10 的位置也空了,从left = 2向右遍历,直到有大于X的元素,此时当left = 8到达元素86(>X)所在位置,将86赋值给r[right = 10],right减一;

此时left = 8 的位置又空了o(╥﹏╥)o,再从right = 9向左遍历,直到有小于X的元素,r[right = 9] = 2 < X,将2赋值给r[left = 8],left 加一 。

此时left  = right = 9,第一趟排序结束,将r[left]X填上,用mid记一下left的位置。

然后就同理处理区间 r[left...mid-1] r[mid+1, right] 了。

注意当区间只有一个元素时,貌似就不用处理了吧,恩,对的。

5趟排序就完事了。

 

补上代码:

#include <bits/stdc++.h>
using namespace std;

#define MAXSIZE 100
typedef int KeyType;
typedef struct///定义每个节点信息
{
    KeyType key;
//    InfoType otherinfo;
}RType;
typedef struct///定义顺序表
{
    RType r[MAXSIZE + 1];
    int length;
}SqList;
int Partition(SqList &L, int left, int right)
{
    int pivotkey = L.r[left].key;
    L.r[0] = L.r[left];
    while(left < right)
    {
        while(left < right && L.r[right].key >= pivotkey)right--;
        L.r[left] = L.r[right];
        while(left < right && L.r[left].key <= pivotkey) left ++;
        L.r[right] = L.r[left];
    }
    L.r[left] = L.r[0];
    return left;
}
void QSort(SqList &L, int left, int right)
{
    int pivotkey;
    if(left < right)
    {
        pivotkey = Partition(L, left, right);
        ///打印每一趟排序的输出
///        for(int i = 1; i <= L.length; i ++)printf("%5d", L.r[i]);
///        printf("\n");
        QSort(L, left, pivotkey - 1);
        QSort(L, pivotkey + 1, right);
    }
}
void QuickSort(SqList &L)
{
    QSort(L, 1, L.length);
}
int main()
{
    SqList L;
    printf("请输入序列的个数:");
    scanf("%d", &L.length);
    printf("\n\n请输入序列:");
    for(int i = 1; i <= L.length; i ++)
    {
        scanf("%d", &L.r[i].key);
    }
    QuickSort(L);
    for(int i = 1; i <= L.length; i ++)
    {
        printf("%5d", L.r[i].key);
    }
    printf("\n");
    return 0;
}
/**
10
70   30   52   29   50   62   49   86    2   13
**/

再来分析一下代码。看起来有二分的思想,但不完全是二分,因为每次X不上的位置不完全是中间位置,但可以近似的看成是二分,所以平均时间复杂度为O(nlog n),但也存在某些特殊的序列使时间复杂度算退化为O(n^2),也由此可见此算法不太稳定。不过不用太担心,一般情况不会遇到那些特殊的序列。由于是用递归实现的所以其空间复杂度不是O(1),大致是O(log n)。

补充:

在C和C++里面都有写好的快速排序函数,可以直接调用

额,(⊙o⊙)…大家还是看其他大佬的博客吧

C语言的:https://blog.csdn.net/zhaozicang/article/details/24174965

C++ 的:https://blog.csdn.net/kuimzzs/article/details/81395347

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值