分享
 
 
 

单向链表操作详解(二)[The End]

王朝other·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

/*

接着讲(测试编译时,请把相应的函数及测试代码放到上一讲代码的相关地方):

排序(选择、插入、冒泡)

插入(有序)

*/

/*

===============================================

作者:rerli

时间:2003-12-08

目的:学习单向链表的创建、修改、删除、

插入(无序、有序)、输出、

排序(选择、插入、冒泡)、反序

说明:编译没有任何错误,能生成EXE文件。

这个程序TC2.0中编译生成的EXE文件,

在运行输入节点时出现以下错误(TC2.01中没有任何错误):

scanf : floating point formats not linked

Abnormal program termination

即:struct student中float score字段在输入时,

它不认float数格式,而改为long score却可以正常运行。

但是在TC2.01中float score重新编译、链接、运行很正常。

因此,我认为这是TC2.0在结构类型中的Bug.

================================================

*/

/*

==========================

功能:选择排序(由小到大)

返回:指向链表表头的指针

==========================

*/

/*

选择排序的基本思想就是反复从还未排好序的那些节点中,

选出键值(就是用它排序的字段,我们取学号num为键值)最小的节点,

依次重新组合成一个链表。

我认为写链表这类程序,关键是理解:

head存储的是第一个节点的地址,head->next存储的是第二个节点的地址;

任意一个节点p的地址,只能通过它前一个节点的next来求得。

单向链表的选择排序图示:

---->[1]---->[3]---->[2]...---->[n]---->[NULL](原链表)

head 1->next 3->next 2->next n->next

---->[NULL](空链表)

first

tail

---->[1]---->[2]---->[3]...---->[n]---->[NULL](排序后链表)

first 1->next 2->next 3->next tail->next

图10:有N个节点的链表选择排序

1、先在原链表中找最小的,找到一个后就把它放到另一个空的链表中;

2、空链表中安放第一个进来的节点,产生一个有序链表,并且让它在原链表中分离出来(此时要注意原链表中出来的是第一个节点还是中间其它节点);

3、继续在原链表中找下一个最小的,找到后把它放入有序链表的尾指针的next,然后它变成其尾指针;

*/

struct student *SelectSort(struct student *head)

{

struct student *first; /*排列后有序链的表头指针*/

struct student *tail; /*排列后有序链的表尾指针*/

struct student *p_min; /*保留键值更小的节点的前驱节点的指针*/

struct student *min; /*存储最小节点*/

struct student *p; /*当前比较的节点*/

first = NULL;

while (head != NULL) /*在链表中找键值最小的节点。*/

{

/*注意:这里for语句就是体现选择排序思想的地方*/

for (p=head,min=head; p->next!=NULL; p=p->next) /*循环遍历链表中的节点,找出此时最小的节点。*/

{

if (p->next->num < min->num) /*找到一个比当前min小的节点。*/

{

p_min = p; /*保存找到节点的前驱节点:显然p->next的前驱节点是p。*/

min = p->next; /*保存键值更小的节点。*/

}

}

/*上面for语句结束后,就要做两件事;一是把它放入有序链表中;二是根据相应的条件判断,安排它离开原来的链表。*/

/*第一件事*/

if (first == NULL) /*如果有序链表目前还是一个空链表*/

{

first = min; /*第一次找到键值最小的节点。*/

tail = min; /*注意:尾指针让它指向最后的一个节点。*/

}

else /*有序链表中已经有节点*/

{

tail->next = min; /*把刚找到的最小节点放到最后,即让尾指针的next指向它。*/

tail = min; /*尾指针也要指向它。*/

}

/*第二件事*/

if (min == head) /*如果找到的最小节点就是第一个节点*/

{

head = head->next; /*显然让head指向原head->next,即第二个节点,就OK*/

}

else /*如果不是第一个节点*/

{

p_min->next = min->next; /*前次最小节点的next指向当前min的next,这样就让min离开了原链表。*/

}

}

if (first != NULL) /*循环结束得到有序链表first*/

{

tail->next = NULL; /*单向链表的最后一个节点的next应该指向NULL*/

}

head = first;

return head;

}

/*

==========================

功能:直接插入排序(由小到大)

返回:指向链表表头的指针

==========================

*/

