鏈表在數據結構和算法中的重要性不言而喻。這裡我們要用C來實現鏈表(單鏈表)中的基本操作。對於鏈表的基本概念請參考《數據結構與算法之鏈表》這篇博客。
(1)定義單鏈表的節點類型
typedef int elemType ;
// 定義單鏈表結點類型
typedef struct ListNode{
elemType element; //數據域
struct ListNode *next; //地址域
}Node;
(2)初始化線性表
// 1.初始化線性表,即置單鏈表的表頭指針為空
void initList(Node *pNode){
pNode = NULL;
printf("%s函數執行,初始化成功\n",__FUNCTION__);
}
當聲明一個頭結點後,把該頭結點設置為空,即把數據域和地址域都設為空,即可完成該鏈表的初始化。
(3)創建線性表
// 2.創建線性表,此函數輸入負數終止讀取數據
Node *creatList(Node *pHead){
Node *p1;//表頭節點,始終指向頭結點
Node *p2;//表尾節點,始終指向鏈表的最後一個元素
p1 = p2 = (Node *)malloc(sizeof(Node)); //申請新節點,分配空間
if(p1 == NULL || p2 == NULL){
printf("內存分配失敗\n");
exit(0);
}
memset(p1,0,sizeof(Node));
scanf("%d",&p1->element); //輸入新節點的值
p1->next = NULL; //新節點的指針置為空
while(p1->element > 0){ //輸入的值大於0則繼續,直到輸入的值為負
if(pHead == NULL){ //空表,接入表頭
pHead = p1; //直接把p1作為頭結點,也可以理解為把pHead頭結點指向p1
}else{
p2->next = p1; //非空表,接入表尾
}
p2 = p1; //p1插入後,p1就是尾結點,所以p2要指向尾結點
p1 = (Node *)malloc(sizeof(Node)); //再重申請一個節點
if(p1 == NULL || p2 == NULL){
printf("內存分配失敗\n");
exit(0);
}
memset(p1,0,sizeof(Node));
scanf("%d",&p1->element);
p1->next = NULL;
}
printf("%s函數執行,鏈表創建成功\n",__FUNCTION__);
return pHead; //返回鏈表的頭指針
}
我這裡使用手動的方式輸入元素,直到輸入0或者負數停止。
(4)打印鏈表
// 3.打印鏈表,鏈表的遍歷
void printList(Node *pHead){
if(NULL == pHead){ //鏈表為空
printf("%s函數執行,鏈表為空\n",__FUNCTION__);
}else{
while(NULL != pHead){
printf("%d ",pHead->element);
pHead = pHead->next;
}
printf("\n");
}
}
使用地址域順序打印即可。
(5)清空鏈表
// 4.清除線性表L中的所有元素,即釋放單鏈表L中所有的結點,使之成為一個空表
void clearList(Node *pHead){
Node *pNext; //定義一個與pHead相鄰節點,理解為當前節點的下一個節點
if(pHead == NULL){
printf("%s函數執行,鏈表為空\n",__FUNCTION__);
}
while(pHead->next != NULL){
pNext = pHead->next;//保存下一結點的指針
free(pHead); //釋放當前節點
pHead = pNext; //指向下一個節點
}
printf("%s函數執行,鏈表已經清除\n",__FUNCTION__);
}
想要檢驗是否清空成功,可以使用(4)中的鏈表打印檢驗即可。
(6)計算鏈表長度
// 5.返回單鏈表的長度
int sizeList(Node *pHead){
int size = 0;
while(pHead != NULL){
size++;
pHead = pHead->next;
}
printf("%s函數執行,鏈表長度 %d \n",__FUNCTION__,size);
return size; //鏈表的實際長度
}
也就是計算有多少個節點。
(7)判斷鏈表是否為空
// 6.檢查單鏈表是否為空,若為空則返回1,否則返回0
int isEmptyList(Node *pHead){
if(pHead == NULL){
printf("%s函數執行,鏈表為空\n",__FUNCTION__);
return 1;
}
printf("%s函數執行,鏈表非空\n",__FUNCTION__);
return 0;
}
// 7.返回單鏈表中第pos個結點中的元素,若pos超出范圍,則停止程序運行
void getElement(Node *pHead, int pos){
int i = 0;
if(pos < 1){
printf("%s函數執行,pos值非法\n",__FUNCTION__);
}
if(pHead == NULL){
printf("%s函數執行,鏈表為空\n",__FUNCTION__);
}
while(pHead != NULL){
i++;
if(i == pos){
break;
}
pHead = pHead->next; //移到下一結點
}
if(i < pos){ //pos值超過鏈表長度
printf("%s函數執行,pos值超出鏈表長度\n",__FUNCTION__);
}
printf("%s函數執行,位置 %d 中的元素為 %d\n",__FUNCTION__,pos,pHead->element);
}
// 8.從單鏈表中查找具有給定值x的第一個元素,若查找成功則返回該結點data域的存儲地址,否則返回NULL
elemType* getElemAddr(Node *pHead, elemType x){
if(NULL == pHead){
printf("%s函數執行,鏈表為空\n",__FUNCTION__);
return NULL;
}
while((pHead->element != x) && (NULL != pHead->next)) {//判斷是否到鏈表末尾,以及是否存在所要找的元素
pHead = pHead->next;
}
if((pHead->element != x) && (pHead != NULL)){
//當到達最後一個節點
printf("%s函數執行,在鏈表中未找到x值\n",__FUNCTION__);
return NULL;
}
if(pHead->element == x){
printf("%s函數執行,元素 %d 的地址為 0x%x\n",__FUNCTION__,x,&(pHead->element));
}
return &(pHead->element);//返回元素的地址
}
(10)修改某個節點的值
// 9.把單鏈表中第pos個結點的值修改為x的值,若修改成功返回1,否則返回0
int modifyElem(Node *pNode,int pos,elemType x){
int i = 0;
if(NULL == pNode){
printf("%s函數執行,鏈表為空\n",__FUNCTION__);
return 0;
}
if(pos < 1){
printf("%s函數執行,pos值非法\n",__FUNCTION__);
return 0;
}
while(pNode != NULL){
i++;
if(i == pos){
break;
}
pNode = pNode->next; //移到下一結點
}
if(i < pos) { //pos值大於鏈表長度
printf("%s函數執行,pos值超出鏈表長度\n",__FUNCTION__);
return 0;
}
pNode->element = x;
printf("%s函數執行\n",__FUNCTION__);
return 1;
}
(11)表頭插入一個節點(頭節點)
// 10.向單鏈表的表頭插入一個元素
int insertHeadList(Node **pNode,elemType insertElem){
Node *pInsert;
pInsert = (Node *)malloc(sizeof(Node));
memset(pInsert,0,sizeof(Node));
pInsert->element = insertElem;
pInsert->next = *pNode;
*pNode = pInsert; //頭節點*pNode指向剛插入的節點,注意和上一行代碼的前後順序;
printf("%s函數執行,向表頭插入元素成功\n",__FUNCTION__);
return 1;
}
(12)表尾插入一個節點
// 11.向單鏈表的末尾添加一個元素
int insertLastList(Node **pNode,elemType insertElem){
Node *pInsert;
Node *pHead;
pHead = *pNode;
pInsert = (Node *)malloc(sizeof(Node)); //申請一個新節點
memset(pInsert,0,sizeof(Node));
pInsert->element = insertElem;
while(pHead->next != NULL){
pHead = pHead->next;
}
pHead->next = pInsert; //將鏈表末尾節點的下一結點指向新添加的節點
printf("%s函數執行,向表尾插入元素成功\n",__FUNCTION__);
return 1;
}
(13)測試函數
int main(int argc, const char * argv[]) {
Node *pList; //聲明頭結點
initList(pList); //鏈表初始化
printList(pList); //遍歷鏈表,打印鏈表
pList = creatList(pList); //創建鏈表
printList(pList);
sizeList(pList); //鏈表的長度
printList(pList);
isEmptyList(pList); //判斷鏈表是否為空鏈表
getElement(pList,3); //獲取第三個元素,如果元素不足3個,則返回0
printList(pList);
getElemAddr(pList,5); //獲得元素5的內存地址
modifyElem(pList,4,1); //將鏈表中位置4上的元素修改為1
printList(pList);
insertHeadList(&pList,5); //表頭插入元素5
printList(pList);
insertLastList(&pList,10); //表尾插入元素10
printList(pList);
clearList(pList); //清空鏈表
printList(pList);
return 0;
}