數組模擬雙鏈表
通過前面的學習我們知道單鏈表是單個指針指向操作,那么通過類比我們可以把指針設定為兩個,並且讓它們分別指向前后數據,這就是“雙向鏈表”。使用這種鏈表,不僅可以從前往后, 還可以從后往前遍歷數據,十分方便。
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下標是從二開始的
}
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 ++;
}
那么問題來了如果我們想在下標為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];
}
5.例題
實現一個雙鏈表,雙鏈表初始為空,支持 5 種操作:
- 在最左側插入一個數;
- 在最右側插入一個數;
- 將第 k 個插入的數刪除;
- 在第 k 個插入的數左側插入一個數;
- 在第 k 個插入的數右側插入一個數
現在要對該鏈表進行 MM 次操作,進行完所有操作后,從左到右輸出整個鏈表。
注意:題目中第 k 個插入的數並不是指當前鏈表的第 k 個數。例如操作過程中一共插入了 n 個數,則按照插入的時間順序,這 n 個數依次為:第 1 個插入的數,第 2 個插入的數,…第 n 個插入的數。
輸入格式
第一行包含整數 M,表示操作次數。
接下來 M 行,每行包含一個操作命令,操作命令可能為以下幾種:
L x
,表示在鏈表的最左端插入數 x。R x
,表示在鏈表的最右端插入數 x。D k
,表示將第 k 個插入的數刪除。IL k x
,表示在第 k 個插入的數左側插入一個數。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.總結
雙鏈表的操作與單鏈表存在着很大的聯系,兩個指針使得插入操作變得相對來說繁瑣了一些,在寫代碼的過程中一定要手動畫圖!畫圖!畫圖!
注:如果文章有任何錯誤或不足,請各位大佬盡情指出,評論留言留下您寶貴的建議!如果這篇文章對你有些許幫助,希望可愛親切的您點個贊推薦一手,非常感謝啦