/*

直接插入排序的基本思想就是假设链表的前面n-1个节点是已经按键值

(就是用它排序的字段,我们取学号num为键值)排好序的,对于节点n在

这个序列中找插入位置,使得n插入后新序列仍然有序。按照这种思想,依次

对链表从头到尾执行一遍,就可以使无序链表变为有序链表。

单向链表的直接插入排序图示:

---->[1]---->[3]---->[2]...---->[n]---->[NULL](原链表)

head 1->next 3->next 2->next n->next

---->[1]---->[NULL](从原链表中取第1个节点作为只有一个节点的有序链表)

head

图11

---->[3]---->[2]...---->[n]---->[NULL](原链表剩下用于直接插入排序的节点)

first 3->next 2->next n->next

图12

---->[1]---->[2]---->[3]...---->[n]---->[NULL](排序后链表)

head 1->next 2->next 3->next n->next

图13:有N个节点的链表直接插入排序

1、先在原链表中以第一个节点为一个有序链表,其余节点为待定节点。

2、从图12链表中取节点,到图11链表中定位插入。

3、上面图示虽说画了两条链表,其实只有一条链表。在排序中,实质只增加了一个用于指向剩下需要排序节点的头指针first罢了。

这一点请读者务必搞清楚,要不然就可能认为它和上面的选择排序法一样了。

*/

struct student *InsertSort(struct student *head)

{

struct student *first; /*为原链表剩下用于直接插入排序的节点头指针*/

struct student *t; /*临时指针变量:插入节点*/

struct student *p; /*临时指针变量*/

struct student *q; /*临时指针变量*/

first = head->next; /*原链表剩下用于直接插入排序的节点链表:可根据图12来理解。*/

head->next = NULL; /*只含有一个节点的链表的有序链表:可根据图11来理解。*/

while (first != NULL) /*遍历剩下无序的链表*/

{

/*注意:这里for语句就是体现直接插入排序思想的地方*/

for (t=first, q=head; ((q!=NULL) && (q->num < t->num)); p=q, q=q->next); /*无序节点在有序链表中找插入的位置*/

/*退出for循环,就是找到了插入的位置*/

/*注意:按道理来说,这句话可以放到下面注释了的那个位置也应该对的,但是就是不能。原因:你若理解了上面的第3条,就知道了。*/

first = first->next; /*无序链表中的节点离开,以便它插入到有序链表中。*/

if (q == head) /*插在第一个节点之前*/

{

head = t;

}

else /*p是q的前驱*/

{

p->next = t;

}

t->next = q; /*完成插入动作*/

/*first = first->next;*/

}

return head;

}

/*

==========================

功能:冒泡排序(由小到大)

返回:指向链表表头的指针

==========================

*/

/*

直接插入排序的基本思想就是对当前还未排好序的范围内的全部节点,

自上而下对相邻的两个节点依次进行比较和调整,让键值(就是用它排

序的字段,我们取学号num为键值)较大的节点往下沉,键值较小的往

上冒。即:每当两相邻的节点比较后发现它们的排序与排序要求相反时,

就将它们互换。

单向链表的冒泡排序图示:

---->[1]---->[3]---->[2]...---->[n]---->[NULL](原链表)

head 1->next 3->next 2->next n->next

---->[1]---->[2]---->[3]...---->[n]---->[NULL](排序后链表)

head 1->next 2->next 3->next n->next

图14:有N个节点的链表冒泡排序

任意两个相邻节点p、q位置互换图示:

假设p1->next指向p,那么显然p1->next->next就指向q,

p1->next->next->next就指向q的后继节点,我们用p2保存

p1->next->next指针。即:p2=p1->next->next,则有:

[ ]---->[p]---------->[q]---->[ ](排序前)

p1->next p1->next->next p2->next

图15

[ ]---->[q]---------->[p]---->[ ](排序后)

图16

1、排序后q节点指向p节点,在调整指向之前,我们要保存原p的指向节点地址,即:p2=p1->next->next;

2、顺着这一步一步往下推,排序后图16中p1->next->next要指的是p2->next,所以p1->next->next=p2->next;

3、在图15中p2->next原是q发出来的指向,排序后图16中q的指向要变为指向p的,而原来p1->next是指向p的,所以p2->next=p1->next;

4、在图15中p1->next原是指向p的,排序后图16中p1->next要指向q,原来p1->next->next(即p2)是指向q的,所以p1->next=p2;

5、至此,我们完成了相邻两节点的顺序交换。

6、下面的程序描述改进了一点就是记录了每次最后一次节点下沉的位置,这样我们不必每次都从头到尾的扫描,只需要扫描到记录点为止。

因为后面的都已经是排好序的了。

*/

