AcWing 826. 單連結串列
模板題:
實現一個單連結串列,連結串列初始為空,支援三種操作:
- 向連結串列頭插入一個數;
- 刪除第 k 個插入的數後面的一個數;
- 在第 k 個插入的數後插入一個數。
現在要對該連結串列進行 M 次操作,進行完所有操作後,從頭到尾輸出整個連結串列。
注意:題目中第 k 個插入的數並不是指當前連結串列的第 k 個數。例如操作過程中一共插入了 n 個數,則按照插入的時間順序,這 n 個數依次為:第 1 個插入的數,第 2 個插入的數,…第 n 個插入的數。
輸入格式
第一行包含整數 M,表示操作次數。
接下來 M 行,每行包含一個操作命令,操作命令可能為以下幾種:
H x
,表示向連結串列頭插入一個數 x。D k
,表示刪除第 k 個插入的數後面的數(當 k 為 0 時,表示刪除頭結點)。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 種操作:
- 在最左側插入一個數;
- 在最右側插入一個數;
- 將第 k 個插入的數刪除;
- 在第 k 個插入的數左側插入一個數;
- 在第 k 個插入的數右側插入一個數
現在要對該連結串列進行 M 次操作,進行完所有操作後,從左到右輸出整個連結串列。
注意:題目中第 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≤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;
}
-
圖解:
-
插入:
-
刪除:
-