單雙連結串列

小码king發表於2024-09-07

AcWing 826. 單連結串列

模板題:

實現一個單連結串列,連結串列初始為空,支援三種操作:

  1. 向連結串列頭插入一個數;
  2. 刪除第 k 個插入的數後面的一個數;
  3. 在第 k 個插入的數後插入一個數。

現在要對該連結串列進行 M 次操作,進行完所有操作後,從頭到尾輸出整個連結串列。

注意:題目中第 k 個插入的數並不是指當前連結串列的第 k 個數。例如操作過程中一共插入了 n 個數,則按照插入的時間順序,這 n 個數依次為:第 1 個插入的數,第 2 個插入的數,…第 n 個插入的數。

輸入格式

第一行包含整數 M,表示操作次數。

接下來 M 行,每行包含一個操作命令,操作命令可能為以下幾種:

  1. H x,表示向連結串列頭插入一個數 x。
  2. D k,表示刪除第 k 個插入的數後面的數(當 k 為 0 時,表示刪除頭結點)。
  3. I k x,表示在第 k 個插入的數後面插入一個數 x(此操作中 k 均大於 0)。

輸出格式

共一行,將整個連結串列從頭到尾輸出。

資料範圍

1≤M≤1000001≤M≤100000
所有操作保證合法。

輸入樣例:

10
H 9
I 1 1
D 1
D 0
H 6
I 3 6
I 4 5
I 4 5
I 3 4
D 6

輸出樣例:

6 4 6 5

AC程式碼:

#include<iostream>
using namespace std;
const int N=1000010;
int head,e[N],ne[N],idx;
//初始化:
void inim(){
    head=-1;
    idx=0;
}
//頭插
void pos(int x){
    e[idx]=x;
    ne[idx]=head;
    head=idx++;
}
//刪除第k個插入後的一個數;
void add(int x){
    ne[x]=ne[ne[x]];
}
//在第k個插入的數後面插入一個數;
void edd(int k,int x){
    e[idx]=x;
    ne[idx]=ne[k];
    ne[k]=idx++;
}
int main(){
    inim();//!一定要初始化;
    int n;
    cin >> n;
    while(n--){
        char s;
        cin >> s;
        if(s=='H'){
            int x;
            cin >> x;
            pos(x);
        }else if(s=='D'){
            int x;
            cin >> x;
            if(x==0) head=ne[head];
            else
            add(x-1);
        }else{
            int k,x;
            cin >> k >> x;
            edd(k-1,x);
        }
    }
    for(int i=head;i!=-1;i=ne[i]) cout << e[i] << " ";
    return 0;
}

AC程式碼問題解析:

  • 什麼是head,head指向的又是什麼?

    • 個人理解:head是一個標誌指標 (工具人),它剛開始指向的是-1,也就表示此時的連結串列沒有任何東西,連結串列為空;(這裡也就證實瞭如果head在後續的操作中head=-1,就是空連結串列);
  • 為什麼要 x-1, k-1?

  • 第一步:head=-1 (初始化) ;

  • 第二步:H x,表示向連結串列頭插入一個數 x;

    • e[idx]=x;//idx=0(初始化);e[0]=9;
      ne[idx]=head;//ne[0]=-1;//這裡(新手不太理解什麼意思):連結串列形式:9->-1
      head=idx++;//head=0,idx++=1;
      
  • 第三步:I k x,表示在第 k 個插入的數後面插入一個數 x(此操作中 k 均大於 )

    • e[idx]=x;//idx++=1;e[1]=1;
      ne[idx]=ne[k];
      ne[k]=idx++;//在這裡讓ne[k]=1:以連結串列形式展示:9->1->-1
      
      • ne[idx]=ne[k]; 這裡不就解釋了:因為你idx初始值就是下標為0,根據下標索引把第一個插入的值給到下標為0的陣列,所以在根據陣列的定義,我們要找的是k-1的下標陣列索引;(也可以把初始值idx改為1;這樣就可以跟下標k統一了);
  • 在給解釋一下第四步:D k,表示刪除第 k 個插入的數後面的數(當 k 為 0 時,表示刪除頭結點)

  • 這裡剛好對應了兩個操作一起終結了

  • D 1

  • D 0

  • 老樣子,上圖解:

    • ne[x]=ne[ne[x]];//這裡就可以看到ne[x]就是模擬第幾次插入,ne[0]=-1;就相當與第一次插入的e[0]被移除,變成-1;
      
    • // D 0 操作(當 k 為 0 時,表示刪除頭結點)
      if(x==0) head=ne[head];//這裡就執行了特判:head本來就指向是頭節點,e[0]被移除,就剩下e[1]一個頭節點,移除的話不就是成為空連結串列,直接指向-1就好了;(head)指向的永遠都是含有值得頭節點,沒有值才指向-1;
      
    • 這裡就是又開始重新插入刪除了,跟上述操作一樣;
  • 最終結果:

