内部排序算法
生活中人们常用的排序方法经过抽象整理,形成计算机中如下几个方面的排序算法
- 每当有新数据到来时插入到当前一个较小的有序表中,这个是插入排序
- 给一个无序的表,然后交换次序使之有序,这个是快速排序
- 每次选择当前n-i+1个记录中最小的一个,形成一个新的局部不断扩充的有序表,使之有序,这个是选择排序
- 合并几个有序的记录为一个,使之为整体有序,这个叫做归并排序
- 排序的时候需要考虑多个关键字(姓名,年龄,职业),按照多个关键字依次排序并形成最后的结果,这个叫做基数排序
自定义的数据结构
- 如下所示
#define MAXSIZE 20
#define SIZE 100
typedef int KeyType;
typedef int KeysType;
typedef int InfoType;
typedef struct{
KeyType key;
InfoType otherinfo;
}RedType;
typedef struct{
RedType r[MAXSIZE+1];
int length;
}SqList;
#define HeapType SqList
typedef struct
{
RedType rc;
int next;
}SLNode;
typedef struct
{
SLNode r[SIZE];
int length;
}SLinkListType;
//radix definition
#define MAX_NUM_OF_KEY 2
#define RADIX 10
#define MAX_SPACE 100
typedef struct
{
KeysType keys[MAX_NUM_OF_KEY];
InfoType otheritems;
int next;
}SLCell;
typedef struct
{
SLCell r[MAX_SPACE];
int keynum;
int recnum;
}SLList;
typedef int ArrType[RADIX];
插入排序
- 直接插入排序:最简单,始终保持有序的子表,然后加1通过比较移动保持有序,是稳定的,平方级的复杂度:
### 直接插入排序源码
//o(n*n)插入排序
void insertSort(SqList* L)
{
int i = 2,j;
for(i = 2;i<=L->length;++i)
{
if(LT(L->r[i].key,L->r[i-1].key))
{
L->r[0] = L->r[i];
L->r[i] = L->r[i-1];
for(j = i-2;LT(L->r[0].key,L->r[j].key);--j)
{
L->r[j+1]=L->r[j];
}
L->r[j+1] = L->r[0];
}
}
}
- 折半插入排序:采用折半查找的策略减少了比较次数,但是移动次数依旧一样,是稳定的,平方级的复杂度:
//binary search sort o(n*n)
void binaryInsertSort(SqList *L)
{
int i,j;
for(i = 2;i<=L->length;i++)
{
int low =1;
int high = i-1;
L->r[0] = L->r[i];
while(low<=high)
{
int m = (low+high)/2;
if(LT(L->r[0].key,L->r[m].key))
high = m-1;
else
low=m+1;
}
for(j=i-1;j>=high+1;--j)
{
L->r[j+1] =L->r[j];
}
L->r[high+1] =L->r[0];
}
}
- 2路插入排序:用两个表代替一个表,中位数大的放在一个表,比他小的放在另外一个表,减少了移动次数,是稳定的,也是平方级的复杂度
void twoWayInsertSort(SqList *L,SLinkListType *S)
{
int i =0;
for(i = 2;i<=L->length;i++)
{
S->r[i].rc = L->r[i];
S->r[i].next = 99;
//S->r[S->r[0].next)].rc.key
//printf("11:%d ",S->r[i].rc.key);
if(LT(S->r[i].rc.key,S->r[S->r[0].next].rc.key))
{
//printf("22:%d ",S->r[i].rc.key);
int lessMin = S->r[0].next;
S->r[0].next = i;
S->r[i].next = lessMin;
}
else
{
if(LT(S->r[i].rc.key,S->r[1].rc.key))
{
//printf("33:%d ",S->r[i].rc.key);
insertStaticList(S,i,S->r[0].next);
}
else{
//printf("44:%d ",S->r[i].rc.key);
insertStaticList(S,i,1);
}
}
//printf("%d ",S->r[i].rc.key);
}
}
- 表插入排序:通过修改指针的方式来代替数据记录的交换,稳定,平方级复杂度:
void arrangeSList(SLinkListType *S)
{
int p = S->r[0].next,q;
int i = 1;
for(i=1;i<S->length;++i)
{
while(p<i)
p=S->r[p].next;
q=S->r[p].next;
if(p!=i)
{
SLNode temp = S->r[p];
S->r[p] = S->r[i];
S->r[i] = temp;
S->r[i].next = p;
}
p = q;
}
}
- 希尔排序:按照一定的增量序列(例如5,3,1的间隔,1是最大公因数,增量序列是2的n次方-1)划分各个子序列使其基本有序,然后每次进行直接插入排序,不稳定,1.5次方的复杂度,最多可以降到n*(log2n)2:
void shellInsert(SqList *S,int dk)
{
int i,j;
for(i=dk+1;i<=S->length;++i)
{
if(LT(S->r[i].key,S->r[i-dk].key))
{
S->r[0] = S->r[i];
for(j=i-dk;j>=0&& LT(S->r[0].key,S->r[j].key);j-=dk)
{
S->r[j+dk] = S->r[j];
}
S->r[j+dk] = S->r[0];
}
}
}
void shellSort(SqList *S,int dlta[],int t)
{
int k=0;
for(k=0;k<t;k++)
shellInsert(S,dlta[k]);
}
快速排序
- 起泡排序: 起泡排序:将每个记录依次与其他所有记录相比较,使其交换顺序,不稳定,平方级复杂度。
- 快速排序:用low和high两个指针依次分别和枢轴比较,先high往后走,比轴小交换位置,然后是low比轴大交换位置,一直到中间,不稳定的,性能比较好,是最好的内部排序(同数量级的平均性能最好,有序时候会退化为气泡排序),降低到n*logn的复杂度:
int partition(SqList *L,int low,int high)
{
L->r[0] = L->r[low];
int pivotKey = L->r[low].key;
while(low < high)
{
while(low < high && L->r[high].key >= pivotKey)
--high;
L->r[low] = L->r[high];
while(low < high && L->r[low].key <=pivotKey)
++low;
L->r[high] = L->r[low];
}
L->r[low] = L->r[0];
return low;
}
void quickSort(SqList *L,int low,int high)
{
if(low < high)
{
int pivotloc =partition(L,low,high);
quickSort(L,low,pivotloc-1);
quickSort(L,low+1,high);
}
}
选择排序
- 简单选择排序:每次取出来当前最小的,放在前面,然后依次选择所有的元素,最后元素排序完毕,不稳定的,平方级复杂度:
int seleMinKey(SqList *L,int start)
{
int i;
int min=200;
int j;
for(i=start;i<=L->length;i++)
if(L->r[i].key <min){
min =L->r[i].key;
j=i;
}
return j;
}
void selectSort(SqList *L)
{
int i = 1;
for(i =1;i<L->length;i++)
{
int j = seleMinKey(L,i);
printf("%d\n",j);
if(i !=j)
{
RedType temp = L->r[i];
L->r[i] = L->r[j];
L->r[j] = temp;
}
}
}
- 堆排序(针对的是完全二叉树):非终端(叶子)节点的值不大于其左右孩子节点的值,分为调整和构建两周策略,每次输出根节点:
1.每次调整从n/2到0,判断孩子节点是否需要调整交换,一直到根节点结束;
2.堆顶记录,依次与n到2的节点交换位置,然后调整,形成最终的大顶堆(小顶堆)。
性能最差不会超过n*logn.
void heapAdjust(HeapType *H,int s,int m)
{
int j;
RedType rc=H->r[s];
for(j=2*s;j<=m;j*=2)
{
if(j<m && LT(H->r[j].key,H->r[j+1].key))
++j;
if(!LT(rc.key,H->r[j].key))
break;
H->r[s] = H->r[j];
s=j;
}
H->r[s] =rc;
}
void heapSort(HeapType *H)
{
int i;
for(i=H->length/2;i>=0;--i)
{
heapAdjust(H,i,H->length);
}
for(i=H->length;i>1;--i)
{
RedType temp = H->r[1] ;
H->r[1] = H->r[i];
H->r[i] = temp;
heapAdjust(H,1,i-1);
}
}
归并排序
- 归并排序:时间也是比较快(n*logn),且是稳定的,可能主要用于外部排序,思路是:采用归并两个有序表的策略(例如两路),通常采用递归形式实现(不断取中间位置,然后在栈中弹出,不断归并)
void merge(RedType SR[],RedType *TR,int i,int m,int n)
{
int j,k;
for(j=m+1,k=i;i<=m&&j<=n;++k)
{
if(LT(SR[i].key,SR[j].key))
TR[k]=SR[i++];
else
TR[k]=SR[j++];
}
while(i<=m)
TR[k++] = SR[i++];
while(j<=n)
TR[k++] = SR[j++];
}
void mSort(RedType SR[],RedType *TR1,int s,int t)
{
int m;
if(s==t)
TR1[s]=SR[s];
else{
SqList *TR2 = (SqList *)malloc(sizeof(SqList));
m=(s+t)/2;
mSort(SR,(TR2->r),s,m);
mSort(SR,(TR2->r),m+1,t);
merge(TR2->r,TR1,s,m,t);
}
}
基数排序
基数排序:(d*(n+rd),在n远大于d的时候可以采用,可以稳定也可以不稳定,MSD,LSD的区别)
链式基数排序:从低到高位,每次拉成桶内的局部队列,然后按照桶的关键字大小依次排成一个整体的链表,然后重复,直到排序完所有的基数。借助于比较的排序算法的最好时间复杂度:nlogn.
void initSLList(SLList *L)
{
int i;
L->keynum = MAX_NUM_OF_KEY;
L->recnum = MAXSIZE+1;
srand((unsigned) time(NULL));
L->r[0].keys[0] = 9;
L->r[0].keys[1] = 9;
L->r[0].next = 1;
for(i=1;i<MAXSIZE+1;i++)
{
L->r[i].keys[0] = rand()% 10;
L->r[i].keys[1] = rand()% 10;
L->r[i].next = i+1;
}
L->r[L->recnum-1].next = 0;
}
void traverseSLList(SLList *L)
{
int p,i=0;
for(p = L->r[0].next;p;p=L->r[p].next)
{
i++;
printf("|| %d %d ",p,L->r[p].keys[0]+L->r[p].keys[1]*10);
}
printf("\n");
/* for(p=0;p<MAXSIZE+1;p++)
{
printf("||%d %d %d ",p,L->r[p].next,L->r[p].keys[0]+L->r[p].keys[1]*10);
}
printf("\n");*/
}
int succ(int j)
{
return j+1;
}
int ord(int i)
{
return i;
}
void distribute(SLCell *r,int i,int *f,int *e)
{
int j,p;
for(j=0;j<RADIX;++j)
{
f[j] = 0;
e[j] = 0;
}
for(p=r[0].next;p;p=r[p].next)
{
j=ord(r[p].keys[i]);
if(!f[j])
f[j] = p;
else
r[e[j]].next = p;
e[j] = p;
}
}
void collect(SLCell *r,int i,int *f,int *e)
{
int j,t;
for(j=0;!f[j];j=succ(j));
r[0].next=f[j];
t=e[j];
while(j<RADIX)
{
for(j=succ(j);j<(RADIX-1) &&!f[j] ;j=succ(j));
if(f[j])
{
r[t].next = f[j];
t=e[j];
}
}
r[t].next = 0;
}
void traverseFE(SLList *L,int f[],int e[])
{
int j;
for(j=0;j<RADIX;j++)
{
int temp = f[j];
printf("%d : %dF->",j,f[j]);
printf("%d ",L->r[temp].keys[0]+L->r[temp].keys[1]*10);
while(temp !=e[j])
{
temp = L->r[temp].next;
printf("%d ",L->r[temp].keys[0]+L->r[temp].keys[1]*10);
}
printf("<-%dE",e[j]);
printf("\n");
}
}
void radixSort(SLList *L)
{
int i=0,j=0;
int f[RADIX];
int e[RADIX];
/*for(i=0;i<L->recnum;++i)
L->r[i].next = i+1;
L->r[L->recnum-1].next = 0;*/
for(i=0;i<L->keynum;++i)
{
distribute(L->r,i,f,e);
traverseFE(L,f,e);
collect(L->r,i,f,e);
}
traverseSLList(L);
}