上回我们学习了用数组构造顺序表,顺序表申请的一块数组地址是连续的,这次我们学习用指针构造链表,这时所构造的链表地址不一定是连续的,比较活用。
链表分为单链表、循环链表、双向链表。
今天先来码单链表,链表可以带头结点,也可以不带头结点,带头结点更方便插入、删除的运算,所以我选择了带头结点。
- 不带头结点的单链表
- 带头结点的单链表
/*头文件及宏定义*/
#include<stdio.h>
#include<stdlib.h>
typedef int datatype;
typedef struct node //定义结点
{
datatype data; //数据域
struct node *next; //指针域
}LNode;
链表的建立
链表建立可以用两种方法:头插法和尾接法。
- 头插法:在链表头一个元素结点前插入新的元素,这样插入的链表元素是倒着 的顺序。
- 尾接法:在链表的尾结点后新增元素,这样建立的链表是顺着的。
//头插法(往首元素前面插,倒着创建)
LNode *CreateLinkList1()
{
LNode *head,*p; //head为链表的头指针,p为链表的暂存指针
int x;
head=(LNode *)malloc(sizeof(LNode)); //生成头结点
head->next=NULL;
printf("请输入单链表元素:");
scanf("%d",&x);
while(x!='\n')
{
p=(LNode*)malloc(sizeof(LNode)); //申请新结点
p->data=x;
p->next=head->next; //新结点指向头元素的结点
head->next=p; //head指向新结点
scanf("%d",&x);
}
return head;
}
//尾接法(在最后一个元素末尾插,顺着建立)
LNode *CreateLinkList2()
{
LNode *head,*p,*q; //head为链表的头指针,p为链表的暂存指针,q为指向末尾结点的指针
int x;
head=(LNode *)malloc(sizeof(LNode)); //生成头结点
head->next=NULL;
q=head; //在链表没有元素的情况下可以尾指针q可以看作是头指针
printf("请输入单链表元素:");
scanf("%d",&x);
while(x!=-1) //输入-1则停止
{
p=(LNode*)malloc(sizeof(LNode)); //申请暂存结点空间
p->data=x;
p->next=NULL;
q->next=p; //将链表连接起来
q=p; //q指向最新的结点,即尾结点
scanf("%d",&x);
}
return head;
}
//求表长
int Length_LinkList(LNode *head)
{
LNode *p=head;
int i=0;
while(p->next!=NULL)
{
p=p->next;
i++;
//printf("%d %c\n",i,p->data); 用来测试
}
return i;
}
查找运算
查找头结点为head的链表第i个元素
//按位置查找
LNode *Get_LinkList(LNode *head,int i)
{
LNode *p=head;
int j=0;
while(p->next!=0&&j<i)
{
j++;
p=p->next;
}
if(i==j) //若找到则返回指针值,否则返回空值
return p;
else
return NULL;
}
插入运算
在第i个位置插入元素x
//插入运算
void Insert_LinkList(LNode *head,int i,int x)//head指向头结点,i为插入的位置,x为插入的值
{
LNode *p,*q;
q=Get_LinkList(head,i-1); //找到要插入结点的前一个位置
if(q==NULL)
printf("位置非法,无法插入");
else
{
p=(LNode*)malloc(sizeof(LNode)); //申请结点空间
p->data=x;
p->next=q->next; //插入结点
q->next=p;
}
}
删除运算
删除第i个元素,这里运用free()函数释放内存空间
//删除运算
void Del_LinkList(LNode *head,int i) //head指向头结点,i为要删除的结点位置
{
LNode *p,*q;
q=Get_LinkList(head,i-1); //找到要删除结点的前一个位置
if(q==NULL)
printf("删除位置非法!");
else
{
p=q->next; //p指向要删除的i结点
q->next=p->next; //从链表中删除第i个结点
free(p); //释放第i个结点的存储空间
}
}
调试代码
int main()
{
/*建立链表并输出*/
LNode *head,*p,*q;
head=CreateLinkList2();
p=head; //使p指向头结点,以下操作皆是
printf("表长为:%d\n",Length_LinkList(p));
p=p->next;
printf("链表元素为:");
while(p!=NULL)
{
printf("%d ",p->data);
p=p->next;
}
/*查找*/
int a;
printf("请输入要查找的元素位置:");
scanf("%d",&a);
p=head;
q=Get_LinkList(p,a);
if(q!=NULL)
printf("元素值为:%d",q->data);
else
printf("该元素不存在!");
/*插入(用时取,不用则注释掉)*/
p=head;
int i;
int x;
printf("请输入要插入的位置:");
scanf("%d",&i);
printf("请输入要插入的元素值:");
scanf("%d",&x);
Insert_LinkList(p,i,x);
/*删除(用时取,不用则注释掉)*/
p=head;
int i;
int x;
printf("请输入要删除的位置:");
scanf("%d",&i);
Del_LinkList(p,i);
/*输出*/
p=head;
printf("表长为:%d\n",Length_LinkList(p));
p=p->next;
printf("链表元素为:");
while(p!=NULL)
{
printf("%d ",p->data);
p=p->next;
}
return 0;
}
总结
这里的链表确实考验对指针的理解,我也是思考了很久才得出可以让自己信服的解释,指针指向一个结点,其中p=head我想了很久,其含义为p指针指向head指针指向的地址,这是和小伙伴讨论了许久才得到的答案。懂得了指针的含义,链表也就很容易地写出来了。
循环链表由于只是在单链表的基础上,将尾结点指向头结点,所以就不再多加赘述。