内部排序算法总结

内部排序算法

生活中人们常用的排序方法经过抽象整理,形成计算机中如下几个方面的排序算法

  • 每当有新数据到来时插入到当前一个较小的有序表中,这个是插入排序
  • 给一个无序的表,然后交换次序使之有序,这个是快速排序
  • 每次选择当前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);
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值