C語言鏈表
概述
當每一個數據元素都和它下一個數據元素用指針鏈接在一起時,就形成了一個鏈,這個鏈子的頭就位於第一個數據元素,這樣的存儲方式就是鏈式存儲。它是動態的進行存儲分配的一種結構。
數組和鏈表
數組的優勢,在於可以方便的遍歷查找需要的數據。但是,這種時間上的便利性,是因爲數組在內存中佔用了連續的空間,在進行類似的查找或者遍歷時,本質是指針在內存中的定向偏移。然而,當需要對數組成員進行添加和刪除的操作時,數組內完成這類操作的時間複雜度則變成了O(n)。
鏈表的特性,使其在某些操作上比數組更加高效。例如當進行插入和刪除操作時,鏈表操作的時間複雜度僅爲O(1)。另外,因爲鏈表在內存中不是連續存儲的,所以可以充分利用內存中的碎片空間。除此之外,鏈表還是很多算法的基礎,最常見的哈希表就是基於鏈表來實現的。
鏈表節點元素的構成
在鏈表中有一個頭指針變量,圖中head表示的就是頭指針,這個指針變量保存一個地址。從圖中的箭頭可以看到該地址爲一個變量的地址,也就是說頭指針指向一個變量。這個變量稱爲元素,在鏈表中每一個元素包括兩個部分:數據部分和指針部分。數據部分用來存放元素所包含的數據,而指針部分用來指向下一個元素。最後一個元素指針指向NULL,表示指向的地址爲空
每個元素由兩部分組成:數據域(本身的信息)和指針域(指向後繼的指針)。
typedef struct student
{
char name[10]; //數據元素
float score; //數據元素
struct student *next; //指向下一個結點的位置
} pstudent;
通過一個簡單的程序來熟悉對鏈表的基本操作
void main()
{
int a = 0;
pstudent* head;
head = creat(3);
change(head,2);
head = Insert(head, 4);
head = Delete(head,4);
Traversal(head);//遍歷的是執行Delete()函數後的鏈表
a = Length(head);
printf("%d\n",a);
}
- 調用函數時,當head(頭指針)的指向發生改變時,我們應該返回一個新的指針(head),避免別的函數傳參時發生錯誤。
靜態鏈表(簡單鏈表)
靜態鏈表有助於很好的理解動態鏈表:
struct student
{
long num;
float score;
struct student* next;
};
void main()
{
struct student a,b,c,*head,*p;
a.num = 10101; a.score = 89;
b.num = 10101; b.score = 90;
c.num = 10101; c.score = 99;
head = &a;
a.next = &b;
b.next = &c;
c.next = NULL;
p = head;
do
{
printf("%ld %5.1f\n",p->num,p->score);
p = p->next;
}while(p != NULL);
}
動態鏈表的創建
頭指針:只有指針的元素,並沒有數據元素。
頭節點:頭節點的數據類型與首節點的數據類型相同,並且頭節點是首節點前面的那個節點,並不存放有效數據;頭節點的存在只是爲了方便鏈表的操作。
創建鏈表的方法有好多種,可以參考下文:創建鏈表的四種方法
以下代碼是自己對鏈表的理解所寫,理解起來更容易
pstudent* creat(int n)
{
pstudent* head;//頭指針
pstudent* node;//頭節點(數據域和指針域)
pstudent* next;//下一節點(數據域和指針域)
node = (pstudent*)malloc(sizeof(pstudent));
head = NULL;//n=0時爲空鏈表
if(n != 0)
{
head = node;//頭指針指向頭節點
for (int i = 0; i < n; i++)
{
next = (pstudent*)malloc(sizeof(pstudent));
printf("please input %d node value:\n",i+1);
printf("please input name:\n");
fflush(stdin);//情除緩存區
gets(node->name);
printf("please input score:\n");
fflush(stdin);//(注1)
scanf("%f", &node->score);
system("cls");//清屏
node->next = next;//頭節點指向下一個節點
node = next;//指向下一個元素(注2)
}
node->next = NULL;//結束創建(注3)
}
return head;
}
- 第一次輸入的回車可能會被第二次輸入操作所捕捉,這個的作用就是清空緩衝,這樣就不會出現這種情況了。
- node = next,頭節點輸入完成,該next節點輸入,由於使用for循環,第二次循環仍爲node節點(node->name),而我們需要的是第二個節點,故在第一次循環結束時,將node指向了第二個節點next,即node = next,此時的node不再是頭節點。注意不能寫成next = node,node節點是我們每次錄入的節點,該地址應該在本次錄入完成後賦予下一個節點的地址,故node爲左值。
- node->next = NULL,循環結束後,最後一個節點之後不再有節點,故下一個節點爲空。
鏈表之遍歷輸出
void Traversal(pstudent* head)
{
printf("Output Traversal Message:\n");
while (head->next != NULL)
{
printf("%s\n", head->name);
printf("%f\n\n", head->score);
head = head->next;
}
}
鏈表之長度
int Length(pstudent* head)
{
int length = 0;
while(head->next != NULL)
{
++length;
head = head->next;
}
return length;
}
鏈表之修改節點
void change(pstudent* head,int n)
{
int i = 1;
pstudent* node = head;
if (n == 1)
{
printf("please input change name:\n");
fflush(stdin);
scanf("%s", &node->name);
printf("please input change score:\n");
fflush(stdin);
scanf("%f", &node->score);
}
else
{
while (i < n && node != NULL)
{
node = node->next;
i++;
}
if (node != NULL)
{
printf("please input change name:\n");
fflush(stdin);
scanf("%s", &node->name);
printf("please input change score:\n");
fflush(stdin);
scanf("%f", &node->score);
}
else
{
printf("not exit node");
}
}
}
鏈表之插入節點
pstudent* Insert(pstudent* head, int position)
{
int i = 2;
pstudent* node = head;//定義一個指針node,用來移動指向鏈表節點的位置
pstudent* insert = (pstudent*)malloc(sizeof(pstudent));
printf("please input insert name:\n");
fflush(stdin);
gets(insert->name);
printf("please input insert score:\n");
fflush(stdin);
scanf("%f", &insert->score);
if(position == 1)
{
insert->next = head;
head = insert;
}
else if(position == 2)
{
insert->next = node->next;
node->next = insert;
}
else
{
while (i < position && node != NULL)
{
node = node->next;
i++;
}
if (node != NULL)
{
insert->next = node->next;
node->next = insert;
}
else if(node == NULL)
{
node->next = insert;
insert->next = NULL;
}
else
{
printf("not exit node");
}
}
return head;
}
鏈表之刪除節點
pstudent* Delete(pstudent* head, int n)
{
int i = 1;
pstudent* node = head;
pstudent* front;
if(n == 1)
{
head = node->next;
free(node);
}
else
{
while (i < n && node != NULL)
{
front = node;
node = node->next;
i++;
}
if (node != NULL)
{
front->next = node->next;
free(node);
}
else
{
puts("not exit node");
}
}
return head;
}