• 算法之路——插入排序篇2


    个人见解:

      直接插入排序简单,明了,适合小数据规模。可是[i]插入时必须逐个的和[i]进行比较,然后再执行后移的操作。每次的比较浪费了一定的时间,所以有了折半插入排序的算法,该算法对以排序的部分使用折半查找法,可以快速的找到带插入元素的位置,然后进行相应的后以操作和插入。与直接插入排序相比,减少了比较的次数,当规模比较大时,会有一定的效果。

      可是无论直接插入排序还是折半插入排序,都避免不了每次插入时可能的大量移位操作。所以如果可以减少移位操作就可以提高排序的速度。表插入排序就是通过实现这点来提高排序速度的。表插入排序首先通过在每个元素里增减一个next来记录下一个元素的位置,形成了循环链表。可是这样的链表只能实现顺序查找,不能实现随机查找,所以需要进一步完善。而随机查找的特点就是第i大的元素在数组的第i个位置。所以如果能将第i大的元素放到数组的第i个位置上,这就可以实现随机查找了。于是算法的下一步就是讲第i大的元素放在数组的第i个位置。具体的实现是:先用变量i来指示数组的下标,用j来指示第i大的元素在数组中实际的位置,然后[i]和[j]交换位置。这样第i大的元素就就位了,可是原本的[i]跑到第j个位置了,所以通过将[i]的next设置为j来记录原来的数据现在的位置。并且原本在i位置的元素可以通过*****处的循环定位到。这样就没有中断循环链表,移位操作就可以完成了。表插入排序的缺点就是另外需要n个空间存放next。如果通过malloc()函数,和free()函数来临时的生成n个空间,算法性能会更好一些。而且也不用定义特定的struct。可以和之前的算法更兼容。

    #include <stdio.h>
    #include <stdlib.h>
    #include <limits.h>

    #define MAXSIZE 100

    typedef int KeyType;

    typedef struct {
    KeyType data;
    //其他的属性
    }RedType,* PRedType;

    #define OK 0
    #define P_NULL 1
    #define TOOBIG 2
    #define NUM_ERROR 3

    //表插入排序
    #define SL_T "%d"
    typedef struct
    {
    RedType rc;
    int next;
    }SLNode,* PSLNode;

    typedef struct
    {
    SLNode list[MAXSIZE+1]; //第一个元素用来只是最小的元素的位置,并且可以做哨兵
    int length;
    }SLinkListType, * PSLinkListType;
    int comp(void * d1, void * d2);
    int inputSLinkList (PSLinkListType p,int length);
    int outputSLinkList (PSLinkListType p);
    int SLinkSort (PSLinkListType p, int (*comp)(void *,void *));



    int main (int argc, char * argv)
    {
    int status;
    PSLinkListType test;
    test = (PSLinkListType)malloc(sizeof(SLinkListType));
    int n = 0 ;
    printf("请输入第一组待排序的数的个数(输入0结束循环):");

    while (1)
    {
    while (scanf("%d",&n) != 1)
    {
    puts("输入有误!请重新输入!");
    while(getchar() != '\n');
    }

    if (n == 0) //结束
    break;
    if (status = inputSLinkList(test, n) != 0)
    {
    puts("输入的数字有误!");
    while(getchar() != '\n');
    //exit(status);
    }

    SLinkSort(test,comp);
    outputSLinkList(test);
    printf("请输入下一组待排序的数的个数(输入0结束循环):");
    }
    free(test);
    return 0;
    }


    //表插入排序
    int comp(void * d1, void * d2)
    {//比较函数
    return (((PRedType)d1)->data - ((PRedType)d2)->data );
    }

    int inputSLinkList (PSLinkListType p,int length)
    {//输入序列

    if (p == NULL)
    return P_NULL; //1,指针是空的
    if (length > MAXSIZE)
    return TOOBIG; //2,要输入的序列太多

    int i = 0;
    p->list[0].rc.data = INT_MAX; //是指哨兵
    for (i = 1; i < length + 1; i++)
    if (scanf(SL_T,&(p->list[i].rc.data)) != 1)
    return NUM_ERROR; //3,输入的数字有误

    p->length = length;
    return OK; //0
    }

    int outputSLinkList (PSLinkListType p)
    {//输出序列
    if (p == NULL)
    return P_NULL;

    int i = 0;

    for (i = 1; i < p->length + 1; i++)
    printf (SL_T"\t",p->list[i].rc.data);
    putchar('\n');

    return OK;
    }

    /*
    for (i = 0; i < p->length + 1; i++)
    printf (SL_T"\t",p->list[i].rc.data%);
    putchar('\n');
    for (i = 0; i < p->length + 1; i++)
    printf (SL_T"\t",p->list[i].next);
    putchar('\n');
    */
    int SLinkSort (PSLinkListType p, int (*comp)(void *,void *))
    {//表插入排序
    if (p == NULL)
    return P_NULL;

    //初始化
    p->list[0].next = 1;
    p->list[1].next = 0;

    int i = 0;
    int j = 0;

    //将表通过next连成循环链表
    for (i = 2; i < p->length + 1; i++)
    {
    j = 0;
    while (comp (&(p->list[p->list[j].next].rc),&(p->list[i].rc)) < 0)
    j = p->list[j].next;

    //从while()出来的[j]是小于[i]那个元素,并且[j].next指示的元素是大于等于i的元素,[i]应该放在这个链之间。
    p->list[i].next = p->list[j].next;
    p->list[j].next = i;
    }

    //把第i大的元素放到第i个位置
    SLNode t;

    for (i = 1; i < p->length; i++)
    {
    j = p->list[0].next;

    //*****
    while (j < i) //如果j小于i,说明这儿原本的元素被调换位置了,要从p->list[j].next继续查找
    j = p->list[j].next;

    //下面的这条语句使p->list[0].next始终存着下一次要调换的元素的下标,j每次从这儿取值
    //(这条语句不能在while之前,我开始的时候就放在了while之前,然后又查了半天)
    p->list[0].next = p->list[j].next;

    if (j != i)
    {
    t = p->list[i];
    p->list[i] = p->list[j];
    p->list[j] = t;
    p->list[i].next = j; //存储本来[i],被放在了[i]处
    }
    }
    return OK;
    }
    /*
    直接插入排序简单,明了,适合小数据规模。可是[i]插入时必须逐个的和[i]进行比较,然后再执行后移的操作。每次的比较浪费了一定的时间,所以有了
    折半插入排序的算法,该算法对以排序的部分使用折半查找法,可以快速的找到带插入元素的位置,然后进行相应的后以操作和插入。与直接
    插入排序相比,减少了比较的次数,当规模比较大时,会有一定的效果。

    可是无论直接插入排序还是折半插入排序,都避免不了每次插入时可能的大量移位操作。所以如果可以减少移位操作就可以提高排序的速度。
    表插入排序就是通过实现这点来提高排序速度的。表插入排序首先通过在每个元素里增减一个next来记录下一个元素的位置,形成了循环链表。
    可是这样的链表只能实现顺序查找,不能实现随机查找,所以需要进一步完善。而随机查找的特点就是第i大的元素在数组的第i个位置。所以如果能
    将第i大的元素放到数组的第i个位置上,这就可以实现随机查找了。于是算法的下一步就是讲第i大的元素放在数组的第i个位置。具体的实现是:
    先用变量i来指示数组的下标,用j来指示第i大的元素在数组中实际的位置,然后[i]和[j]交换位置。这样第i大的元素就就位了,可是原本的[i]
    跑到第j个位置了,所以通过将[i]的next设置为j来记录原来的数据现在的位置。并且原本在i位置的元素可以通过*****处的循环定位到。这样就没有
    中断循环链表,移位操作就可以完成了。表插入排序的缺点就是另外需要n个空间存放next。如果通过malloc()函数,和free()函数来临时的生成n个
    空间,算法性能会更好一些。而且也不用定义特定的struct。可以和之前的算法更兼容。
    */



    //这是通过设立next数组实现表排序
    int BSortByNext (PSqList p, int (*comp)(void *,void *))
    {//next是暂时的数组的表插入排序
    if (p == NULL)
    return P_NULL;
    p->list[0].data = INT_MAX;
    int next[MAXSIZE + 1];
    //初始化
    next[0] = 1;
    next[1] = 0;

    int i = 0;
    int j = 0;

    //将表通过next连成循环链表
    for (i = 2; i < p->length + 1; i++)
    {
    j = 0;
    while (comp (&(p->list[next[j]]),&(p->list[i])) < 0)
    j = next[j];

    //从while()出来的[j]是小于[i]那个元素,并且[j].next指示的元素是大于等于i的元素,[i]应该放在这个链之间。
    next[i] = next[j];
    next[j] = i;
    }
    //把第i大的元素放到第i个位置
    RedType t;

    for (i = 1; i < p->length; i++)
    {
    j = next[0];

    while (j < i)
    j = next[j];

    next[0] = next[j];

    if (j != i)
    {
    t = p->list[i];
    p->list[i] = p->list[j];
    p->list[j] = t;
    next[i] = j; //存储本来[i],被放在了[i]处
    }
    }
    return OK;
    }
  • 相关阅读:
    MyBatis进阶(一)
    git命令整理
    今天的任务--git练习
    深入浅出JavaScript(一)
    数据结构_树_二叉搜索树
    网络_体系结构
    数据结构_树
    算法_五大经典搜索算法
    SpringMVC入门
    spring 线程异步执行
  • 原文地址:https://www.cnblogs.com/svking/p/b_sort.html
Copyright © 2020-2023  润新知