数据结构(c语言)–排序算法之交换排序(冒泡排序、快速排序)
一:交换排序
思想:根据序列两个元素的关键字的比较结果来交换这两个记录在序列中的位置。
1、冒泡排序
(1):算法思想
从后往前(从前往后)两两比较,若为逆序,则交换,知道序列比较完成—为一趟冒泡;结果是将最小的元素交换到待排序序列的第一个位置(或关键字最大的元素交换到待排序列的最后一个位置)(位置已定死了),前一趟确定的元素不在参与比较,每趟冒泡把最小或者最大的位置放到序列的最终位置;这样最多有n-1趟冒泡就能把所有的元素排好序。
(2):算法实现
#include "stdio.h"
typedef int Element;
void swap(int *a,int *b){ //交换值
int temp;
temp=*a;
*a=*b;
*b=temp;
}
void Print(int a[],int n){
for (int i = 0; i < 9; ++i) {
printf("%d ,",a[i]);
}
printf("\n");
}
void BubbleSor(Element a[],int n){
int i,j;
for (i = 0; i < n-1; ++i) {
int flag=0; //本趟冒泡是否发生交换的标志
for ( j = n-1; j >i ; j--) //一趟冒泡的过程
if(a[j]<a[j-1]){
swap(&a[j],&a[j-1]); //元素交换
flag=1;
}
Print(a,n);
if(flag==0) //本趟遍历后没有发生交换,表明表已有序
return;
}
}
int main(){
Element a[9]={44,49,38,16,97,76,13,27,49}; //初始化一个数组,a[0]为哨兵
printf("初始序列:");
Print(a,9);
printf("\n排序过程:\n");
BubbleSor(a,9);
return 0;
}
/**
初始序列:44 ,49 ,38 ,16 ,97 ,76 ,13 ,27 ,49 ,
排序过程:
13 ,44 ,49 ,38 ,16 ,97 ,76 ,27 ,49 ,
13 ,16 ,44 ,49 ,38 ,27 ,97 ,76 ,49 ,
13 ,16 ,27 ,44 ,49 ,38 ,49 ,97 ,76 ,
13 ,16 ,27 ,38 ,44 ,49 ,49 ,76 ,97 ,
13 ,16 ,27 ,38 ,44 ,49 ,49 ,76 ,97 ,
*/
(3)性能分析
- 空间性能:O(1)
- 时间复杂度:O(n*n)
- 稳定性: if(a[j]<a[j-1])才交换;所以是稳定的
- 每一趟都会唯一确定一个元素的位置
2、快速排序(在内部排序中的最快的排序)
(1):算法思想
基本思想是基于分治法的:在待排序表L[1…n]中任取一个元素pivot作为枢轴(基准–通常是首元素),通过一趟排序将待排序表分为两个部分L[1,k-1]和L[k+1,n];前半部分L[1,k-1]小于基准元素pivot,后半部分L[k+1,n]大于基准元素pivot,pivot最终放在L[k]上,这个过程成为一次划分或者一趟快速排序。然后,分别递归地对两个子表重复上述的过程,直至每部分内只有一个元素或者空为止。
(2):算法实现
#include "stdio.h"
typedef int Element;
void Print(int a[],int n){
for (int i = 0; i < 9; ++i) {
printf("%d ,",a[i]);
}
printf("\n");
}
int Partition(Element a[],int low ,int high){
//Partition一次操作后,表中的元素被一分为二
Element pivot=a[low]; //将表中的第一个元素作为基准元素
while (low<high){ //循环跳出条件
while (low<high&&a[high]>=pivot)
--high;
a[low]=a[high]; //将比pivot基准小的元素移动到左端
while (low<high&&a[low]<=pivot)
++low;
a[high]=a[low]; //将比pivot基准大的元素移动到右端
}
a[low]=pivot;
Print(a,9);
return low;
}
void QuickSort(Element a[],int low,int high){
if(low<high){ //递归跳出条件
//Partition()就是划分操作,将表A[low....high]划分为满足上面两个条件的子表
int pivotpos= Partition(a,low,high); //划分
QuickSort(a,low,pivotpos-1); //依次对两个子表进行递归排序
QuickSort(a,pivotpos+1,high);
}
}
int main(){
Element a[9]={44,49,38,16,97,76,13,27,49}; //初始化一个数组,a[0]为哨兵
printf("初始序列:");
Print(a,9);
printf("\n排序过程:\n");
QuickSort(a,0,8);
return 0;
}
/**
初始序列:44 ,49 ,38 ,16 ,97 ,76 ,13 ,27 ,49 ,
排序过程:
27 ,13 ,38 ,16 ,44 ,76 ,97 ,49 ,49 ,
16 ,13 ,27 ,38 ,44 ,76 ,97 ,49 ,49 ,
13 ,16 ,27 ,38 ,44 ,76 ,97 ,49 ,49 ,
13 ,16 ,27 ,38 ,44 ,49 ,49 ,76 ,97 ,
13 ,16 ,27 ,38 ,44 ,49 ,49 ,76 ,97 ,
*/
(3)性能分析
-
时间复杂度:
最坏情况:O(n*n)初始排序表基本有序或者基本逆序时(不对称)
最好情况:O(log2n)在进行一次Partition()能做到一次平衡的划分,使得到的两个子问题的大小都不可能大于n/2,在这种情况下,快排的效率会大大上升。 -
空间复杂度:最好O(log2n)、最坏O(n)、平均O(log2n)—都取决递归调用栈的深度。
-
稳定性:不稳定。反例L={3,2,2}
注意:在快排中,并不会产生有序的子序列,但没趟排序后会将枢轴元素(基准)放到最终的位置上。