http://blog.csdn.net/fisherwan/article/details/19760681
上午寫了下單向循環鏈表的程序,今天下午我把雙向鏈表的程序寫完了。其實雙向鏈表和單向鏈表也是有很多相似的地方的,聽名字可以猜到,每個節點都包含兩個指針,一個指針指向上一個節點,一個指針指向下一個節點。這里有兩個特殊的地方,第一就是頭節點的一個指針指向NULL空指針(沒有前驅節點),第二就是尾節點的一個指針指向NULL指針(沒有后繼節點)。
我們可以看下雙向鏈表的示意圖(自己畫的比較難看):

所以,我們在編程序的時候,這兩個指針的控制就是我們的難點,因為我們始終要讓這個鏈表保持這樣的鏈接不管是在創建的時候、插入的時候、刪除的時候等,一定要讓節點的兩個指針指向正確的節點。下面我們來看下雙向鏈表的代碼。
DbLinkList.h? 頭文件——包含節點結構的定義和各種操作函數的聲明。
- #ifndef?DOUBLY_LINKED_LIST_H??
- #define?DOUBLY_LINKED_LIST_H??
- ??
- typedef?struct?Node??
- {??
- ????int?data;??
- ????struct?Node?*pNext;??
- ????struct?Node?*pPre;??
- }NODE,?*pNODE;??
- ??
- ??
- pNODE?CreateDbLinkList(void);??
- ??
- ??
- void?TraverseDbLinkList(pNODE?pHead);??
- ??
- ??
- int?IsEmptyDbLinkList(pNODE?pHead);??
- ??
- ??
- int?GetLengthDbLinkList(pNODE?pHead);??
- ??
- ??
- int?InsertEleDbLinkList(pNODE?pHead,?int?pos,?int?data);??
- ??
- ??
- int?DeleteEleDbLinkList(pNODE?pHead,?int?pos);??
- ??
- ??
- void?FreeMemory(pNODE?*ppHead);??
- ??
- #endif??
DbLinkList.cpp
雙向鏈表的源文件——包含了各種操作函數的定義。
(1)這部分是創建雙向鏈表,和單向鏈表很相似,但是呢,有些地方還是得注意,就是每創建一個節點的時候都要注意初始化它的兩個指針。
- #include?<stdio.h>??
- #include?<stdlib.h>??
- #include?"DbLinkList.h"??
- ??
- ??
- pNODE?CreateDbLinkList(void)??
- {??
- ????int?i,?length?=?0,?data?=?0;??
- ????pNODE?pTail?=?NULL,?p_new?=?NULL;??
- ????pNODE?pHead?=?(pNODE)malloc(sizeof(NODE));??
- ??
- ????if?(NULL?==?pHead)??
- ????{??
- ????????printf("內存分配失敗!\n");??
- ????????exit(EXIT_FAILURE);??
- ????}??
- ??????
- ????pHead->data?=?0;??
- ????pHead->pPre?=?NULL;??
- ????pHead->pNext?=?NULL;??
- ????pTail?=?pHead;??
- ??
- ????printf("請輸入想要創建鏈表的長度:");??
- ????scanf("%d",?&length);??
- ??
- ????for?(i=1;?i<length+1;?i++)??
- ????{??
- ????????p_new?=?(pNODE)malloc(sizeof(NODE));??
- ??
- ????????if?(NULL?==?p_new)??
- ????????{??
- ????????????printf("內存分配失敗!\n");??
- ????????????exit(EXIT_FAILURE);??
- ????????}??
- ??
- ????????printf("請輸入第%d個元素的值:",?i);??
- ????????scanf("%d",?&data);??
- ??
- ????????p_new->data?=?data;??
- ????????p_new->pNext?=?NULL;??
- ????????p_new->pPre?=?pTail;??
- ????????pTail->pNext?=?p_new;??
- ????????pTail?=?p_new;??
- ????}??
- ??
- ????return?pHead;??
- }??
(2)這部分是獲得雙向鏈表的信息,這里和單向鏈表基本一致,因為遍歷的時候只用到了一個指針。
- ??
- void?TraverseDbLinkList(pNODE?pHead)??
- {??
- ????pNODE?pt?=?pHead->pNext;??
- ??
- ????printf("打印鏈表如:");??
- ????while?(pt?!=?NULL)??
- ????{??
- ????????printf("%d?",?pt->data);??
- ????????pt?=?pt->pNext;??
- ????}??
- ????putchar('\n');??
- }??
- ??
- ??
- int?IsEmptyDbLinkList(pNODE?pHead)??
- {??
- ????pNODE?pt?=?pHead->pNext;??
- ??
- ????if?(p?==?NULL)??
- ????????return?1;??
- ????else??
- ????????return?0;??
- }??
- ??
- ??
- int?GetLengthDbLinkList(pNODE?pHead)??
- {??
- ????int?length?=?0;??
- ????pNODE?pt?=?pHead->pNext;??
- ??
- ????while?(pt?!=?NULL)??
- ????{??
- ????????length++;??
- ????????pt?=?pt->pNext;??
- ????}??
- ????return?length;??
- }??
(3)這部分是向雙向鏈表插入節點,也跟單向鏈表很多相似的地方。我們先來看下插入節點時的示意圖:

從圖中可以看到,每次我們添加一個節點都有很多地方要調節的,也就是每個節點的那兩個指針,一定要認真仔細自己動手寫一遍,有可能有些細節就會出錯。這里有一個地方需要注意,是和單向鏈表不同的地方,單向鏈表在插入節點的時候不需要判斷最后一個節點是否為空,因為這不影響程序的結果,但是對于雙向鏈表就不一樣了,因為我們后面要用到最后一個節點的一個指針指向前一個節點,如果最后一個節點是空的話(就是程序中的pt),就不存在pt->pPre了,那么程序運行到這里時就會報錯,所以我們要加個判斷,判斷此時節點是NULL的話就不需要控制它的指針了。
- ??
- int?InsertEleDbLinkList(pNODE?pHead,?int?pos,?int?data)??
- {??
- ????pNODE?pt?=?NULL,?p_new?=?NULL;??
- ??
- ????if?(pos?>?0?&&?pos?<?GetLengthDbLinkList(pHead)+2)??
- ????{??
- ????????p_new?=?(pNODE)malloc(sizeof(NODE));??
- ??
- ????????if?(NULL?==?p_new)??
- ????????{??
- ????????????printf("內存分配失敗!\n");??
- ????????????exit(EXIT_FAILURE);??
- ????????}??
- ??
- ????????while?(1)??
- ????????{??
- ????????????pos--;??
- ????????????if?(0?==?pos)??
- ????????????????break;??
- ????????????pHead?=?pHead->pNext;??
- ????????}??
- ??????????
- ????????pt?=?pHead->pNext;??
- ????????p_new->data?=?data;??
- ????????p_new->pNext?=?pt;??
- ????????if?(NULL?!=?pt)??
- ????????????pt->pPre?=?p_add;??
- ????????p_new->pPre?=?pHead;??
- ????????pHead->pNext?=?p_new;??
- ??????????
- ????????return?1;??
- ????}??
- ????else??
- ????????return?0;??
- }??
(4)這部分是從鏈表中刪除節點,當然這里和單向鏈表差不多,要注意的地方和插入節點時是一樣的,上面已經說明了。
- ??
- int?DeleteEleDbLinkList(pNODE?pHead,?int?pos)??
- {??
- ????pNODE?pt?=?NULL;??
- ??
- ????if?(pos?>?0?&&?pos?<?GetLengthDbLinkList(pHead)?+?1)??
- ????{??
- ????????while?(1)??
- ????????{??
- ????????????pos--;??
- ????????????if?(0?==?pos)??
- ????????????????break;??
- ????????????pHead?=?pHead->pNext;??
- ????????}??
- ??
- ????????pt?=?pHead->pNext->pNext;??
- ????????free(pHead->pNext);??
- ????????pHead->pNext?=?pt;??
- ????????if?(NULL?!=?pt)??
- ????????????pt->pPre?=?pHead;??
- ??
- ????????return?1;??
- ????}??
- ????else??
- ????????return?0;??
- }??
(5)這部分是用來釋放內存的,注意的地方和上面一樣。
- ??
- void?FreeMemory(pNODE?*ppHead)??
- {??
- ????pNODE?pt?=?NULL;??
- ??
- ????while?(*ppHead?!=?NULL)??
- ????{??
- ????????pt?=?(*ppHead)->pNext;??
- ????????free(*ppHead);??
- ????????if?(NULL?!=?pt)??
- ????????????pt->pPre?=?NULL;??
- ????????*ppHead?=?pt;??
- ????}??
- }??
main.cpp
測試程序源文件——通過簡單的交互信息來測試各個函數功能是否正確。
- #include?<stdio.h>??
- #include?<stdlib.h>??
- #include?"DbLinkList.h"??
- ??
- int?main(void)??
- {??
- ????int?flag?=?0,?length?=?0;??
- ????int?position?=?0,?value?=?0;??
- ????pNODE?head?=?NULL;??
- ??
- ????head?=?CreateDbLinkList();??
- ??
- ????flag?=?IsEmptyDbLinkList(head);??
- ????if?(flag)??
- ????????printf("雙向鏈表為空!\n");??
- ????else??
- ????{??
- ????????length?=?GetLengthDbLinkList(head);??
- ????????printf("雙向鏈表的長度為:%d\n",?length);??
- ????????TraverseDbLinkList(head);??
- ????}??
- ??
- ????printf("請輸入要插入節點的位置和元素值(兩個數用空格隔開):");??
- ????scanf("%d?%d",?&position,?&value);??
- ????flag?=?InsertEleDbLinkList(head,?position,?value);??
- ????if?(flag)??
- ????{??
- ????????printf("插入節點成功!\n");??
- ????????TraverseDbLinkList(head);??
- ????}?????
- ????else??
- ????????printf("插入節點失敗!\n");??
- ??
- ????flag?=?IsEmptyDbLinkList(head);??
- ????if?(flag)??
- ????????printf("雙向鏈表為空,不能進行刪除操作!\n");??
- ????else??
- ????{??
- ????????printf("請輸入要刪除節點的位置:");??
- ????????scanf("%d",?&position);??
- ????????flag?=?DeleteEleDbLinkList(head,?position);??
- ????????if?(flag)??
- ????????{??
- ????????????printf("刪除節點成功!\n");??
- ????????????TraverseDbLinkList(head);??
- ????????}?????
- ????????else??
- ????????????printf("刪除節點失敗!\n");??
- ????}??
- ??
- ????FreeMemory(&head);??
- ????if?(NULL?==?head)??
- ????????printf("已成功刪除雙向鏈表,釋放內存完成!\n");??
- ????else??
- ????????printf("刪除雙向鏈表失敗,釋放內存未完成!\n");??
- ??
- ????return?0;??
- }??
PS:相信對很多人來說鏈表的相關知識其實不難,很快能把這個程序編出來。但是還是有很多細節的問題要自己編過才知道的,我自己在學習的過程中就遇到過,所以我不讓大家再走彎路。