c語言鏈表的用法有哪些
鏈表是數據結構中比較基礎也是比較重要的類型之一,那麼有了數組,為什麼我們還需要鏈表呢!或者説設計鏈表這種數據結構的初衷在哪裏?下面小編就為大家介紹下c語言鏈表的用法。
鏈表的定義:
鏈表的定義一般使用結構體,在看《數據結構與算法分析》這本書的時候發現,書中頻繁的使用typedef的關鍵字,結果真的很棒不僅保持的代碼的整潔程度,也讓我們在下面的編碼過程中少見了很多煩人的指針(當然指針還是一直存在的)。所以這裏也借用了書中的定義方法。
struct Node;
typedef struct Node* PtrNode;
typedef PtrNode Position;
typedef PtrNode List;
struct Node{
int Value;
PtrNode Next;
};
下面接着書寫一個建立鏈表的函數,輸入每個節點的值,直到這個值是-1的時候函數結束。
在這個裏面,我以前一直搞不明白為什麼需要定義三個Node *,現在終於瞭解了,最終還是複習了指針的內容明白的,這裏説一下指針實現鏈表對指針的操作很頻繁,需要比較紮實的掌握了指針之後,在來看鏈表會輕鬆很多。在下面的一段程序裏,我分別定義了head/p/tmp這三個指向節點結構體的指針,head的主要作用就像一個傳銷頭目,他會主動聯繫上一個下線p,然後他就什麼也不幹了,p接着去發展一個又一個的下線tmp,結果一串以head為首的鏈表就出來了。
起先,我總覺得有了head,為什麼還要p,這是因為如果直接使用head去指向下一個節點,head的位置也是不斷在移動的,即它永遠處於鏈表的尾端,這樣當我們返回鏈表的時候,其實是空值。所以,我們需要p這個中轉環節。(其實,這種做法在指針中非常普遍,大部分有返回指針類型的函數中,都會首先定義一個指針變量來保存函數的傳入的參數,而不是對參數直接進行操作)。
/*
函數功能:創建一個鏈表
函數描述:每次輸入一個新的整數,即把新增加一個節點存放該整數,
當輸入的整數為-1時,函數結束。
*/
List create()
{
int n=0;
Position p,head,tmp;
head=NULL;
tmp=malloc(sizeof(struct Node));
if(tmp==NULL)
{
printf("tmp malloc failed!");
return NULL;
}
else
{
p=tmp;
printf("please input the first node's message!");
scanf("%d",&(tmp->Value));
}
while(tmp->Value!=-1)
{
n+=1;
if(n==1)
{
head=p;
tmp->Next=NULL;
}
else
{
p->Next=tmp;
}
p=tmp;
tmp=malloc(sizeof(struct Node));
printf("please input the %d node!",n+1);
scanf("%d",&(tmp->Value));
}
p->Next=NULL;
free(tmp); //free函數free掉的只是申請的空間,但是指針還是依然存在的。
tmp=NULL;
return head;
}
接下來,在寫一個刪除鏈表節點的函數,輸入一個整數然後遍歷鏈表節點,當鏈表節點的值與該整數相等的時候,即把該節點刪除。
在完成這個函數首先一定要把這個過程思考清楚,不可否認我之前是一個上來就敲代碼的人,看了《劍指offer》感覺這種習慣是程序員的大忌,甚至還想寫一篇博客,名字都想好了《程序員的自我修養之思考在前,代碼在後》。其實想想也是,我們寫程序的目的是為了解決問題,而不是為了簡單的寫程序,純粹的讓程序跑起來大概只會在上學那會存在吧!真實的程序開發中需要考慮幾乎所有 能想到的實際問題,所以無論程序再下,一要學會先思考清楚,再下筆寫程序。
關於這個函數,我們要想到的是:
1 如果鏈表為空,我們該怎麼做,當然是直接返回。
2 如果要刪除的元素為頭節點該怎麼辦?
3 如果要刪除的元素為尾節點該怎麼辦?
當注意到以上三個部分,我們的程序就可能避免掉了輸入鏈表為空,程序直接崩潰的現象,也可以避免刪除元素值為頭節點時刪不掉的尷尬。我們的.程序就有了一定的魯棒性。
下面着重考慮鏈表的刪除的實現:
list: ???? Node_a->Node_b->Node_c->Node_d;
?????????????? list ?????? tmp???????? p
-------> ?????? ? ? ? tmp->Next=p->Next;
list:?????? Node_a->Node_b----------->Node_d
????????????????????????????????????? free(p)
假設我們要刪除的節點為上圖的Node_c;假設我們能夠找到Node_c的前一個位置tmp和被刪除節點位置p的話;這個時候我們只需要執行tmp->Next=p->Next即可。
只要完成上面的分析以及考慮到各種情況,我們完成下面的代碼就水到渠成了。
/*
函數功能:刪除鏈表中指定值的節點(如果存在多個,只刪除第一個)
本例中輸入一個整數,刪除鏈表節點值為這個整數的節點。
*/
List DeleteNode(List list)
{
Position p,tmp;
int value;
if(list==NULL)
{
printf("The list is null,function return!");
return NULL;
}
else
{
printf("please input the Node's value:");
scanf("%d",&value);
}
p=list;
if(p->Value==value)
{
list=p->Next;
free(p);
p=NULL;
return list;
}
while(p!=NULL&&p->Value!=value)
{
tmp=p;
p=p->Next;
}
if(p->Value==value)
{
if(p->Next!=NULL){
tmp->Next=p->Next;
}
else
{
tmp->Next=NULL;
}
free(p);
p=NULL;
}
return list;
}
關於鏈表的使用場景分析:
鏈表在程序開發中用到的頻率還是非常高的,所以在高級語言中往往會對鏈表進行一些實現,比如STL中list以及Java中也有類似的東西。在目前的服務器端開發,主要運用鏈表來接收一些從數據中取出來的數據進行處理。
即使你不知道鏈表的底層實現,仍然可以成功的運用STL裏面的現成的東西。但是作為一個學習者,我覺得會使用和從底層掌握仍然是兩個不同的概念,linux之父説:“talk is less,show you code”。
以下的程序,用鏈表模擬了一個電話通訊錄的功能,包括添加聯繫人,查找聯繫人,以及刪除聯繫人。
PS:關於魯棒性,程序中最大的危險是使用了gets這個函數,目前先保留使用gets,等待找到工作之後在做進一步的程序完善。(尼瑪,讀書去。。。應屆生,找個工作他媽咋這麼難呢!?? 工作經驗,工作經驗,艹,那個大牛一出校門就什麼都會。)
/**************************************************************************
Programe:
This is a phone list write by list
The programe is just prictise for list
Author: heat nan
Data:2015/07/27
**************************************************************************/
#include
#include
#include
#define N 25
#define M 15
struct node;
typedef struct node* p_node;
typedef p_node List;
typedef p_node Position;
typedef struct node** PList;
struct node{
char name[N];
char number[M];
Position next;
};
int JudgeNameExist(List list,char* name);
void AddPerson(PList list);
void PrintList(List list);
List FindPerson(List list);
List FindPersonByName(List list,char* name);
int AddPersonByName(PList list,List node);
int DeletePersonByName(PList list,char* name);
void DeletePerson(PList list);
int main()
{
List list=NULL;
Position p;
char cmd[100];
while(1)
{
printf(" MAIN");
printf(" ******* 1 add a person *******");
printf(" ******* 2 show the phone list *******");
printf(" ******* 3 find from phone list *******");
printf(" ******* 4 from phone list *******");
printf("Please input the cmd number:");
gets(cmd);
switch(cmd[0])
{
case '1':
AddPerson(&list);
break;
case '2':
PrintList(list);
break;
case '3':
FindPerson(list);
break;
case '4':
DeletePerson(&list);
break;
default:
printf("wrong cmd!");
break;
}
}
return 0;
}
/*
Function:判斷要添加的聯繫人名稱是否已經存在於電話簿中.
Input: List 電話列表,name 要添加的聯繫人的姓名.
Return: 已經存在返回1,不存在返回0.
*/
int JudgeNameExist(List list,char* name)
{
if(FindPersonByName(list,name)!=NULL)
return 1;
else
return 0;
}
/*
Function:根據輸入的姓名查找聯繫人的信息節點
Input: 要輸入的電話列表list,姓名name
Return: 返回查找到的節點
*/
List FindPersonByName(List list,char* name)
{
while(list!=NULL)
{
if(strcmp(list->name,name)==0)
break;
list=list->next;
}
return list;
}
/*
Function:根據姓名添加新的聯繫人到聯繫人列表
Input: 指向聯繫人列表地址的指針, 新用户節點
Return: 添加成功返回1,添加失敗返回0
*/
int AddPersonByName(PList list,List node)
{
if(node==NULL)
{
printf("the node is NULL!");
return 0;
}
if(*list==NULL)
{
*list=node;
return 1;
}
List pHead=*list;
while(pHead->next!=NULL)
pHead=pHead->next;
pHead->next=node;
return 1;
}
void AddPerson(PList list)
{
Position tmp;
Position p_head;
tmp=(struct node*)malloc(sizeof(struct node));
char name[N];
char number[M];
if(tmp==NULL)
{
printf("malloc the tmp node failed in function add person!");
}
else
{
printf("please input the name:");
gets(name);
printf("please input the number:");
gets(number);
strcpy(tmp->name,name);
strcpy(tmp->number,number);
tmp->next=NULL;
}
if(JudgeNameExist(*list,name)==1)
{
free(tmp);
printf("the name have already exist!");
return;
}
AddPersonByName(list,tmp);
}
/*
Function: 打印聯繫人列表
Input: 聯繫人列表
*/
void PrintList(List list)
{
Position show;
show=list;
if(show==NULL)
{
return ;
}
printf("Now,we print the phone list:");
while(show!=NULL)
{
printf("Name:%s Number:%s",show->name,show->number);
show=show->next;
}
}
List FindPerson(List list)
{
char name[N];
Position pHead=list;
printf("please input the name you will find:");
gets(name);
Position node=FindPersonByName(list,name);
if(node!=NULL)
printf("find success! name-> %s number-> %s",node->name,node->number);
else
printf("find failed!");
return node;
}
/*
Function:根據姓名刪除聯繫人
Input: 指向聯繫人地址的指針,聯繫人姓名
Output: 刪除成功返回1,失敗返回0
*/
int DeletePersonByName(PList list,char* name)
{
if(*list==NULL||name==NULL)
return 0;
List pHead=*list;
if(strcmp(pHead->name,name)==0)
{
*list=pHead->next;
free(pHead);
pHead->next==NULL;
return 0;
}
List tmp=pHead->next;
while(tmp!=NULL)
{
if(strcmp(tmp->name,name)==0)
{
pHead->next=tmp->next;
free(tmp);
tmp->next=NULL;
return 1;
}
pHead=tmp;
tmp=tmp->next;
}
return 0;
}
void DeletePerson(PList list)
{
List pHead=*list;
if(pHead==NULL)
{
printf("there is no person you can delet");
return ;
}
char name[N];
printf("please input the name:");
gets(name);
DeletePersonByName(list,name);
}