數組實現單鏈表


單鏈表常見的實現方法有兩種,一種方式是定義一個結構體表示鏈表節點。比如:

struct node{
      int val;
      node* next;
};

然后就是通過next指針將鏈表的所有節點連接起來。如果涉及到鏈表節點的插入和刪除操作,則只需要修改鏈表節點的指針即可。

這種方式有個明顯的缺點,就是不能隨機存取。如果要在某個節點之后插入或者刪除節點,復雜度是O(n),因為要從頭開始逐個遍歷到需要插入或者刪除的節點(通過next指針找)。所以用結構體實現的單鏈表缺點就是太慢了。當然還有一個原因就是C++里要new一個node比較慢。總之就是慢~

因此算法題中使用單鏈表往往是通過數組實現,數組實現的單鏈表可以通過下標來索引節點,可以直接通過下標找到某個節點的值和下一個節點的,因此數組實現的單鏈表的最大優點就是快(插入和刪除操作都是O(1)的時間復雜度),畢竟數組的特點就是隨機存取嘛。

這里結合一道題目來看數組實現單鏈表。
原題鏈接

這道題題意非常直白。就是需要實現一個單鏈表,這個鏈表支持三個操作:(1)在鏈表的表頭插入一個節點;(2)刪除某個數后面的數;(3)在某個數后面加入一個數(即插入節點)。
輸入有若干行,每行都有可能是三個操作的其中之一,最后的輸出就是經過所有操作之后從頭到尾將鏈表輸出。

來模擬一下樣例吧。

來看一下實現。
要實現三個操作,我們可以額外定義三個函數分別表示上面三個操作。
這里分別命名為insertBeforeHead(int x)Insert(int k, int x)Delete(int k)。這里的x就是要插入的值,k就是要插入/刪除節點的前一個位置(也就是在第k個節點之后進行插入/刪除)。

這里要注意,第k個節點並不是當前鏈表從前往后數,而是從最開始計算,插入的第k個節點(也就是說前面插入的節點被刪除了,並不會重新計算k)。

要用數組表示單鏈表,和結構體實現鏈表一樣,每個節點都要有值和記錄下一個節點的“指針”,因此我們可以開兩個數組val和nex分別表示節點的數值和下一個節點的位置。
由於需要對鏈表頭做操作(在鏈表頭插入節點)和記錄已經插入的節點的個數(因為要在第k個節點之后進行插入/刪除操作),因此我們需要用兩個“指針”記錄頭節點和當前操作的節點是第幾個節點。
簡單的說,head指向的就是鏈表頭節點(第一個節點,不是附加頭節點)的下標,idx表示當前用到的是第幾個節點。

const int N = 1e5 + 5;
int val[N], nex[N], head, idx;    //head表示頭節點的下標,idx表示已經插入的節點的個數

鏈表需要有初始化操作,不然headidx的值可能是不確定的。

void init() {
      head = -1;      //注意:這里的head並不是虛擬頭節點,指向的就是鏈表的第一個節點,這樣做主要是為了方便在頭節點插入數據,最開始沒有插入節點,head指向-1
      idx = 0;        //還沒插入節點,idx為0
}

然后看一下在鏈表頭節點(之前)插入節點。
由於head記錄了原來的頭節點,我們希望新插入一個節點,這個節點的值為x,且這個節點的**next指針**指向原來的head
看一下代碼:

void insertBeforeHead(int x) {
      val[idx] = x;      //新插入一個值為x的節點
      nex[idx] = head;   //x的下一個節點是原來的頭節點,表示在頭節點之前插入x
      head = idx;        //head指針指向新節點
      ++idx;             //更新插入節點的個數
}

看一下如何在第k個節點之后插入一個節點。
首先肯定要創建一個值為x的節點:val[idx] = x;
且這個新節點的next指針指向第k+1個節點:nex[idx] = nex[k]. (nex[k]是原來的第k+1個節點,讓新插入的節點指向這個節點)。
然后k的next指針也要更新,指向x: nex[k] = idx;
再更新一下已經插入的節點的個數:++idx;
這樣就得到在第k個節點之后插入節點x的代碼:

void Insert(int k, int x) {
      val[idx] = x;
      nex[idx] = nex[k];
      nex[k] = idx;
      ++idx;
}

然后就剩下在第k個節點之后刪除節點的函數啦:
這個很簡單,要讓第k個節點之后的值不存在,就直接讓第k個節點的next指針指向第k+2個節點,也就是跳過了第k+1個節點,這樣第k+1個節點就社會性死亡了。

void Delete(int k) {
      nex[k] = nex[nex[k]];
}

這道題的完整代碼如下:

#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 5;
int val[N], nex[N], head, idx;

void init() {
    head = -1;
    idx = 0;
}

void insertBeforeHead(int x) {
    val[idx] = x;
    nex[idx] = head;
    head = idx;
    ++idx;
}

//在下標為k的節點后插入x
void Insert(int k, int x) {
    val[idx] = x;
    nex[idx] = nex[k];
    nex[k] = idx;
    ++idx;
}

//刪除下標為k的節點的下一個節點
void Delete(int k) {
    nex[k] = nex[nex[k]];
}

int main() {
    int M;
    cin >> M;            //操作的個數
    init();              //操作之前需要對head和idx指針進行初始化
    while(M--) {
        char op;         //當前進行的操作
        int k, x;
        cin >> op;
        if(op == 'H') {     //在頭節點之前插入一個節點
            cin >> x;
            insertBeforeHead(x);
        } else if(op == 'I') {    //在第k個節點之后插入一個節點
            cin >> k >> x;
            Insert(k - 1, x);            //由於idx初始為0,所以第k個節點的下標為k - 1
        } else if(op == 'D') {
            cin >> k;
            if(k == 0) {                  
                head = nex[head];
            } else {
                Delete(k - 1);            //由於idx初始為0,所以第k個節點的下標為k - 1
            }
        }
    }
    for(int i = head; i != -1; i = nex[i]) {
        cout << val[i] << ' ';
    }
    cout << endl;
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM