上回我們學習了用數組構造順序表,順序表申請的一塊數組地址是連續的,這次我們學習用指針構造鏈表,這時所構造的鏈表地址不一定是連續的,比較活用。
鏈表分爲單鏈表、循環鏈表、雙向鏈表。
今天先來碼單鏈表,鏈表可以帶頭結點,也可以不帶頭結點,帶頭結點更方便插入、刪除的運算,所以我選擇了帶頭結點。
- 不帶頭結點的單鏈表
- 帶頭結點的單鏈表
/*頭文件及宏定義*/
#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指針指向的地址,這是和小夥伴討論了許久纔得到的答案。懂得了指針的含義,鏈表也就很容易地寫出來了。
循環鏈表由於只是在單鏈表的基礎上,將尾結點指向頭結點,所以就不再多加贅述。