陣列模擬雙連結串列
通過前面的學習我們知道單連結串列是單個指標指向操作,那麼通過類比我們可以把指標設定為兩個,並且讓它們分別指向前後資料,這就是“雙向連結串列”。使用這種連結串列,不僅可以從前往後, 還可以從後往前遍歷資料,十分方便。
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.總結
雙連結串列的操作與單連結串列存在著很大的聯絡,兩個指標使得插入操作變得相對來說繁瑣了一些,在寫程式碼的過程中一定要手動畫圖!畫圖!畫圖!
注:如果文章有任何錯誤或不足,請各位大佬盡情指出,評論留言留下您寶貴的建議!如果這篇文章對你有些許幫助,希望可愛親切的您點個贊推薦一手,非常感謝啦