//根據head開始透過ne[i]找到:6->4->6->5;
for(int i=head;i!=-1;i=ne[i]) cout << e[i] << " ";

圖解來自:https://www.acwing.com/user/myspace/index/55289/
單連結串列完結;


AcWing 827. 雙連結串列

模板題:

實現一個雙連結串列,雙連結串列初始為空,支援 5 種操作:

  1. 在最左側插入一個數;
  2. 在最右側插入一個數;
  3. 將第 k 個插入的數刪除;
  4. 在第 k 個插入的數左側插入一個數;
  5. 在第 k 個插入的數右側插入一個數

現在要對該連結串列進行 M 次操作,進行完所有操作後,從左到右輸出整個連結串列。

注意:題目中第 k 個插入的數並不是指當前連結串列的第 k 個數。例如操作過程中一共插入了 n 個數,則按照插入的時間順序,這 n 個數依次為:第 1 個插入的數,第 2 個插入的數,…第 n 個插入的數。

輸入格式

第一行包含整數 M,表示操作次數。

接下來 M 行,每行包含一個操作命令,操作命令可能為以下幾種:

  1. L x,表示在連結串列的最左端插入數 x。
  2. R x,表示在連結串列的最右端插入數 x。
  3. D k,表示將第 k 個插入的數刪除。
  4. IL k x,表示在第 k 個插入的數左側插入一個數。
  5. IR k x,表示在第 k 個插入的數右側插入一個數。

輸出格式

共一行,將整個連結串列從左到右輸出。

資料範圍

1≤M≤1000001≤M≤100000
所有操作保證合法。

輸入樣例:

10
R 7
D 1
L 3
IL 2 10
D 3
IL 2 7
L 8
R 9
IL 4 7
IR 2 2

輸出樣例:

8 7 7 3 2 9

AC程式碼:

#include<iostream>
using namespace std;
const int N=100015;
int e[N],r[N],l[N],idx;
int n;
//初始化
void init(){
    l[1]=0;//頭節點;
    r[0]=1;//尾節點;
    idx=2;
}
//插入操作(具體看圖解)
void add(int k,int x){
    e[idx]=x;
    r[idx]=r[k];
    l[idx]=k;
    l[r[k]]=idx;
    r[k]=idx;
    idx++;
}
//刪除(具體看圖解)
void edd(int k){
    r[l[k]]=r[k];
    l[r[k]]=l[k];
}
int main(){
    cin >> n;
    init();
    string s;
    int k,x;
    while(n--){
        cin >> s;
        if(s=="L"){
            cin >> x;
            add(0,x);//在最左端插入,就是說在0這個節點的右邊插入一個數,而add函式是在第k個插入的點的右邊插入數,所以只用傳0節點過去就行了
        }else if(s=="R"){
            cin >> x;
            add(l[1],x);//這裡就跟上述的同理,(在最右側插入一個數,相當於在"1"(尾節點)左一個插入,畢竟都是不能越界)
                        //0和 1 只是代表 頭和尾  所以   最右邊插入 只要在  指向 1的 那個點的右邊插入就可以了
        }else if(s=="D"){
            cin >> k;
            edd(k+1);//這裡就很好解釋了,跟單連結串列刪除操作一致,使k節點的前後相連(具體看圖解);
            //這裡k+1,個人簡單理解,初始節點就是2,所以k+1是跟著自己定義的來判斷;
        }else if(s=="IL"){
            cin >> k >> x;
            add(l[k+1],x);//在左端點後插入一個數;
        }else{
            cin >> k >> x;
            add(k+1,x);//上述一樣(需要注意k+1);
        }
    }
    for(int i=r[0];i!=1;i=r[i]) cout << e[i] << " ";
    return 0;
}

  • 圖解:

    • 插入:

    • 刪除:

相關文章