排序
冒泡排序
稳定!
最好情况:顺序 T = O(N)
最坏情况:逆序 T = O(N^2)
```c
void Swap(ElementType *a, ElementType *b){
ElementType c;
c = *a;
*a = *b;
*b = c;
}
void Bubble_Sort(ElementType A\[\], int N){
int P, i, flag;
for (P=N-1; P>0; P--){ // 最后一位下标是N-1
flag = 0;
for (i=0; i<P; i++){
if(Ai > Ai+1){
Swap(&Ai, &Ai+1);
flag = 1; // 标识发生了交换
}
}
if (flag==0)
break; // 若全程无交换
}
}
```
插入排序
稳定!
最好情况:顺序 T = O(N)
最坏情况:逆序 T = O(N^2)
时间复杂度和逆序对有关 T(N,I) = O(N+I)
定理: 任意N个不同元素组成的序列平均具有 N ( N -1 ) / 4 个逆序对。
简单排序(交换相邻两个数)的时间复杂度下界: Omega(N^2)
```c
void Insertion_Sort(ElementType A\[\], int N){
ElementType Tmp;
int P, i;
for(P=1; P<N; P++){
Tmp = AP; // 摸下一张牌
for (i = P; i>0; i--)
{
if(Ai-1 > Tmp) // 若前一张牌比新摸的牌大
Ai = Ai-1; // 移出空位
else break; // 若新摸的牌比前一张牌大,则跳出循环
}
Ai = Tmp; // 新牌落位
}
}
```
希尔排序
不稳定!
定义增量序列,如Sedgewick增量序列{1,5,19,41,109...}
对每个D进行D间隔的排序
```c
void Shell_Sort(ElementType A\[\], int N){
int D, P,i;
ElementType Tmp;
for (D=N/2; D>0; D/=2){ // 希尔排序增量
for ( P=D; P<N; P++){ // 插入排序
Tmp = AP;
for (i = P; i-D>=0; i-=D)
{
if(Ai-D > Tmp)
Ai = Ai-D; // 移出空位
else break;
}
Ai = Tmp; // 新牌落位
}
}
}
```
堆排序
不稳定!
选择排序:
```c
void Selection_Sort ( ElementType A\[\], int N )
{
for(i=0;i<N;i++){
MinPosition = ScanForMin( A, i, N--1 );
Swap( Ai, AMinPosition );
}
}
```
重要的是如何找到最小元.
堆排序处理N个不同元素的 随机排列的平均比较次数是2NlogN - O(NloglogN)
用最大堆,交换根节点和最后一个结点,让最大的数到最后,然后减小堆的规模
```c
void PercDown(ElementType A\[\] , int p, int N)
{// 下滤函数, 将Minheap中以H->Datap为根的子堆调整为最小堆
int parent, child;
ElementType X;
X = Ap; // 取出根节点的值
for(parent = p; parent*2+1 <= N-1 ; parent = child)
{
child = parent*2+1;
if( (child != N-1 ) && (Achild < Achild+1))
{
child++;
}
if (X >= Achild)
break;
else // 将X下滤
Aparent = Achild;
}
Aparent = X;
}
void Heap_Sort(ElementType A\[\], int N){
int i;
for (i=N/2; i>=0; i--) // Build MaxHeap
PercDown(A, i, N);
for (i=N-1; i>0; i--){
Swap(&A0, &Ai); // DeleteMax
PercDown(A, 0, i); // 重新整理成最大堆,堆的size为i
}
}
```
归并排序
稳定!
有序子列的归并
```c
void Merge(ElementType A\[\], ElementType TmpA\[\],
int L, int R, int RightEnd)
{
int LeftEnd, Num, Tmp;
int i;
LeftEnd = R - 1;
Tmp = L; // 存放结果的数组的初始位置
Num = RightEnd - L + 1; // 一共有几个数据
while (L <= LeftEnd && R <= RightEnd){ // 左和右都不为空
if (AL <= AR)
TmpATmp++ = AL++;
else
TmpATmp++ = AR++;
}
// 跳出循环时左边还有剩下的
while (L <= LeftEnd)
TmpATmp++ = AL++;
// 跳出循环时右边还有剩下的
while (R <= RightEnd)
TmpATmp++ = AR++;
// 把TmpA倒回到A, 从RightEnd倒着复制回去
for(i=0; i<Num; i++){ // 重复Num次
ARightEnd = TmpARightEnd;
RightEnd--;
}
}
```
递归算法:
```c
void MSort(ElementType A\[\], ElementType TmpA\[\], int L, int RightEnd)
{
int Center;
if(L < RightEnd){ // L和RightEnd相等时,就只有一个元素了,不能再分
Center = (L+RightEnd)/2;
// 分
MSort(A, TmpA, L, Center);
MSort(A, TmpA, Center+1, RightEnd);
// 合
Merge(A, TmpA, L, Center+1, RightEnd);
}
}
// 同一接口
void Merge_Sort(ElementType A\[\], int N){
ElementType *TmpA; // 临时数组
TmpA = malloc(N * sizeof(ElementType));
if (TmpA != NULL){ // 分配空间成功
MSort(A, TmpA, 0, N-1);
free(TmpA);
}
else
printf("空间不足\n");
}
```
T(N) = T(N/2) + T(N/2) + O(N)
T(N) = O(NlogN)
非递归算法
```c
void Merge_Pass(ElementType A\[\], ElementType TmpA\[\], int N, int length) // lenght = 当前有序子列的长度, 一开始等于1
{
int i,j;
for (i=0; i <= N-2*length; i+=2*length) // 留两个length,分情况讨论
Merge(A, TmpA, i, i+length, i+2*length-1); // A TmpA L R RightEnd
if (i+length < N) // 归并最后两个子列
Merge(A, TmpA, i, i+length, N-1);
else // 最后只剩一个子列, 则复制过来
for (j=i; j<N; j++)
TmpAj = Aj;
}
void Merge_sort(ElementType A\[\], int N){
int length = 1; // 初始化子序列的长度
ElementType *TmpA; // 临时数组
TmpA = malloc(N * sizeof(ElementType));
if (TmpA != NULL){ // 分配空间成功
while (length < N){ // 有序子列长度等于N,则完成,跳出循环
Merge_Pass(A, TmpA, N, length);
length *= 2;
Merge_Pass(TmpA, A, N, length);
length *= 2;
}
free(TmpA);
}
else
printf("空间不足\n");
}
```
归并排序是稳定的
快速排序
不稳定
主元一次性放到正确的位置上
主元左边的都比主元小,右边的都比主元大
```c
// 选主元 pivot
ElementType Median3(ElementType A\[\], int Left, int Right){
int Center = (Left + Right) / 2;
// 整理成 ALeft <= ACenter <= ARight
if (ALeft > ACenter)
Swap(&ALeft, &ARight);
if (ALeft > ARight)
Swap(&ALeft, &ARight);
if (ACenter > ARight)
Swap(&ACenter, &ARight);
// 将基准Pivot藏到右边Right-1处, 则之后只需考虑 ALeft+1 -> ARight-2
Swap(&ACenter, &ARight-1);
return ARight-1;
}
void Qsort(ElementType A\[\], int Left, int Right){
int Pivot, Cutoff, Low, High;
// 如果序列元素足够多,则进入快排
Cutoff = 4;
if (Cutoff <= Right - Left){
Pivot = Median3(A, Left, Right);
Low = Left;
High = Right-1;
while(1){
while(A++Low < Pivot); // 一直循环,直到ALow >= Pivot, 停住
while(A--High > Pivot); // 一直循环,直到AHight <= Pivot, 停住
if (Low<High)
Swap(&ALow, &AHigh);
else // Low > High low已经越过了high,子序列排序结束
break;
}
Swap(&ALow, &ARight-1); // 将Pivot归位,此时 Low>High, 所以Pivot和Low交换
Qsort(A, Left, Low-1); // 递归Pivot左边
Qsort(A, Low+1, Right); // 递归Pivot右边
}
else // 元素太少,用简单排序
Insertion_Sort(A+Left, Right-Left+1); // 待排序列表A从Left开始,即A+Left, 长度是R-L+1
}
void Quick_Sort(ElementType A\[\], int N){
Qsort(A, 0 ,N-1);
}
```
选择排序
不稳定
```c
#define MaxDigit 3 // 假设最大为三位数
#define Radix 10 // 十进制
/* 桶元素结点 */
typedef struct Node *PtrNode;
struct Node
{
int key;
PtrNode next;
};
/* 桶头结点 */
struct HeadNode
{
PtrNode head;
PtrNode tail;
};
typedef struct HeadNode BucketRadix;
/* 返回整型关键字X的第D位数字 */
int GetDigit(int X, int D){
// 默认次位D=1, 主位 D<= MaxDigit
int d, i;
for(i=1; i<=D; i++){
d = X % Radix;
X /= Radix;
}
return d;
}
// 次位优先(Least Significant Digit)排序
void LSDRadix_Sort(ElementType A\[\], int N){
int D, Di, i;
Bucket B;
PtrNode tmp, p, List=NULL;
// 初始化每个桶为空链表
for(i=0; i<Radix; i++)
Bi.head = Bi.tail = NULL;
// 将原始序列A\[\], 逆序(插在链表头)存入链表List
for(i=0; i<N; i++){
tmp = (PtrNode)malloc(sizeof(struct Node));
tmp->key = Ai;
tmp->next = List;
List = tmp;
}
// 开始排序
for (D=1; D<= MaxDigit; D++){ // 对数据的每一位循环处理
p=List;
while(p)
{
Di = GetDigit(p->key, D); // 获得当前元素的第D位数字
// 从链表P中删除结点
tmp = p;
p = p->next;
tmp->next = NULL;
// 把结点插入到对应的BDi桶里
if (BDi.head == NULL)
BDi.head = BDi.tail = tmp;
else{
BDi.tail->next = tmp; // 把tmp加在尾部
BDi.tail = tmp; // 尾部指向最后一个结点,即tmp
}
} // while循环结束, 以第D位的分配完毕
// D位数排完以后开始收集
List = NULL;
for(Di=Radix-1; Di >= 0; Di--) // 将诶个桶的元素顺序收入List, 因为每次插入到List的头部,所以要从9开始收
{
if (BDi.head){
BDi.tail->next = List;
List = BDi.head;
BDi.head = BDi.tail = NULL; // 清空桶
}
}
}// 大for循环结束
// 将List\[\]导入A\[\], 并释放空间
for(i=0; i<N; i++){
tmp = List;
List = List->next;
Ai = tmp->key;
free(tmp);
}
}
```
时间复杂度 T = O(P(N+B))
当B较小时,基本是线性的.