算法之路——插入排序篇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;
}
Keep Looking