雙向鏈表也叫雙鏈表,是鏈表的一種,它的每一個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。因此,從雙向鏈表中的任意一個結點開始,均可以很方便地訪問它的前驅結點和後繼結點。ios
通常咱們都構造雙向循環鏈表,由於雙向鏈表解決了單向鏈表的不足和問題,而單鏈表由於出現的問題多,因此在面試中常常會考到單鏈表的問題。面試
在這裏,我用 .cpp對雙向鏈表的基本操做進行實現。ide
具體代碼以下:函數
List.h文件:
測試
#pragma once typedef int DataType; struct ListNode //所有用做公有的就定義爲struct,用做私有就定義class { //固然用class,寫在public中也能夠 DataType _data; ListNode* _next; //前驅指針 ListNode* _prev; //後繼指針 ListNode(DataType x) //構造函數 :_data(x) ,_next(NULL) ,_prev(NULL) {} }; class List { public: List() :_head(NULL) ,_tail(NULL) {} ~List() { Clear(); } public: void PushBack(DataType x) { if(_head == NULL) { _head = _tail = new ListNode(x);//調用構造函數 } else { ListNode* tmp = new ListNode(x); _tail->_next = tmp; tmp->_prev = _tail; _tail = tmp; } } void PopBack() { //沒有節點 //一個節點 //一個以上節點 if(_head == NULL) { return; } else if(_head == _tail) { delete _head; _head = _tail = NULL; } else { ListNode* cur = _tail->_prev; delete _tail; _tail = cur; cur->_next = NULL; } } void PushFront(DataType x) { if(_head == NULL) { _head = _tail = new ListNode(x); } else { ListNode* tmp = new ListNode(x); tmp->_next = _head; _head = tmp; } } void PopFront() { if(_head == NULL)//空 { return; } else if(_head == _tail)//一個節點 { delete _head; _head = _tail = NULL; } else //一個以上節點 { ListNode* del = _head; _head = _head->_next; _head->_prev = NULL; delete del; } } void Insert(ListNode* pos,DataType x) { assert(pos); if(pos == _tail) { PushBack(x); } else { /* ListNode* tmp = new ListNode(x); tmp->_next = pos->_next; pos->_next->_prev = tmp; //使用這種兩個指針的方式雖然能夠達到目的,可是容易出現問題 pos->_next = tmp; //首先得考慮pos是否爲空,其次若pos->_next爲空,再想找到它的prev就會出錯 tmp->_prev = pos; */ ListNode* tmp = new ListNode(x);//這樣定義兩個指針保存先後的節點,就不容易出錯 ListNode* next = pos->_next; tmp->_next = next; next->_prev = tmp; pos->_next = tmp; tmp->_prev = pos; } } ListNode* Find(DataType x) { if(_head == NULL) { return NULL;//找不到返回空 } else { ListNode* cur = _head; while(cur) { if(cur->_data == x) { return cur; } cur = cur->_next; } } } void Erase(ListNode* pos) { assert(_head); assert(pos); if(pos == _head)//刪除頭節點 { PopFront(); /* ListNode* del = _head; _head = _head->_next; _head->_prev = NULL; delete del; */ } else if(pos == _tail)//刪除尾節點 { PopBack(); } else // 刪除非頭尾節點 { /* ListNode* cur = pos->_prev; cur->_next = pos->_next; pos->_next->_prev = cur; delete pos; */ ListNode* prev = pos->_prev; ListNode* next = pos->_next; prev->_next = next; next->_prev = prev; delete pos; } } void PrintList()//在此用不上前驅節點prev { ListNode* cur = _head; while(cur) { cout<<cur->_data<<"->"; cur = cur->_next; } cout<<"NULL"<<endl; } void Clear() { ListNode* cur = _head; while(cur) { ListNode* del = cur; cur = cur->_next; delete del; } } //翻轉雙向鏈表 void reverse() { /* ①.交換值 ListNode* begin = _head; ListNode* end = _tail; while(begin != end && begin->_prev != end)//當兩個指針相遇或者已經偏離中止 { swap(begin->_data , end->_data); begin = begin->_next; end = end ->_prev; } */ //②.交換指針 ListNode* cur = _head; while(cur) { swap(cur->_prev , cur->_next);//把每一個節點的先後指針交換 cur = cur->_prev; } swap(_head,_tail); } private: ListNode* _head; ListNode* _tail; }; void Test() { List l; l.PushBack(1); l.PushBack(2); l.PushBack(3); l.PushBack(4); l.PushBack(5); l.PrintList(); l.PopBack(); l.PrintList(); l.PushFront(0); l.PrintList(); l.PopFront(); l.PrintList(); l.Insert(l.Find(1),0); l.PrintList(); l.Erase(l.Find(1)); l.PrintList(); l.reverse(); l.PrintList(); }
List.cpp 文件:(測試)spa
#include<iostream> #include<assert.h> using namespace std; #include "DoubleSList.h" //雙向鏈表 int main() { Test(); return 0; }
咱們發現,當遇到問題時不能直接上手去寫,爲何?由於咱們寫的是代碼,而不是bug,正確的程序是須要嚴謹的邏輯爲前提,因此在寫代碼以前須要謹慎的思考各類臨界的狀況和異常,這是很是之重要的!
指針