1. 程式人生 > 其它 >python 釋放連結串列節點_一文搞懂雙向連結串列——附C++程式碼與分析

python 釋放連結串列節點_一文搞懂雙向連結串列——附C++程式碼與分析

技術標籤:python 釋放連結串列節點

361d47f5395f1e492eba5a1588078b0f.png

前面文章分析了單向連結串列,並給出了python和C++實現:

機器學習入坑者:乾貨!單鏈表從原理到實現——附python和C++兩個版本​zhuanlan.zhihu.com 144fc4db93dc9544d9451db03ecd920e.png

本文介紹的雙向連結串列是在單向連結串列基礎上的一個改進,每個節點指向其直接前驅和直接後繼節點。因此,從雙向連結串列的任意位置開始,都能訪問所有的節點

雙向連結串列的缺點

從節點的結構上可以看出,雙向連結串列的所需的儲存空間大於單向連結串列。同時,對於插入和刪除等操作來說,雙向連結串列的節點操作更加複雜,涉及到節點的前後兩個節點。

雙向連結串列的節點

對於雙向連結串列來說,它的每個節點要指向“直接前驅”和“直接後繼”,所以節點類需要含有兩個指標域。指向直接前驅的指標使用pre表示,指向後繼的指標使用next表示。

e368ef33a7388b9fcdebd9d49d93fba3.png

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;
		}
	}
}