LinkedList

與數組類似,鏈表也是一種線性數據結構。這裏有一個例子:
在這裏插入圖片描述
  正如你所看到的,鏈表中的每一個元素其實是一個單獨的對象,而全部對象都經過每一個元素中的引用字段連接在一塊兒。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 以後添加新值,咱們應該:對象

  1. 使用給定值初始化新結點 cur
    在這裏插入圖片描述
  2. 將 cur 的「next」字段連接到 prev 的下一個結點 next
    在這裏插入圖片描述
  3. 將 prev 中的「next」字段連接到 cur
    在這裏插入圖片描述

  與數組不一樣,咱們不須要將全部元素移動到插入元素以後。所以,您能夠在 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 相當重要。

  1. 初始化一個新結點 cur
  2. 將新結點連接到咱們的原始頭結點 head
  3. cur 指定爲 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,能夠分兩步完成:

  1. 找到 cur 的上一個結點 prev 及其下一個結點 next
    在這裏插入圖片描述
  2. 接下來連接 prevcur 的下一個節點 next
    在這裏插入圖片描述

  在咱們的第一步中,咱們須要找出 prevnext。使用 cur 的參考字段很容易找出 next,可是,咱們必須從頭結點遍歷鏈表,以找出 prev,它的平均時間是 O(N),其中 N 是鏈表的長度。所以,刪除結點的時間複雜度將是 O(N)

  空間複雜度爲 O(1),由於咱們只須要常量空間來存儲指針。

示例在這裏插入圖片描述

讓咱們嘗試把結點 6從上面的單鏈表中刪除。

  1. 從頭遍歷鏈表,直到咱們找到前一個結點 prev,即結點 23

  2. 將 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);
}

時間複雜度

在這裏插入圖片描述

代碼清單

LinkedList.h

#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

main.cpp

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;
}
相關文章
相關標籤/搜索