雙鏈表與單鏈表的區別就在於:單鏈表中一個節點只包含指向下一個節點的指針;但是雙鏈表中,一個節點既包含指向下一個節點的指針,又包含指向上一個節點的指針。
雙鏈表的具體實現與文件結構都與單鏈表相似,只是在插入、刪除元素的實現上有細微的差別。
雙鏈表的結構形式:
需要注意的是,在雙鏈表中,curr仍然是指當前位置的前一個位置。
在單鏈表中,表頭head不包含具體的元素,但是tail是包含具體元素值的;在雙鏈表中,head和tail都不包含具體元素。
文件結構:
其中,Public.h、Tools.h、Tools.cpp爲提供輔助功能的文件:Public.h包含常用的頭文件,Tools.h和Tools.cpp實現了異常機制Assert();
List.h中實現線性表的抽象類模板,不包含數據成員,成員函數都是虛函數(當然構造函數除外);
Doubly_Linked_List.h和Doubly_Linked_List_Def.h實現單鏈表的模板類定義,其繼承自List.h中的線性表模板;
main.cpp爲主函數,實現單鏈表的調用和測試。
List.h的內容不再展示。
Doubly_Linked_List.h:將整張雙鏈表定義爲一個類(模板)
/********************************************************/
// 用模板實現單向鏈表(Doubly Linked List)的定義
// 繼承基類線性表template<class dataType> class List
/********************************************************/
#pragma once
#include "List.h"
// 1,定義節點類模板
template<class E>
class Doubly_Node
{
public:
E element; //本結點存儲的元素值
Doubly_Node* next;//指向下一結點的指針
Doubly_Node* prev;//指向上一結點的指針
Doubly_Node(const E& elemval, Doubly_Node* nextval = NULL, Doubly_Node* prevval = NULL) :
element(elemval), next(nextval), prev(prevval) {}
Doubly_Node(Doubly_Node* nextval = NULL, Doubly_Node* prevval = NULL) :
next(nextval), prev(prevval) {}
};
// 2,定義鏈表類模板
template<class E>
class Doubly_LList : public List<E>
{
private:
Doubly_Node<E>* head; //表頭,head結點內並不存儲真是的元素
Doubly_Node<E>* tail;
Doubly_Node<E>* curr; //實際上curr->next纔是真正的當前位置
int cnt; //鏈表中當前存儲的元素個數
void init(); //初始化
void remove_all(); //清空
public:
Doubly_LList();
~Doubly_LList();
void print() const;
void clear();
void insert(const E& it); //在當前位置插入元素
void append(const E& it); //在鏈表末尾追加
E remove(); //刪除當前的元素並返回其值
void moveToStart(); //將當前位置設爲鏈表開始
void moveToEnd(); //將當前位置設爲鏈表結束的前一個位置
void prev(); //將當前位置左移一位
void next(); //將當前位置右移一位
int length() const; //返回當前鏈表存儲的元素個數
int currPos() const; //返回當前位置
void moveToPos(int pos); //將當前位置設爲指定位置
const E& getValue() const;//返回當前位置的元素值
};
Doubly_Linked_List_Def.h:實現Doubly_Linked_List.h中的成員函數定義
/********************************************************/
// 用模板實現單向鏈表(Doubly Linked List)的定義
// 繼承基類線性表 template<class dataType> class List
// 本頭文件實現 Doubly_LList<class E>的成員函數
/********************************************************/
#pragma once
#include "Doubly_Linked_List.h"
template<class E>
void Doubly_LList<E>::init()
{
//所有指針指向新開的空間
head = curr = new Doubly_Node<E>;
tail = new Doubly_Node<E>;
head->next = tail;
tail->prev = head;
cnt = 0;
}
//刪除所有結點
template<class E>
void Doubly_LList<E>::remove_all()
{
while (head != NULL)
{
curr = head;
//請注意,下面兩句的順序不能顛倒,不然curr刪除後就相當於head刪除了,就不能再獲取head->next了
head = head->next;
delete curr;
}
}
//構造函數
template<class E>
Doubly_LList<E>:: Doubly_LList()
{
//構造時只開闢首尾節點的空間
init();
}
//析構函數
template<class E>
Doubly_LList<E>::~ Doubly_LList()
{
remove_all();
}
//打印所有元素
template<class E>
void Doubly_LList<E>::print() const
{
Doubly_Node<E>* temp = head->next; //head中不存儲數據,head->next中才有數據
while (tail != temp)
{
//此處暗示類型E必須定義了“<<”操作,否則報錯
cout << temp->element << endl;
temp = temp->next;
}
}
//清空鏈表
template<class E>
void Doubly_LList<E>::clear()
{
remove_all();
init(); //刪除之後,還要留着表頭、表尾
}
//當前位置插入元素
template<class E>
void Doubly_LList<E>::insert(const E& it)
{
//Doubly_Node<E> *temp = NULL;
//temp = new Doubly_Node<E>(it, curr->next, curr);
//curr->next->prev = temp; //下面兩句的順序有講究
//curr->next = temp;
// 下面一句等於上面4句
curr->next = curr->next->prev = new Doubly_Node<E>(it, curr->next, curr);
cnt++;
}
//鏈表末尾追加元素
template<class E>
void Doubly_LList<E>::append(const E& it)
{
//Doubly_Node<E> *temp = new Doubly_Node<E>(it, tail, tail->prev);
//tail->prev->next = temp;//下面兩句的順序有講究
//tail->prev = temp;
tail->prev = tail->prev->next = new Doubly_Node<E>(it, tail, tail->prev);
cnt++;
}
//刪除當前元素並返回其值
template<class E>
E Doubly_LList<E>::remove()
{
Assert(curr != tail, "當前位置無效,無法刪除");
Doubly_Node<E>* temp = curr->next;
if (temp == tail)
{
return NULL;
}
E it = temp->element;
curr->next = temp->next; //即便是temp爲tail的時候,這句依然成立
temp->next->prev = curr;
delete temp;
cnt--;
return it;
}
//將head設爲當前位置
template<class E>
void Doubly_LList<E>::moveToStart()
{
curr = head;
}
//將tail設爲當前位置
template<class E>
void Doubly_LList<E>::moveToEnd()
{
curr = tail->prev;
}
//將當前位置左移一位,如果已在最前,無改變
template<class E>
void Doubly_LList<E>::prev()
{
if (curr == head)
{
return;
}
curr = curr->prev;
}
//將當前位置右移一位,如果已在最後,無改變
template<class E>
void Doubly_LList<E>::next()
{
if (curr == tail)
{
return;
}
curr = curr->next;
}
//返回鏈表長度,即
template<class E>
int Doubly_LList<E>::length() const
{
return cnt;
}
//返回當前位置
template<class E>
int Doubly_LList<E>::currPos() const
{
Doubly_Node<E>* temp = head;
int pos = 0;
while (temp != curr)
{
pos++;
temp = temp->next;
}
return pos;
}
//將當前位置設爲指定位置
template<class E>
void Doubly_LList<E>::moveToPos(int pos)
{
Assert((pos >= 0) && (pos <= cnt), "設定位置越界");
curr = head;
for (int i = 0; i != pos; i++)
{
curr = curr->next;
}
}
//返回當前位置的元素值
template<class E>
const E& Doubly_LList<E>::getValue() const
{
Assert(curr != tail, "無值可取");
return curr->next->element;
}
main.c 主函數,測試構造的雙鏈表是否可用
/********************************************************/
// 主函數
// 用於測試編寫的各函數與數據結構
/********************************************************/
#include "Public.h"
#include "Tools.h"
#include "SeqStack.h"
#include "SeqStack_Vector.h"
#include "SeqStack_VT_Def.h"
#include "postfix.h"
#include "ArrayBasedList_Def.h"
#include "Single_Linked_List_Def.h"
#include "Doubly_Linked_List_Def.h"
int main()
{
/********************************************************/
// 5,《數據結構與算法分析》Clifford 4.1.5 雙鏈表
/********************************************************/
Doubly_LList<int> iDLList;
iDLList.insert(1);
iDLList.insert(2);
iDLList.insert(3);
iDLList.insert(4);
cout << "after insert:" << endl;
iDLList.print();
cout << "\n手動刪除第二個元素:" << endl;
iDLList.moveToPos(1);
iDLList.remove();
iDLList.print();
iDLList.clear();
cout << "\nafter clear:" << endl;
iDLList.print();
system("pause");
return 0;
}
運行結果: