1. 定义
假设含有n个记录的序列为(r1,r2,r3…rn),其相关的关键字分别为{k1,k2,k3…kn},需要确定一种方式,是的其关键字a按照递增(包含相等)的顺序关系,使之成为有序的序列,这样的操作称为排序。
排序的稳定性:两条数据的关键值是一样,排序前有排序后,其相对前后的位置不变,则称为稳定的,反之是不稳定的。
内排序是排序过程中,待排序的所有记录全部都放在内存中,外排序是由于排序记录的个数太多,不能同时放置在内存中,整个排序过程需要的内外存之间多次交换数据才能进行,主要介绍内排序的多种方式。
1.1 排序算法性能
- 时间性能,排序是数据处理中经常执行的一种操作,往往属于系统的核心部分,因此排序算法的时间开销是衡量算法最重要的指标。
- 辅助空间 评价排序算法的另一个主要标准是执行算法所需要的辅助存储空间,辅助存储空间是除了存放待排序所占用的空间之外,执行算法所需要的其他存储空间。
- 算法的复杂度,是指算法本身的复杂度,而不是指算法时间和空间的复杂度,显然算法过于复杂也是影响排序的性能,内排序分为:插入排序,交换排序,选择排序和归并排序。
下列介绍七种排序算法:
排序所需的结构和函数:
#define MAXSIZE 10
typedef struct
{
int r[MAXSIZE];
int length;
}SqListl;
void swap (SqList *L, int i ,int j)
{
int temp=L->r[i];
L->r[i]=L->r[j];
L->[j]=temp;
}
2. 各类算法清单
2.1 冒泡排序
冒牌排序是一种交换排序。
基本思想: 两两比较相邻记录的关键字,如果反序,则可以交换,一直到没有记录为止。
void BubbleSort0(SqList *L)
{
int i,j;
for(i=1;i<L->length;i++)
{
for(j=L->length-1;j>=i;j--)
{
if(L->r[j]>L->r[j+1])
swap(L,j,j+1);
}
}
}
优化冒泡排序,最后一部分的数据已经有序的时候,则不需要再进行排序了,设立标志位进行判断
void BubbleSort0(SqList *L)
{
int i,j;
for(i=1;i<L->length;i++)
{
Status flag=TRUE;
for(j=L->length-1&& flag;j>=i;j--)
{
flag=False;
if(L->r[j]>L->r[j+1])
swap(L,j,j+1);
flag=True;
}
}
}
复杂度:在最坏的情况下,表是逆序的,每次都需要进行比较以及交换,两次循环,故而其复杂度为O(n2)
2.2 简单选择排序
通过n-1次关键字间的比较,从n-i+1个记录中选出关键字最小的记录,并且和第i(1<=i<=n)个记录交换
从待排序堆中找最值进行交换。
void SelectSort(SqList *L)
{
int i,j,min;
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(i!-min)
swap(L,i,min)
}
}
分析:复杂度依旧是O(n2)
2.3 直接插入排序
直接插入排序的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的,记录数增1的有序表。后续的单元逐个进行对比如果比当前元素大,则移位,直到合适的位置插入。
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->L[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];
}
}
}
2.4希尔排序
首先它把较大的数据集合分割成若干个小组(逻辑上分组),然后对每一个小组分别进行插入排序,此时,插入排序所作用的数据量比较小(每一个小组),插入的效率比较高:
在组内进行排序,而后重组,得到了基本有序的数组,基本有序是指小的数组基本在前,大的数组基本在后。
然后缩小增量为上个增量的一半:2,继续划分分组,此时,每个分组元素个数多了,但是,数组变的部分有序了,插入排序效率同样比高:
同理对每个分组进行排序(插入排序),使其每个分组各自有序:
viod ShellSort(SqList *L)
{
int i,j;
int increment=L->length;
do
{
increment=increment/3+1;
for(i=increment+1;i<=L->length;i++)
{
if(L->r[i]<L->r[i-increment])
{
L->r[0]=L->r[i];
for(j=i-increment;j>0**L->r[o]<L->r[j];j-=increment)
L->r[j+increment]=L->r[j];
L->r[j+increment]=L->r[0];
}
}
}
while(increment>1);
}
2.5 堆排序
堆是具有下列性质的完全二叉树:每个节点的值都大于或者等于其左右孩子节点的值,称为大顶堆,或者每个节点的值都小于其左右节点的值,称为小顶堆。
堆排序:利用堆进行排序的方法,基本思想是,将待排序的序列构成一个大顶堆,此时整个序列的最大值就是根节点,让其与末尾元素交换,此时末尾元素为最大值,剩余节点重新再组成一个新的堆,得到根节点是剩余元素中最大的值,此时进行交换操作,重复循环,得到有序序列。
void HeapSort(SqList *L)
{
int i;
for(i=L->length/2;i>0;i--)
{
HeapAdjust(L,i,L->length);
} //构建初始的大顶堆
for(i=L->length;i>1;i--)
{
swap(L,1,I);
HeapAdjust(L,1,i-1);
}
//调整位置后,重新构成大顶堆。
}
将树中的数值顺序完全暗中层次遍历的方式顺序,把数值存储到数组当中。
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
根据公式方程进行相对位置的对比排序
void HeapAdjust(SqList *L, int s, int m)
{
int temp,j;
temp=L->r[s];
for(j=2*s;j<=m;j*=2)
{
if(j<m&&L->r[j]<L->r[j+1])
++j;
if(temp>=L->r[j])
break;
L->r[s]=L->[j];
s=j;
}
L->s[s]=temp;
}
2.6 归并排序
堆排序就很复杂,so用简单一点的归并排序,分治的思想,把原来的数列不断地排序后分为好多段,然后再对数列进行重组。
void Merge(int SR[] ,int TR[], int i, int m, int n)
{
int j,k,l;
for(j=m+1,k=i;i<=m&&j<=n;k++)
{
if(SR[i]<SR[j])
TR[k]=SR[i++]
else
TR[k]=SR[j++]
}
if(i<m)
{
for(l=0;l<=m-i;i++)
TR[k+1]=SR[i+1];
}
if(j<=n)
{
for (l==0;l<=n-j;l++)
TR[k+l]=SR[j+1];
}
}
将SR[1] ~SR[n]中相邻的长度为h的有序序列进行两两归并。
2.7 快速排序
通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均必另一部分的关键字小,则可以分别对这两个部分的继续排序。
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);
}
}
void Partition(SqList *L, int low ,int high)
{
int pivotkey;
pivotkey=L->r[low];
while(low<high)
{
while(low<high&&L->r[right]>=pivotkey)
high--;
swap(L,low,high);
}
return low;
}
3. 结语
终于结束了!!!!!