您的位置:首页 > 理论基础 > 数据结构算法

数据结构之快速排序

2014-10-23 23:55 274 查看
小目录

1.冒泡排序

2.快速排序

1.冒泡排序

冒泡排序我想可能是学习C语言接触的第一个排序算法了,相信每一本C语言的数组的章节都会介绍这个排序算法的。下面我们就从一个类冒泡排序算法来一步步优化这个算法。

冒泡排序的基本思想:它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。

下面给出一个类冒泡排序的程序,注意这不是冒泡排序,但其过程是有冒泡排序的特点的。

/*
* @description:类冒泡排序
*/
void BubbleSort_1(SqList *L) {
int i,j;
//注意这里没有等于
for(i = 1; i < (*L).length ; i++) {
for(j = i + 1; j <= (*L).length; j++)
if((*L).r[i].key > (*L).r[j].key)
swap(L,i,j);
}
}


这里给出调用到的swap函数,后面不会再提示:

/*
* @description:用于交换要节点元素位置
*/
void swap(SqList *L,int i,int j) {
ElemType temp;

temp = (*L).r[i];
(*L).r[i] = (*L).r[j];
(*L).r[j] = temp;
}


可以看到这里并不是相邻两个元素的比较交换,接下来我们来看看常见的冒泡排序算法:

/*
* @description:冒泡排序,相邻两元素比较
* @more:这个相对于上面的优化在于冒泡在
冒泡的过程也把相关的元素的位置
上升了
*/
void BubbleSort_2(SqList *L) {
int i,j;

for(i = 1; i < (*L).length ; i++) {
for(j = (*L).length - 1; j >= i; j-- )
//注意这里的不同
if((*L).r[j].key > (*L).r[j + 1].key)
swap(L,j + 1,j);
}
}




从两个程序可以看出来,冒泡在比较的过程中不仅仅把最小的(或者最大的元素)往上冒泡了,还把相关的元素的也往上冒泡了,这样在无形中就减少下一次的移动次数。

性能分析:若是反序的,需要进行



趟排序。每趟排序要进行



次关键字的比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:





冒泡排序的最坏时间复杂度为



。综上,因此冒泡排序总的平均时间复杂度为



,空间复杂度为O(n)。冒泡排序是稳定的排序算法,因为在相邻两元素比较时只有小于或是大于才交换。

改进冒泡排序:

再次感受到感受到前人的牛逼,但是有点优化的规律是这样的:要充分利用前面已经进行的比较来减少当前所需进行的比较(还记得KMP算法吗)。这里所谓的改进的冒泡算法就是当待排序列部分有序时,比如 序列2 ,1,3,4,5,6在调整一次后为1,2,3,4,5,6这时候如果还按照上面的冒泡算法还需要进行后面的比较,这不是浪费吗!比如,现在再次进行调整(注意这里从后面扫描),则发现一路往上都没有需要交换的,这是不是代表着有序了,如果存在无序必然存在交换,于是我们想到用一个标志来记录是否发生了交换。实现程序如下:

/*
* @description:优化之后的冒泡排序
* @more:这是顺序比较的,在部分有序的情况下体现效果
在最坏的情况下和上面的冒泡排序是一样的
*/
void BubbleSort_3(SqList *L) {
int i,j,flag;

flag = 1;
for(i = 1; i < (*L).length && flag; i++) {
flag = 0;
for(j = (*L).length - 1; j >= i; j--)  {
if((*L).r[j].key > (*L).r[j + 1].key) {
swap(L,j + 1,j);
flag = 1;
}
}
}

}
性能分析:优化后的算法只是在序列部分有序时才体现效果,其时间复杂度依然是O(n的平方),为稳定排序算法。

2.快速排序

快速排序是对冒泡的一种改进,它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

/*
* @descrption:快速排序具体交换操作,函数最后返回最后的基准的位置
* @more:基本思想:设置一个基准,从high一直往前找,找到第一个比
基准值小,交换他们两;接着从low一直往后找,找到第一个
比基准大的,交换他们两
这里的基准预设为的第一个元素
*/
int Partition(SqList *L,int low,int high) {
KeyType pivkey;
//使用第0个空位来暂存基准值
(*L).r[0] = (*L).r[low];

pivkey = (*L).r[low].key;	//用当前的顺序表的第一个元素关键词作为基准值

while(low < high) {
//一直往前找
while(low < high && (*L).r[high].key >= pivkey)
high--;
//找到直接复制
(*L).r[low] = (*L).r[high];

//一直往后找
while(low < high && (*L).r[low].key < pivkey)
low++;
(*L).r[high] = (*L).r[low];

}

(*L).r[low] = (*L).r[0];

return low;
}

/*
* @description:递归排序
*/
void QSort(SqList *L,int low,int high) {
int pivloc;

if(low < high) {
pivloc = Partition(L,low,high);
QSort(L,low,pivloc - 1);
QSort(L,pivloc + 1,high);
}
}

/*
* @description:快速排序
* @more:快速排序是目前认为最好的一种内部排序方法
*/
void QuickSort(SqList *L) {
QSort(L,1,(*L).length);
}
可以看出来算法设置一个基准,从high一直往前找,找到第一个比基准值小,交换他们两;接着从low一直往后找,找到第一个比基准大的,交换他们两

这里的基准预设为的第一个元素

csdn这玩意不知道怎么了,老是自动给图片加水印把内容挡住了,以后再补图吧。

性能分析:快速排序的最坏情况下的时间复杂度为O(n的平方)但是其平均时间复杂度为O(nlogn),这是前面介绍的冒泡和插入排序所不具备的。所以快速排序被认为是目前性能最好的内部排序算法。该排序算法为不稳定排序算法。

最后附上源码地址:GitHub
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: