首页 > 代码库 > 有序链表的插入操作
有序链表的插入操作
C语言教材的有序单链表程序的插入我并不满意,因为链表为空,表尾等原因导致有四种情况要处理,给同学们的阅读造成困难。书上的做法较复杂的一个原因是链表是不带头结点的,所以要考虑新结点插入时会不会变成表头,
例如:当链表为空时, 插入3, 3变成表头, 再插入1, 链表为1->3, 链表头指向1, 代码必须处理这样的情况.
第一部分: 不含头结点的链表插入的非常规思路
下面我给出另外一个思路,新结点一律插入表头,这样就不要考虑链表为空的情况了。但是可能无序,可以从表头开始,相邻节点不符合次序要求交换即可,代码的逻辑非常简单,当然效率不高,因为交换(结构可能很大)的缘故。
例如 1->2, 插入3 构成 3->1->2, 先交换变成1->3->2,再交换变成1->2->3
下面是我简单的示例程序,附带说明了另外一个概念,插入的过程即是建表的过程。
1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <string.h> 4 struct node /*节点的数据结构*/ 5 { 6 int num; 7 char str[20]; 8 struct node *next; 9 };10 /* * * * * * * * * * *主函数 * * * * * * * * * * * * * * * * */11 int main(void)12 {13 struct node *insert(struct node *head,char *pstr,int n); /*函数声明 */14 void print(struct node *head); /*函数声明 */15 struct node *head;16 char str[20];17 int i, n;18 head=NULL; /*做空表*/19 20 for(i=0;i<5;i++){ //仅输入5组数据21 printf("\n input inserted num name:\n"); // 号码 空格 姓名22 scanf("%d", &n);23 gets(str); /*输入姓名*/24 head=insert(head,str,n); /* 将节点插入链表*/25 print(head); /*调用函数输出节点*/26 }27 return 0;28 }29 30 /* * * * * * * * * * * * * * 插入节点函数* * * * * * * * * * * * * * * */31 struct node *insert(struct node *head,char *pstr,int n) /*插入学号为n、姓名为pstr的节点*/32 {33 struct node *p;34 p=(struct node*)malloc(sizeof(struct node)); /*分配一个新节点*/35 strcpy (p->str,pstr); /*写入节点的姓名字串*/36 p->num=n; /* 指向学号*/37 38 /*新节点插入表头*/39 p->next=head;40 head=p;41 42 while(p->next&&p->next->num<p->num)43 {44 //交换邻接结点p和p->next的值(但是保留结点的next)45 struct node t;46 struct node *pr=p->next, *pn=p->next->next;47 t=*(p->next);48 *(p->next) = *p;49 p->next->next=pn;50 *p = t;51 p->next=pr;52 53 p=p->next;54 }55 return(head);/* 返回链表的头指针*/56 }57 58 59 /* * * * * * * * * * * * * * 链表输出函数* * * * * * * * * * * * * */60 void print (struct node *head)61 {62 struct node *temp;63 temp=head;64 printf("output strings:\n");65 while (temp!=NULL)66 {67 printf("%d -- %s\n",temp->num,temp->str);68 temp=temp->next;69 }70 return;71 }
输入如下
input inserted num name:
1 zhang
output strings:
1 -- zhang
input inserted num name:
2 li
output strings:
1 -- zhang
2 -- li
input inserted num name:
3 zhao
output strings:
1 -- zhang
2 -- li
3 -- zhao
input inserted num name:
4 qian
output strings:
1 -- zhang
2 -- li
3 -- zhao
4 -- qian
input inserted num name:
5 chen
output strings:
1 -- zhang
2 -- li
3 -- zhao
4 -- qian
5 -- chen
第二部分:带头结点的做法,我们分配一个结点永远做为链表头, 这样的话新插入的结点总是在后面,就没有前面提到的烦恼了.
1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <string.h> 4 struct node /*节点的数据结构*/ 5 { 6 int num; 7 char str[20]; 8 struct node *next; 9 };10 /* * * * * * * * * * *主函数 * * * * * * * * * * * * * * * * */11 int main(void)12 {13 void insert(struct node *head,char *pstr,int n); /*函数声明 */14 void print(struct node *head); /*函数声明 */15 struct node *head;16 char str[20];17 int i, n;18 head=(struct node*)malloc(sizeof(struct node)); /*head指向头结点*/19 head->next=NULL;20 21 for(i=0;i<5;i++){ //仅输入5组数据22 printf("\n input inserted num name:\n"); // 号码 空格 姓名23 scanf("%d", &n);24 gets(str); /*输入姓名*/25 insert(head,str,n); /* 将节点插入链表*/26 print(head); /*调用函数输出节点*/27 }28 return 0;29 }30 31 /* * * * * * * * * * * * * * 插入节点函数* * * * * * * * * * * * * * * */32 void insert(struct node *head,char *pstr,int n) /*插入学号为n、姓名为pstr的节点*/33 {34 struct node *p;35 p=(struct node*)malloc(sizeof(struct node)); /*分配一个新节点*/36 strcpy (p->str,pstr); /*写入节点的姓名字串*/37 p->num=n; /* 指向学号*/38 39 40 /*寻找p的插入位置*/41 while(head->next&&head->next->num<p->num)42 {43 head=head->next; 44 }45 46 /*应该在head之后插入p*/47 p->next=head->next;48 head->next=p; 49 }50 51 52 /* * * * * * * * * * * * * * 链表输出函数* * * * * * * * * * * * * */53 void print (struct node *head)54 {55 struct node *temp;56 temp=head->next;57 printf("output strings:\n");58 while (temp!=NULL)59 {60 printf("%d -- %s\n",temp->num,temp->str);61 temp=temp->next;62 }63 return;64 }
行18,19就是分配头结点并初始化, 头结点的data我们不用.
因为head不会改变, 第32行insert也没有必要返回值, 而且恰好弥补了书中接口设计的缺陷. 另外其实现大大简化了,也没有第一个部分的低效问题. 这里值得注意的是第41行head->next的做法,保证了p一定插在head 和head->next之间.
头结点的另外一个变化就是第56行,head->next才是第一个有效的数据结点.