#include <stdio.h>
#include <stdlib.h>
/* 单链表的简单代码 */
/* 此步骤为建立对应的表格结构 */
struct list {
int number;
struct list *next;
};
/* ?若不定义转换,在sizeof(struct list)会报错 */
typedef struct list LIST;
/* 此步骤为创建链表函数,需要用到head p1,p2三个指针 */
LIST *creat(int n)
{
int i,x;
LIST *head,*p1,*p2;
head=NULL;
for(i=0;i<n;i++)
{
p1=(LIST*)malloc(sizeof(LIST));
printf("请输入第%d个数: ",i);
scanf("%d",&x);
p1->number=x;
if(head==NULL)
{
head=p1;p2=p1;
}
else
{
p2->next=p1;
p2=p1;
}
p2->next=NULL;
}
return head;
}
/* 此步骤为输出函数 */
void output(LIST *p)
{
int i=0;
LIST *q;
while(p!= NULL)
{
printf("第%d个数是: %d\n",i+1,p->number);
q=p->next;
p=q;
i++;
}
}
/* 此步骤为添加内容函数,为保证可读性,存在问题:输入n为0或者为负时,无改变表头和报错 */
void additem(LIST *p)
{
int n,i,x;
LIST *q;
printf("请输入要插入在第几个数据后:");
scanf("%d",&n);
printf("请输入要插入的数据:");
scanf("%d",&x);
// if(n=0)
// {
// q=(LIST*)malloc(sizeof(LIST));
// q->number=x;
// q->next=p;
//注释:此步未完成,为便于自己理解基本结构
// }
// if(n<0){
// }
for(i=0;i<n-1;i++) /* 此处i=n-1 比如删除第5位数据,即删除a[4],此时i从0遍历,i=0循环完,p指向a[1],i=4循环完,p指向a[5] 下面删除元素同理 */
{
q=p->next;
p=q;
}
q=(LIST*)malloc(sizeof(LIST));
q->number=x;
q->next=p->next;
p->next=q;
}
/* 此步骤为删除元素,同样没有健壮性处理 */
void del(LIST *p)
{
int n,i;
LIST *q1,*q2;
printf("请输入要删除的元素位数:");
scanf("%d",&n);
for(i=0;i<n-1;i++)
{
q1=p->next;
q2=p;
p=q1;
}
q1=p->next; /* 此步骤完成之后 q1是n+1位置;q2是n-1位置;p则是n的位置 */
q2->next =q1; /* 将p元素删除去 */
free(p); /* 此步骤必不可少,易忘 */
}
/* 此步骤为删除整个函数,必不可少的一步,case 4 case 5均需要 */
void free_list(LIST *p)
{
LIST *q;
while(p!=NULL)
{
q=p->next;
free(p);
p=q;
}
}
/* 补充部分:重组链表,分治为两个链表 */
LIST *A_B(LIST *p)
{
LIST *q,*head,*temp;
head=p->next;
q=p->next;
while(p->next!=NULL)
{
temp=p->next; /*temp为临时节点 */
p->next=temp->next; /*重新构造p节点,跨过原来的下一位来构筑 */
temp=p->next; /*使临时节点指向q之后的节点,用于连接链2 */
q->next=temp; /* 重新链接链2*/
q=q->next;
}
q->next =NULL;
p->next=NULL;
return head;
}
/* 此步骤为输主函数,链表过程实现全部由子函数完成 */
int main(int argc, char *argv[]) {
LIST *p;
int n,select;
remake:
printf("请输入链表长度:");
scanf("%d",&n);
p=creat(n);
printf("链表建立完成!\n");
selection:
printf("=====================================\n");
printf("请按以下提示操作:\n"
"0.退出程序\n"
"1.顺序输出链表\n"
"2.插入数据\n"
"3.删除数据\n"
"4.重建链表\n"
"5.删除链表\n");
scanf("%d",&select);
printf("=====================================\n\a");
switch(select)
{
case 1:
output(p);
break;
case 2:
additem(p);
break;
case 3:
del(p);
break;
case 4:
free_list(p);
goto remake;
break;
case 5:
free_list(p);
break;
case 0:
return 0;
default:
printf("你输入了错误的代码!\n\a");
}
goto selection;
return 0;
}
补充:
1.上述代码创建链表采用尾插法——用一个指针始终指向尾部,在尾部构建后续链表。
在此补充另一种建表方式——头插法,即每次新数据在原有前方插入,代码如下:
/* 头插法 */
LIST *creat(int n)
{
int i,x;
LIST *q,*p;
q=(LIST*)malloc(sizeof(LIST));
q->next=NULL;
for(i=0;i<n;i++)
{
p=(LIST*)malloc(sizeof(LIST));
printf("请输入第%d个数: ",i);
scanf("%d",&x);
p->number=x;
p->next=q->next;
q->next=p; /* 在q与q->next两个之间插入数值,新的q->next变为插入的数 */
}
return p; /*此处也可返回q用来表示空的头结点*/
}
2.关于头结点:
创建头结点,有时候会是删除、插入等各种操作(如上删除操作,如果删除第一个链,没有头结点的时候非常麻烦,还要重新定头等)的时候更加便于理解,特别是在比较复杂的链表中。