struct student *BubbleSort(struct student *head)

{

struct student *endpt; /*控制循环比较*/

struct student *p; /*临时指针变量*/

struct student *p1;

struct student *p2;

p1 = (struct student *)malloc(LEN);

p1->next = head; /*注意理解:我们增加一个节点,放在第一个节点的前面,主要是为了便于比较。因为第一个节点没有前驱,我们不能交换地址。*/

head = p1; /*让head指向p1节点,排序完成后,我们再把p1节点释放掉*/

for (endpt=NULL; endpt!=head; endpt=p) /*结合第6点理解*/

{

for (p=p1=head; p1->next->next!=endpt; p1=p1->next)

{

if (p1->next->num > p1->next->next->num) /*如果前面的节点键值比后面节点的键值大,则交换*/

{

p2 = p1->next->next; /*结合第1点理解*/

p1->next->next = p2->next; /*结合第2点理解*/

p2->next = p1->next; /*结合第3点理解*/

p1->next = p2; /*结合第4点理解*/

p = p1->next->next; /*结合第6点理解*/

}

}

}

p1 = head; /*把p1的信息去掉*/

head = head->next; /*让head指向排序后的第一个节点*/

free(p1); /*释放p1*/

p1 = NULL; /*p1置为NULL,保证不产生“野指针”,即地址不确定的指针变量*/

return head;

}

/*

==========================

功能:插入有序链表的某个节点的后面(从小到大)

返回:指向链表表头的指针

==========================

*/

/*

有序链表插入节点示意图:

---->[NULL](空有序链表)

head

图18:空有序链表(空有序链表好解决,直接让head指向它就是了。)

以下讨论不为空的有序链表。

---->[1]---->[2]---->[3]...---->[n]---->[NULL](有序链表)

head 1->next 2->next 3->next n->next

图18:有N个节点的有序链表

插入node节点的位置有两种情况:一是第一个节点前,二是其它节点前或后。

---->[node]---->[1]---->[2]---->[3]...---->[n]---->[NULL]

head node->next 1->next 2->next 3->next n->next

图19:node节点插在第一个节点前

---->[1]---->[2]---->[3]...---->[node]...---->[n]---->[NULL]

head 1->next 2->next 3->next node->next n->next

图20:node节点插在其它节点后

*/

struct student *SortInsert(struct student *head, struct student *node)

{

struct student *p; /*p保存当前需要检查的节点的地址*/

struct student *t; /*临时指针变量*/

if (head == NULL) /*处理空的有序链表*/

{

head = node;

node->next = NULL;

n += 1; /*插入完毕,节点总数加1*/

return head;

}

p = head; /*有序链表不为空*/

while (p->num < node->num && p != NULL) /*p指向的节点的学号比插入节点的学号小,并且它不等于NULL*/

{

t = p; /*保存当前节点的前驱,以便后面判断后处理*/

p = p->next; /*后移一个节点*/

}

if (p == head) /*刚好插入第一个节点之前*/

{

node->next = p;

head = node;

}

else /*插入其它节点之后*/

{

t->next = node; /*把node节点加进去*/

node->next = p;

}

n += 1; /*插入完毕,节点总数加1*/

return head;

}

/*

测试代码如下:

*/

/*测试SelectSort():请编译时去掉注释块*/

/*

head = SelectSort(head);

Print(head);

*/

/*测试InsertSort():请编译时去掉注释块*/

/*

head = InsertSort(head);

Print(head);

*/

/*测试BubbleSort():请编译时去掉注释块*/

/*

head = BubbleSort(head);

Print(head);

*/

/*测试SortInsert():上面创建链表,输入节点时请注意学号num从小到大的顺序。请编译时去掉注释块*/

/*

stu = (struct student *)malloc(LEN);

printf("\nPlease input insert node -- num,score: ");

scanf("%ld,%f",&stu->num,&stu->score);

head = SortInsert(head,stu);

free(stu);

stu = NULL;

Print(head);

*/

(全文完)

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有