排序用到的结构与函数:
#define MAXSIZE 10
typedef struct
{
int r[MAXSIZE+1];
int lenght;
}SqList;
1.冒泡排序
最简单的排序实现
思想:将第一个元素与后面的元素逐个比较,如果第一个元素大于比较的元素就跟第一个元素交换,再将第二个元素与后面的所有元素比较,如果第二个元素大于比较的元素就跟第二个元素交换,再是第三个元素,第四个元素,依次下去。
代码实现:
void BubbleSort0(SqList *L)
{
int i,j;
int t;
for(i=1;i<L->length;i++)
{
for(j=i+1;j<=L->length;j++)
{
if(L->r[i]>L->r[j]) //L是地址,所以这边可以直接进行交换操作,如果是值复制,就不行了
{
t = L->r[i];
L->r[i] = L->r[j];
L->r[j] = t;
}
}
}
}
注意点:1.第一个for循环执行了L->length-1次,因为倒数第二个元素跟后面的比较的时候已经是最后一组比较了
2.这里的L结构体的首地址,所以这里可以直接在子函数中交换结构体中数组的值
2.标准的冒泡排序法
思想:从表尾部开始,两两比较相邻的元素,如果反序则交换,知道没有反序为止,一共要循环执行n-1次(n是元素个数)
第一种写法:
void BubbleSort(SqList *L)
{
int i,j;
int t;
for(i=1;i<L->length;i++)
{
for(j=L->length;j>=i+1;j--)
{
if(L->r[j-1]>L->r[j])
{
t = L->r[j-1];
L->r[j-1] = L->r[j];
L->r[j] = t;
}
}
}
}
第二种写法:
void BubbleSort(SqList *L)
{
int i,j;
int t;
for(i=1;i<L->length;i++)
{
for(j=L->length-1;j>=i;j--)
{
if(L->r[j+1]<L->r[j])
{
t = L->r[j];
L->r[j] = L->r[j+1];
L->r[j+1] = t;
}
}
}
}
说明:1.首先是大的循环比较L->length-1次
2.两种写法其实一样,if中大的值来决定j的值,小的值来决定for中的循环停止条件。
改进的冒泡排序
思想:如果带排序的序列是{2,1,3,4,5,6,7,8,9},那么第一轮i=1循环后就已经有序,但算法依旧执行了下面的L->length-2次循环,所以加一个标识位,有序后退出循环
void BubbleSort2(SqList *L)
{
int flag = 1;
int i,j,t;
for(i=1;i<L->length&&flag;i++)
{
flag = 0;
for(j=L->length-1;j>=i;j--)
{
if(L->r[j+1]<L->r[j])
{
flag = 1;
t = L->r[j];
L->r[j] = L->r[j+1];
L->r[j+1] = t;
}
}
}
}
冒泡排序的复杂度分析:
假设要排序的表长为n
最好的情况:要排序的表本身有序,这样在i=1第一次循环后,就退出了for循环,一共比较了n-1次
最坏的情况:要排序的表本身逆序,这样i=1时,比较了n-1次,当i=2时,比较了n-2次,所以中的比较次数为1+2+.......+n-1次,等于n(n-1)/2
2.简单选择排序
思想:最简单排序从第一个元素开始,将第一个元素跟后面的每个元素比较,如果大于后面的元素进行交换,这样会执行很多次无效的交换,简单选择排序是不急于交换,它只是记住最小值的下标,在一次循环后,将第一个元素直接与最小值交换,虽然比较的次数没有改变,但交换的次数减小了。同样的步骤将第二个元素与后面的比较。
void SelectSort(SqList *L)
{
int i,j,min,t;
for(i=1;i<L->length;i++)
{
min = i;
for(j=i+1;j<=L->length;j++)
{
if(L->r[min]>L->r[j])
{
min = j;
}
}
if(min!=i)
{
t = L->r[min];
L->r[min] = L->r[i];
L->r[i] = t;
}
}
}
算法复杂度分析:
不管是最好的情况还是最坏的情况,简单排序与冒泡排序比较的次数并未改变,但是相比于冒泡排序它的交换的次数减少了,顺序下一次也不用交换,逆序下交换n-1次。
3.直接插入排序
直接插入排序的基本操作是将一个元素插入到已经排好序的有序表中,从而得到一个新的元素加一的有序表。
代码实现:
void InsertSort(SqList *L)
{
int i,j;
for(i=2;i<=L->length;i++)
{
if(L->r[i]<L->r[i-1])
{
L->r[0] = L->r[i];
for(j=i-1;L->r[j]>L->r[0];j--)
{
L->r[j+1] = L->r[j];
}
L->r[j+1] = L->r[0];
}
}
}
说明:直接插入排序是将元素插入已经排好的有序表中,将新元素放到最后,假设该元素的下标为i,首先将该元素赋值给下标为0的元素,现在下标从1到i-1的元素已经有序,那么从下标为i-1的元素开始与下标为0的元素对比,如果下标为i-1的元素大于下标为0的元素,那么就将下标为ii-1的元素向后移动一位,再将下标为i-2的元素与下标为0的元素对比,重复上面的步骤,最终到达下标为j的元素时,下标为j的元素不大于下标为0的元素,这时比较,将下标为0的元素赋值到下标为j+1的元素。整个过程就像插牌一样。这里注意是下标为j的元素的下一个元素是用来被替换的,也是用来被 插入的位置。
性能分析:
最好的情况:有序{2,3,4,5,6},这时比较只执行了if(L->r[i]<L->r[i-1]),比较执行了4次,没有移动记录,如果有n个元素,比价就执行了n-1次,没有移动记录。
最坏的情况:逆序{6,5,4,3,2},当i=2时,if(L->r[i]<L->r[i-1])比较了一次,for(j=i-1;L->r[j]>L->r[0];j--)比较了一次,所以移动比较了2次,类推,一共比较了2+3+4+...+n=
(n-1)(n+2)/2次,移动了(n-1)(n+4)/2次。平均比价和移动了n*2/4
4.希尔排序
思路:分割成若干个子序列,此时每个子序列排序的元素的个数就比较少了,然后在这些子序列内分别进行直接插入排序,当整个序列都基本有序时,再对全体记录进行一次直接插入排序。基本原理:一次交换减少的逆序数有可能不止一个。
代码实现:
void ShellSort(SqList *L)
{
int i,j;
int increment = L->length;
do
{
increment = increment/3+1;
printf("%d ",increment);
printf("\n");
for(i=increment+1;i<=L->length;i+=increment)
{
if(L->r[i]<L->r[i-1])
{
L->r[0] = L->r[i];
for(j=i-increment;j>0&&L->r[j]>L->r[0];j-=increment)
{
L->r[j+increment] = L->r[j];
}
L->r[j+increment] = L->r[0];
}
}
}while(increment>1);
}
5.快速排序
基本思想:通过一趟排序将待排元素分割成独立的两个部分,其中一部分的元素都比两一部分的元素小,则可分别对这两部分元素继续进行排序,以达到整个有序的目的。快速排序是对冒泡排序的改进,原理:一次不相邻逆序元素的交换,可能减少的逆序数目不止一个。
示例:待排序序列{48,62,35,77,55}
{35,62},48,{77,55}
{35},48,{62,77,55}
{55,77,62}
{55},62,{77}
代码实现:
void QuickSort(SqList *L)
{
QSort(L,1,L->length);
}
void QSort(SqList *L,int low,int high)
{
int pivot;
if(low<high)
{
pivot = Partition(L,low,high);
QSort(L,low,pivot-1);
QSort(L,pivot+1,high);
}
}
int Partition(SqList *L,int low,int high)
{
int pivotkey;
pivotkey = L->r[low];
while(low<high)
{
while(low<high&&L->r[high]>pivotkey)
high--;
swap(L,low,high);
while(low<high&&L->r[low]<pivotkey)
low++;
swap(L,low,high);
}
return low;
}
说明:QSort函数中的if(low<high)递归控制条件,low<high区间长度大于1,low=high区间长度为1。