與數組類似,鏈表也是一種線性數據結構。這裏有一個例子:
正如你所看到的,鏈表中的每一個元素其實是一個單獨的對象,而全部對象都經過每一個元素中的引用字段連接在一塊兒。node
鏈表有兩種類型:單鏈表和雙鏈表。上面給出的例子是一個單鏈表,這裏有一個雙鏈表的例子:
單鏈表中的每一個結點不只包含值,還包含連接到下一個結點的引用字段。經過這種方式,單鏈表將全部結點按順序組織起來。ios
下面是一個單鏈表的例子:
藍色箭頭顯示單個連接列表中的結點是如何組合在一塊兒的。數組
如下是單鏈表中結點的典型定義:數據結構
template<typename T> class Node { public: T e; Node<T>*next; Node():e(0),next(nullptr){} Node(T& E):e(E),next(nullptr){} Node(T& E,Node<T>*Next):e(E),next(Next){} };
在大多數狀況下,咱們將使用頭結點(第一個結點)來表示整個列表。3d
與數組不一樣,咱們沒法在常量時間內訪問單鏈表中的隨機元素。 若是咱們想要得到第 i 個元素,咱們必須從頭結點逐個遍歷。 咱們按索引來訪問元素平均要花費 O(N) 時間,其中 N 是鏈表的長度。指針
例如,在上面的示例中,頭結點是 23。訪問第 3 個結點的惟一方法是使用頭結點中的「next」字段到達第 2 個結點(結點 6); 而後使用結點 6 的「next」字段,咱們可以訪問第 3 個結點。code
若是咱們想在給定的結點 prev 以後添加新值,咱們應該:對象
與數組不一樣,咱們不須要將全部元素移動到插入元素以後。所以,您能夠在 O(1) 時間複雜度中將新結點插入到鏈表中,這很是高效。blog
讓咱們在第二個結點 6 以後插入一個新的值 9。圖片
咱們將首先初始化一個值爲 9 的新結點。而後將結點 9 連接到結點 15。最後,將結點 6 連接到結點 9。
插入以後,咱們的鏈表將以下所示:
template<typename T> void LinkedList<T>::add(int index, T e) { if(index >= 0 && index <= size) { Node<T>*prev = dummyHead; for(int i = 0;i<index;++i){ prev = prev->next; //遍歷到node爲要插入節點的前一節點 } //第一種寫法 // Node<T>*newNode = Node<T>(e); //建立新節點傳入直e // newNode->next = node->next; //新節點的next指向要插入節點 // node->next = newNode; //要插入節點的前一節點的next指向新節點 //第二種寫法 prev->next = new Node<T>(e,prev->next); //建立一個節點傳入直和讓新節點的next指向插入節點,而後要插入節點的前一節點的next指向新節點 ++size; } }
衆所周知,咱們使用頭結點來表明整個列表。
所以,在列表開頭添加新節點時更新頭結點 head 相當重要。
例如,讓咱們在列表的開頭添加一個新結點 9。
咱們初始化一個新結點 9 並將其連接到當前頭結點 23。
指定結點 9 爲新的頭結點。
template<typename T> void LinkedList<T>::addFirst(T e) { //第一種寫法 // Node<T>*node = new Node<T>(e); //建立一個節點,把直放入節點 // node->next = head; //讓建立的節點的下next指向當前頭 // head = node; //頭指向新建立的節點 //第二種寫法 // head = new Node<T>(e,head); //新建立一個節點傳入數據和頭讓新節點的next指向head,而後head在指向新節點 // ++size; add(0,e); }
template<typename T> void LinkedList<T>::addLast(int e) { add(size,e); }
若是咱們想從單鏈表中刪除現有結點 cur,能夠分兩步完成:
在咱們的第一步中,咱們須要找出 prev 和 next。使用 cur 的參考字段很容易找出 next,可是,咱們必須從頭結點遍歷鏈表,以找出 prev,它的平均時間是 O(N),其中 N 是鏈表的長度。所以,刪除結點的時間複雜度將是 O(N)。
空間複雜度爲 O(1),由於咱們只須要常量空間來存儲指針。
讓咱們嘗試把結點 6從上面的單鏈表中刪除。
從頭遍歷鏈表,直到咱們找到前一個結點 prev,即結點 23
將 prev(結點 23)與 next(結點 15)連接
結點 6 如今不在咱們的單鏈表中。
template<typename T> T LinkedList<T>::remove(const int index) { if(index>=0 && index<=size) { Node<T>*prev = dummyHead; for(int i = 0;i<index;++i) //找到要刪除節點的前一個節點 { prev = prev->next; } Node<T>*retNode = prev->next; //保存要刪除的節點 prev->next = retNode->next; //讓前一節點next指向要刪除節點的後一節點 retNode->next = nullptr; //要刪除節點next指向空 --size; return retNode->e; } }
若是咱們想刪除第一個結點,策略會有所不一樣。
正如以前所提到的,咱們使用頭結點 head 來表示鏈表。咱們的頭是下面示例中的黑色結點 23。
若是想要刪除第一個結點,咱們能夠簡單地將下一個結點分配給head。也就是說,刪除以後咱們的頭將會是結點 6。
鏈表從頭結點開始,所以結點 23 再也不在咱們的鏈表中。
template<typename T> T LinkedList<T>::removeFirst() { return remove(0); }
template<typename T> T LinkedList<T>::removeLast() { return remove(size-1); }
#ifndef C___LINKEDLIST_H #define C___LINKEDLIST_H #include <iostream> template<typename T> class Node { public: T e; Node<T>*next; Node():e(0),next(nullptr){} Node(T& E):e(E),next(nullptr){} Node(T& E,Node<T>*Next):e(E),next(Next){} }; template<typename T> class LinkedList { public: LinkedList(); //返回連表大小 int getSize()const; //判斷是否爲空 bool isEmpty()const; //頭插入 void addFirst(T e); //爲插入 void addLast(T e); //插入 void add(int index, T e); //練習:獲取鏈表第👈index個位置的元素 T get(const int index); //獲取鏈表第一個元素 T getFirst(); //獲取鏈表最後一個元素 T getLast(); //練習:修改鏈表第👈index個位置的元素 void set(const int index,const T&e); //查找鏈表是否有元素e bool contains(const T&e)const; //刪除元素 T remove(const int index); //刪除頭 T removeFirst(); //刪除尾 T removeLast(); //打印鏈表 void print()const; private: Node<T>*dummyHead; //虛擬頭節點,不存數據 int size; //記錄大小 }; template<typename T> int LinkedList<T>::getSize() const { return size; } template<typename T> bool LinkedList<T>::isEmpty() const { return size == 0; } template<typename T> void LinkedList<T>::addFirst(T e) { //第一種寫法 // Node<T>*node = new Node<T>(e); //建立一個節點,把直放入節點 // node->next = head; //讓建立的節點的下next指向當前頭 // head = node; //頭指向新建立的節點 //第二種寫法 // head = new Node<T>(e,head); //新建立一個節點傳入數據和頭讓新節點的next指向head,而後head在指向新節點 // ++size; add(0,e); } template<typename T> void LinkedList<T>::add(int index, T e) { if(index >= 0 && index <= size) { Node<T>*prev = dummyHead; for(int i = 0;i<index;++i){ prev = prev->next; //遍歷到node爲要插入節點的前一節點 } //第一種寫法 // Node<T>*newNode = Node<T>(e); //建立新節點傳入直e // newNode->next = node->next; //新節點的next指向要插入節點 // node->next = newNode; //要插入節點的前一節點的next指向新節點 //第二種寫法 prev->next = new Node<T>(e,prev->next); //建立一個節點傳入直和讓新節點的next指向插入節點,而後要插入節點的前一節點的next指向新節點 ++size; } } template<typename T> void LinkedList<T>::addLast(T e) { add(size,e); } template<typename T> LinkedList<T>::LinkedList() { dummyHead = new Node<T>(); size = 0; } template<typename T> T LinkedList<T>::get(const int index) { if(index>=0 && index<=size) { Node<T>*cur = dummyHead->next; //把第一個元素的位置給cur for(int i = 0;i<index;++i) { cur = cur->next; } return cur->e;//返回👈第index個節點的元素 } } template<typename T> T LinkedList<T>::getFirst() { return get(0); } template<typename T> T LinkedList<T>::getLast() { return get(size-1); } template<typename T> void LinkedList<T>::set(const int index, const T &e) { if(index>=0 && index<=size) { Node<T> *cur = dummyHead->next; for (int i = 0; i < index; ++i) { cur = cur->next; } cur->e = e; } } template<typename T> bool LinkedList<T>::contains(const T &e) const { //第一種遍歷 // Node<T>*cur = dummyHead->next; // while(cur!= nullptr) // { // if(cur->e == e) // { // return true; // } // cur = cur->next; // } // return false; //第二種遍歷 for(Node<T>*cur = dummyHead->next;cur!= nullptr;cur = cur->next) { if(cur->e == e) //若是找到元素返回true { return true; } } return false; //不然返回false } template<typename T> void LinkedList<T>::print() const { std::cout << "LinkedList: size = " << size << std::endl; std::cout << "["; for(Node<T>*cur = dummyHead->next;cur!= nullptr;cur = cur->next) { std::cout<<cur->e<<"->"; } std::cout<<"NULL"<<"]"<<std::endl; } template<typename T> T LinkedList<T>::remove(const int index) { if(index>=0 && index<=size) { Node<T>*prev = dummyHead; for(int i = 0;i<index;++i) //找到要刪除節點的前一個節點 { prev = prev->next; } Node<T>*retNode = prev->next; //保存要刪除的節點 prev->next = retNode->next; //讓前一節點next指向要刪除節點的後一節點 retNode->next = nullptr; //要刪除節點next指向空 --size; return retNode->e; } } template<typename T> T LinkedList<T>::removeLast() { return remove(size-1); } template<typename T> T LinkedList<T>::removeFirst() { return remove(0); } #endif
int main() { LinkedList<int> *ll; ll = new LinkedList<int>(); for(int i = 0;i<10;++i) { ll->addFirst(i); ll->print(); } ll->add(2,666); ll->print(); cout<<endl; cout<<"get(2)"<<ll->get(2)<<endl; cout<<"getSize()"<<ll->getSize()<<endl; cout<<"getFirst()"<<ll->getFirst()<<endl; cout<<"getLast()"<<ll->getLast()<<endl; cout<<"isEmpty"<<ll->isEmpty()<<endl; cout<<"contains"<<ll->contains(666)<<endl; ll->set(3,999); ll->addLast(000); ll->print(); cout<<endl; ll->removeLast(); ll->removeFirst(); ll->remove(1); ll->print(); return 0; }