數組模擬雙鏈表,你get到了嗎?


數組模擬雙鏈表

通過前面的學習我們知道單鏈表是單個指針指向操作,那么通過類比我們可以把指針設定為兩個,並且讓它們分別指向前后數據,這就是“雙向鏈表”。使用這種鏈表,不僅可以從前往后, 還可以從后往前遍歷數據,十分方便。

1.使用數組模擬雙鏈表

// e[i] 表示節點i的值
// l[i] 指向當前節點的上一節點(左節點)的指針:即存的是左邊一個節點的位置(下標)
// r[i] 指向當前節點的下一節點(右節點)的指針:即存的是右邊一個節點的位置(下標)
// idx 存儲當前已經用到的節點(當前已經用到哪個節點了)——同單鏈表
int e[N], l[N], r[N], idx;

2.初始化雙鏈表

我們默認初始化:0表示左端點(首),1表示右端點(尾),這兩個是邊界點。

初始化兩個哨兵節點(0、1分別是首和尾),R[0]=1,L[1]=0,分別表示將首節點0向右連接到尾節點1、將尾節點1向左連接到首節點0。R[0]表示0的右,L[1]表示1的左

因為初始化就用了兩個,因此idx = 2

//初始化雙鏈表
void init()
{
    r[0] = 1, l[1] = 0;
    idx = 2;// 因為初始化就用了兩個,因此idx = 2下標是從二開始的
}

image

3.正下標為k的節點的右邊插入x

//在下標為k的節點的右邊,插入x
void add(int k ,int x)
{
    e[idx] = x;
    r[idx] = r[k];
    l[idx] = k;
    l[r[k]] = idx;
    r[k] = idx;
    idx ++;
}

image

那么問題來了如果我們想在下標為k的左邊插入x該怎么實現呢?

你可能會說可以向上述在右邊插入操作一樣實現,那當然是可以的,但是代碼就多寫了。在下標為k的節點的左邊插入x,其實就是在l[k]的右邊(l[k]為下標k位置節點的左邊節點的下標!)插入x,即我們可以直接調用上述add()函數操作即可,減少了一定的代碼量。

// 在下標為k的節點的左邊插入x
	add(l[k] , x);

4.刪除下標為k的節點

// 刪除第k個節點
void remove(int k)
{
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}

image

5.例題

實現一個雙鏈表,雙鏈表初始為空,支持 5 種操作:

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

現在要對該鏈表進行 MM 次操作,進行完所有操作后,從左到右輸出整個鏈表。

注意:題目中第 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≤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

【參考代碼】

#include<iostream>

using namespace std;

const int N = 100000+10;
int l[N], r[N], e[N], idx;

//初始化雙鏈表
void init()
{
    r[0] = 1, l[1] = 0;
    idx = 2;// 下標是從二開始的,因此第k個數的下標:k + 1
}

//在下標為k的節點的右邊,插入x
void add(int k ,int x)
{
    e[idx] = x;
    r[idx] = r[k];
    l[idx] = k;
    l[r[k]] = idx;
    r[k] = idx;
    idx ++;
}
// 刪除第k個節點
void remove(int k)
{
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}
int main()
{   
    int m;
    cin>>m;
    
    //初始化雙鏈表
    init();
    
    while(m --)
    {
        int k, x;
        string opt;
        cin>>opt;
        //表示在鏈表的最左端插入數 x(邊界節點0的右邊)
        if(opt == "L")
        {
            cin>>x;
            add(0, x);
        }
        //表示在鏈表的最右端插入數 xx(邊界節點1的左邊邊)
        else if(opt == "R")
        {
            cin>>x;
            add(l[1],x);
        }
        //表示將第 k 個插入的數刪除
        else if(opt == "D")
        {
            cin>>k;
            remove(k + 1);
        }
        //表示在第 k 個插入的數左側插入一個數
        else if(opt == "IL")
        {
            cin>>k>>x;
            add(l[k + 1], x);
            
        }
        else if(opt == "IR")
        {
            cin>>k>>x;
            add(k + 1, x);           
        }
    }
    
    // 遍歷雙鏈表
    for(int i=r[0]; i!=1; i=r[i]) cout << e[i] << ' ';

    return 0;
}

下標是從二開始的,因此第k個數的下標:k + 1

6.總結

雙鏈表的操作與單鏈表存在着很大的聯系,兩個指針使得插入操作變得相對來說繁瑣了一些,在寫代碼的過程中一定要手動畫圖!畫圖!畫圖!

注:如果文章有任何錯誤或不足,請各位大佬盡情指出,評論留言留下您寶貴的建議!如果這篇文章對你有些許幫助,希望可愛親切的您點個贊推薦一手,非常感謝啦


免責聲明!

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



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