本节将两种交换排序冒泡排序和快速排序
一、冒泡排序(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