排序
冒泡排序
稳定!
最好情况:顺序 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(A[i] > A[i+1]){
Swap(&A[i], &A[i+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 = A[P]; // 摸下一张牌
for (i = P; i>0; i--)
{
if(A[i-1] > Tmp) // 若前一张牌比新摸的牌大
A[i] = A[i-1]; // 移出空位
else break; // 若新摸的牌比前一张牌大,则跳出循环
}
A[i] = 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 = A[P];
for (i = P; i-D>=0; i-=D)
{
if(A[i-D] > Tmp)
A[i] = A[i-D]; // 移出空位
else break;
}
A[i] = Tmp; // 新牌落位
}
}
}
```
堆排序
不稳定!
选择排序:
```c
void Selection_Sort ( ElementType A[], int N )
{
for(i=0;i<N;i++){
MinPosition = ScanForMin( A, i, N--1 );
Swap( A[i], A[MinPosition] );
}
}
```
重要的是如何找到最小元.
堆排序处理N个不同元素的 随机排列的平均比较次数是2NlogN - O(NloglogN)
用最大堆,交换根节点和最后一个结点,让最大的数到最后,然后减小堆的规模
```c
void PercDown(ElementType A[] , int p, int N)
{// 下滤函数, 将Minheap中以H->Data[p]为根的子堆调整为最小堆
int parent, child;
ElementType X;
X = A[p]; // 取出根节点的值
for(parent = p; parent*2+1 <= N-1 ; parent = child)
{
child = parent*2+1;
if( (child != N-1 ) && (A[child] < A[child+1]))
{
child++;
}
if (X >= A[child])
break;
else // 将X下滤
A[parent] = A[child];
}
A[parent] = 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(&A[0], &A[i]); // 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 (A[L] <= A[R])
TmpA[Tmp++] = A[L++];
else
TmpA[Tmp++] = A[R++];
}
// 跳出循环时左边还有剩下的
while (L <= LeftEnd)
TmpA[Tmp++] = A[L++];
// 跳出循环时右边还有剩下的
while (R <= RightEnd)
TmpA[Tmp++] = A[R++];
// 把TmpA倒回到A, 从RightEnd倒着复制回去
for(i=0; i<Num; i++){ // 重复Num次
A[RightEnd] = TmpA[RightEnd];
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++)
TmpA[j] = A[j];
}
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;
// 整理成 A[Left] <= A[Center] <= A[Right]
if (A[Left] > A[Center])
Swap(&A[Left], &A[Right]);
if (A[Left] > A[Right])
Swap(&A[Left], &A[Right]);
if (A[Center] > A[Right])
Swap(&A[Center], &A[Right]);
// 将基准Pivot藏到右边Right-1处, 则之后只需考虑 A[Left+1] -> A[Right-2]
Swap(&A[Center], &A[Right-1]);
return A[Right-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); // 一直循环,直到A[Low] >= Pivot, 停住
while(A[--High] > Pivot); // 一直循环,直到A[Hight] <= Pivot, 停住
if (Low<High)
Swap(&A[Low], &A[High]);
else // Low > High low已经越过了high,子序列排序结束
break;
}
Swap(&A[Low], &A[Right-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 Bucket[Radix];
/* 返回整型关键字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++)
B[i].head = B[i].tail = NULL;
// 将原始序列A[], 逆序(插在链表头)存入链表List
for(i=0; i<N; i++){
tmp = (PtrNode)malloc(sizeof(struct Node));
tmp->key = A[i];
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;
// 把结点插入到对应的B[Di]桶里
if (B[Di].head == NULL)
B[Di].head = B[Di].tail = tmp;
else{
B[Di].tail->next = tmp; // 把tmp加在尾部
B[Di].tail = tmp; // 尾部指向最后一个结点,即tmp
}
} // while循环结束, 以第D位的分配完毕
// D位数排完以后开始收集
List = NULL;
for(Di=Radix-1; Di >= 0; Di--) // 将诶个桶的元素顺序收入List, 因为每次插入到List的头部,所以要从9开始收
{
if (B[Di].head){
B[Di].tail->next = List;
List = B[Di].head;
B[Di].head = B[Di].tail = NULL; // 清空桶
}
}
}// 大for循环结束
// 将List[]导入A[], 并释放空间
for(i=0; i<N; i++){
tmp = List;
List = List->next;
A[i] = tmp->key;
free(tmp);
}
}
```
时间复杂度 T = O(P(N+B))
当B较小时,基本是线性的.