python 釋放連結串列節點_一文搞懂雙向連結串列——附C++程式碼與分析
技術標籤:python 釋放連結串列節點
前面文章分析了單向連結串列,並給出了python和C++實現:
機器學習入坑者:乾貨!單鏈表從原理到實現——附python和C++兩個版本zhuanlan.zhihu.com本文介紹的雙向連結串列是在單向連結串列基礎上的一個改進,每個節點指向其直接前驅和直接後繼節點。因此,從雙向連結串列的任意位置開始,都能訪問所有的節點。
雙向連結串列的缺點
從節點的結構上可以看出,雙向連結串列的所需的儲存空間大於單向連結串列。同時,對於插入和刪除等操作來說,雙向連結串列的節點操作更加複雜,涉及到節點的前後兩個節點。
雙向連結串列的節點
對於雙向連結串列來說,它的每個節點要指向“直接前驅”和“直接後繼”,所以節點類需要含有兩個指標域。指向直接前驅的指標使用pre表示,指向後繼的指標使用next表示。
C++實現分析
(1)節點類:
雙向連結串列的節點含有兩個指標域,即直接前驅pre和直接後繼next。節點類採用的是模板實現,這樣其所儲存的資料就不再依賴於特定型別。
template<class T> class Node { public: Node() {} Node *pre; Node *next; // 由於data屬性是私有的 // 所以採用get和set對data進行處理 void setData(T data) { this->data = data; } T getData() { return this->data; } private: T data; };
(2)連結串列類分析
連結串列類應該包含基本的增、改、刪、查等操作,由於其各種功能的實現是很相似的,所以下面給出了需要實現的典型函式:
- 建構函式:
- isEmpty()判斷是否為空;
- size()返回連結串列長度;
- insert()頭插、尾插、中間插入節點;
- delete()刪除節點;
- getNode()獲取節點;
- traversal()遍歷連結串列;
連結串列類的定義如下:
template<class P> class DoubleLinkedList { public: DoubleLinkedList(); bool isEmpty(); Node<P> *getNode(int index); int size(); void insert(int data, int index); void traversal(); void remove(int index); private: Node<P> *head; };
(3)連結串列類建構函式
初始化時需要建立頭節點,作為頭指標:
template<class P>
DoubleLinkedList<P>::DoubleLinkedList() {
// 建立頭結點
head = new Node<P>();
head->pre = NULL;
head->next = NULL;
head->setData(666);
}
(4)isEmpty()判斷是否為空
對於雙向連結串列來說,判斷是否為空只需要判斷頭指標是否指向其他Node節點:
template<class P>
bool DoubleLinkedList<P>::isEmpty() {
if (head->next == NULL) {
return true;
}
else
{
return false;
}
}
(5)size()獲取連結串列長度
獲取連結串列長度時需要判斷連結串列是否為空,從而確定是否採用遍歷的方式計算連結串列的長度。由於採用的不是迴圈連結串列,所以迴圈的結束條件是判斷是否指向空節點:
template<class P>
int DoubleLinkedList<P>::size() {
if (isEmpty()) {
return 0;
}
else {
int count = 0;
Node<P> *current = head->next;
// 迴圈結束條件
while (current!=NULL)
{
current = current->next;
count++;
}
return count;
}
}
(6)getNode()獲取節點
在插入和刪除等操作中,需要頻繁的進行節點獲取操作。所以應該封裝為單獨的函式用於節點獲取,如下:
template<class P>
Node<P> *DoubleLinkedList<P>::getNode(int index) {
Node<P> *current = head;
int currentCount = 0;
// 迴圈結束條件
while (currentCount<=index)
{
current = current->next;
currentCount++;
}
return current;
}
(7)insert()插入節點
插入節點依舊包含頭插法,尾插法和任意位置的插入。插入操作與單向連結串列的最大區別在於節點的指標移動較為複雜,需要將插入位置前後兩個節點與新節點均建立聯絡:
template<class P>
void DoubleLinkedList<P>::insert(int data, int index) {
Node<P> *node = new Node<P>();
node->setData(data);
// 1、列表為空時
if (isEmpty()) {
head->next = node;
node->pre = head;
return;
}
// 2、頭插法
if (index == 0) {
node->next = head->next;
head->next->pre = node;
node->pre = head;
head->next = node;
}
// 3、尾插法
else if (index >= this->size() - 1) {
// printf("index %d, size %d n", index, this->size());
Node<P> *temp = this->getNode(this->size()-1);
temp->next = node;
node->pre = temp;
}
// 4、任意位置插入
else
{
Node<P> *pre = this->getNode(index);
Node<P> *next = pre->next;
node->next = pre->next;
node->pre = pre;
pre->next = node;
node->next->pre = node;
}
}
8、remove()刪除節點
前面已經定義了用於獲取節點的getNode()函式,所以remove()函式只需要進行指標移動操作。將所要刪除的節點的直接前驅節點和直接後繼節點相連:
template<class P>
void DoubleLinkedList<P>::remove(int index) {
// 保證索引有意義
if ((index < (this->size()-1)) && (index>0)) {
Node<P> *node = this->getNode(index);
Node<P> *pre = node->pre;
Node<P> *next = node->next;
pre->next = next;
next->pre = pre;
}
}
(9)traversal()遍歷連結串列函式
雖然可以從雙向連結串列的任一個節點開始遍歷整個連結串列,但是下面的實現依舊是從頭結點開始的,迴圈的結束依舊是指向空指標:
template<class P>
void DoubleLinkedList<P>::traversal() {
if (!isEmpty()) {
Node<P> *current = head;
while (current)
{
cout << current->getData() << endl;
current = current->next;
}
}
}