鏈表的數據結構
struct ListNode
{
int value;
ListNode* next;
};
那么在鏈表的末尾添加一個節點的代碼如下:
void insert(ListNode** pHead, int value)
{
ListNode* pNew = new ListNode;
pNew->value = value;
pNew->next = NULL;
if (*pHead == NULL)
{
*pHead = pNew;
}
else
{
ListNode* temp = *pHead;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = pNew;
}
}
在上面的代碼中,我們要特別注意函數的第一個參數是pHead是一個指向指針的指針。當我們往一個空鏈表中插入一個節點時,新插入的節點就是鏈表的頭指針。由於此時會改動頭指針,因此必須把pHead參數設為指向指針的指針,否則出了這個函數pHead仍然是一個空指針。
這這里指針的指針可以這樣理解——pHead是一個指向指針的指針,*pHead是一個指針,鏈表的結點也是指針類型,那么pHead就可以指向多個指針,這樣就可以把鏈表的所有結點連接起來形成一條鏈。
由於鏈表中的內存不是一次性分配的,因而我們無法保證鏈表的內存和數組一樣時連續的。因此,如果想在鏈表中找到它的第i個節點,那么我們只能從頭節點開始,沿着指向下一個節點的指針遍歷鏈表,它的時間效率為O(n)。而在數組中,我們可以根據下標在O(1)時間內找到第i個元素。下面是在鏈表中找到第一個含有某值的節點並刪除該節點的代碼:
void RemoveNode(ListNode** pHead, int value)
{
if (pHead == NULL && (*pHead) == NULL)//如果鏈表為空
{
return;
}
ListNode* pdelete = NULL;
if ((*pHead)->vaule == value)
{
pdelete = *pHead;
*pHead = (*pHead)->next;
}
else
{
ListNode* pNode = *pHead;
while (pNode->next != NULL && pNode->next->value != value)
{
pNode = pNode->next;
}
if (pNode->next != NULL && pNode->next->value == value)
{
pdelete = pNode->next;
pNode->next = Pnode->next->next;
}
}
if (pdelete != NULL)
{
delete pdelete;
pdelete = NULL;
}
}
從尾到頭打印鏈表
我們可以用棧實現這種順序。每經過一個節點的時候,把該節點放到一個棧中。當遍歷完整個鏈表后,再從棧頂開始逐個輸出節點的值,此時輸出的結點的順序已經反轉過來了。這種思路的實現代碼如下:
void printf_RList(ListNode *pHead)
{
stack<ListNode*>nodes;
ListNode* pNode = pHead;
while (pNode != NULL)
{
nodes.push(pNode);
pNode = pNode->next;
}
while (!nodes.empty())
{
pNode = node.top();
cout << pNode << " ";
nodes.pop();
}
}
既然想到了用棧來解決這個函數,而遞歸在本質上就是一個棧結構,於是很自然地又想到了用遞歸來實現。要實現反過來的鏈表,我們每訪問到一個節點的時候,先遞歸輸出它后面的節點,再輸出該節點自身,這樣鏈表的輸出結果就反過來了。
基於這樣的思路不難寫出如下代碼:
void printf_RList(ListNode* pHead) { if (pHead != NULL) { if (pHead->next != NULL) { printf_RList(pHead->next); } cout << pHead->value<<" "; } }
