Double linked list

Posted by Jason Wong on March 22, 2019

民國108年3月22日

說明:文章關於二元樹的算法根據 “Data Structures and Algorithms:Annotated Reference with Examples” 這本書的僞代碼通過C++實現而得,此外本文用於說明的插圖也出自此書。

1.雙向鏈表結構

typedef struct Node {
    int data;
    struct Node *prev;   //指向前一個node的指標    
    struct Node *next;  //指向後一個node的指標    
} linkedList;

節點node是鏈表的基本單元,一個節點包含三個要素:數據,前指標,後指標。圖示如下,圖出自”Data Structures and Algorithms: Annotated Reference with Examples”,第十五頁:

##二、雙向鏈表的操作 1.後插法 新的節點每次都添加到鏈表的尾端。

void append(linkedList **head, int value) {
    auto *newBlock = new(linkedList);
    newBlock->data = value;
    if (*head == nullptr) {
        std::cout << "double linked list is empty" << std::endl;
        *head = newBlock;
        newBlock->next = nullptr;
    } else {
        auto *p = *head;
        while (p->next != nullptr) {
            p = p->next;
        }
        p->next = newBlock;
        newBlock->prev = p;
        newBlock->next = nullptr;
    }
}

2.前插法 新節點每次都在鏈表頭添加

void insertFront(linkedList **head, int value) {
    auto newNode = new(linkedList);
    newNode->data = value;
    newNode->next = *head;
    newNode->prev = nullptr;

    if (*head != nullptr) {
        (*head)->prev = newNode;
    }
    //移動head,把newNode作爲新的head
    *head = newNode;
}

3.在特定節點前插入 此插入法需要一個輔助函式來幫助尋找要插入的位置,此處的輔助函式是getNode()完成

linkedList *getNode(linkedList *head, int value) {
    while (head != nullptr && head->data != value) {
        head = head->next;
    }
    //返回包含value值的節點的地址。
    return head;
}

void insertBefore(linkedList **head, linkedList *nextNode, int value) {
    if (nextNode == nullptr) {
        std::cout << "Given nextNode is null" << std::endl;
        return;
    }
    auto *newNode = new(linkedList);
    newNode->data = value;
    //newNode在nextNode之前插入,需要把nextNode的前節點地址賦值給newNode->prev,即讓newNode指向nextNode的前驅。
    newNode->prev = nextNode->prev;
    //newNode的下一個節點就是nextNode,,因爲newNode在nextNode前插入
    newNode->next = nextNode;
    //nextNode的前節點是newNode,因爲newNode在nextNode前插入
    nextNode->prev = newNode;
    if (newNode->prev != nullptr) {
    //newNode不是head節點,告訴newNode的前一個節點,它的下一個節點是newNode
        newNode->prev->next = newNode;
    } else {
    //若newNode前節點爲空,即表明newNode就是head節點,此時的插入操作相當於前插法,讓新插入的newNode變成head節點。
        *head = newNode;
    }
}

4.移除節點 作用:移除包含value的節點

bool remove(linkedList **head, int value) {
    if (*head == nullptr) {
        std::cout << "double linked list is empty" << std::endl;
        return false;
    }
    linkedList *n = (*head);
    if (value == n->data) {
        if (n->next == nullptr) {
            std::cout << "There are just one block in the linked list" << std::endl;
            delete *head;
        } else if (n->next != nullptr) {
            std::cout << "We are removing head node" << std::endl;
            *head = n->next;
            delete (*head)->prev;
        }
        return true;
    }
    while (n->next != nullptr && n->data != value) {
        n = n->next;
    }
    if (n->next == nullptr) {
        if (n->data != value) {
            std::cout << "The item doesn't in the linked list" << std::endl;
            return false;
        } else if (n->data == value) {
            std::cout << "Value to be removed is in the tail of linked list" << std::endl;
            delete n;
            n->prev->next = nullptr;
            return true;
        }
    } else if (n->next != nullptr) {
        std::cout << "The item to remove is somewhere in between head and tail" << std::endl;
        n->prev->next = n->next;
        n->next->prev = n->prev;
        delete n;
        return true;
    }
}

5.reverseTraverse 作用:從表尾循環到表頭(時間複雜度O(n^2)),比單向鏈表的反向遍歷簡單很多

linkedList *getLastNode(linkedList *head) {
    while (head != nullptr && head->next != nullptr) {
        head = head->next;
    }
    return head;
}

linkedList *reverseTraverse(linkedList *head) {
    if (head == nullptr) {
        std::cout << "The linked list is empty" << std::endl;
        return nullptr;
    } else {
        auto *tail = getLastNode(head);
        while (tail != nullptr && tail->prev != nullptr) {
            tail = tail->prev;
        }
        return tail;
    }
}

反向遍歷如下圖所示,圖出自”Data Structures and Algorithms: Annotated Reference with Examples”,第十七頁: