陣列模擬單連結串列你會了嗎?

時間最考驗人發表於2021-10-31

連結串列

實現連結串列的方式

 struct Node
 {
 	int val;
 	Node *next;
 };// 不講——競賽不常用

每次建立一個新的連結串列的時候,就會呼叫一次new函式來建立新的節點(動態建立連結串列),這個操作是非常慢的

單連結串列:演算法題中單連結串列用的最多的是鄰接表(n個連結串列)。應用:儲存樹和圖

雙連結串列:優化某些問題

模擬單連結串列

1.使用陣列來模擬單連結串列

// head 表示頭節點的下標
// e[i] 表示節點i的值
// ne[i] 表示節點i的next指標(下一個節點的位置)是多少
// idx 儲存當前已經用到的節點(當前已經用到哪個節點了)
int head, e[N], ne[N], idx;

image

2.初始化單連結串列

初始化:預設-1是代表空節點 head 初始值為-1,idx 初始值為0;

//初始化
void init()
{
	head = -1;
	idx = 0;
}

image

3.插入操作(頭插法)

  • 建立這個節點的值(e[idx] = x)
  • head的賦值給當前節點next[i]指標(ne[idx] = head)
  • 讓head重新指向頭節點(head = idx)
  • 將idx後移(idx++)為下次插入操作做好準備
//將x插到頭節點(用的最多!)
void add_to_head(int x)
{
	e[idx] = x;// 將插入節點的value值存下
	ne[idx] = head;// 操作1:將新插入節點的指標指向後一個節點(這裡的後一個節點說的就是頭節點!)
	head = idx;// 操作2:更新頭節點的指標(head為指向第一個節點的指標(第一個節點的下標!))
	idx++; // 新節點插入之後(節點被使用之後)idx++後移(為下一次插入做準備)
}

image

4.在下標為k的節點後插入x

  • 建立這個節點的值(e[idx]=val)
  • head的值賦給當前新節點next[i]指標(ne[idx]=ne[k])
  • 將下標為k的節點指向當前節點(ne[k]=idx)
  • 將idx向後移動(idx++)
void add(int k, int x)
{
	e[idx] = x;
	ne[idx] = ne[k]; //02 :新插入節點的指標指向k位置節點的後一個節點
	ne[k] = idx; // 03:k位置的節點的指標指向新插入的節點
	idx++;
}

image

5.刪除下標為k的節點的後一個節點

// 將下標是k的點後面的點刪掉
void remove(int k)
{
    ne[k] = ne[ne[k]];
}

image

6.刪除頭節點

head = ne[head];

image

7.例題

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

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

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

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

輸入格式

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

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

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

輸出格式

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

資料範圍

1≤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

【參考程式碼】

#include<iostream>

using namespace std;

const int N = 100000+10;
int e[N], ne[N], idx, head;

//初始化單連結串列
void init()
{
   head = -1;
   idx = 0; 
}
// 向表頭插入資料
void add_to_head(int x)
{
    e[idx] = x;
    ne[idx] = head;
    head = idx;
    idx++;
}
// 在下標為k的節點後插入x
void add(int k, int x)
{
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx;
    idx++;
}
// 刪除下標為k的節點的後一個節點
void remove(int k)
{
    ne[k] = ne[ne[k]];
}
int main()
{
    int m;
    cin>>m;
    
    //別忘了初始化連結串列
    init();
    
    while(m --)
    {   
        int k, x;
        char opt;
        cin>>opt;
        if(opt == 'H')
        {
            cin>>x;
            add_to_head(x); // 頭插
        }
        
        else if(opt == 'D')
        {
            cin>>k;
            if(k == 0) head = ne[head];// 當 k 為 0 時,表示刪除頭結點
            
            remove(k - 1); // 刪除第 k 個插入的數後面的數(第k個對應下標:k - 1)
        }
        
        else if(opt == 'I')
        {
            cin>>k>>x;
            add(k - 1, x); // 在第 k 個插入的數後面插入一個數 x
        }
    }
    
    // 遍歷單連結串列  i = ne[i]使得i往後走(ne[i]是當前節點的下一個節點的位置)
    for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';
    cout << endl;
    
    return 0;
}

總結

一開始在頭插法那裡卡了好久,其主要原因還是對head, e[N], ne[N], idx;的含義沒有理解透,從而導致不理解具體操作的程式碼是怎麼得來的,通過幾次手動模擬畫圖之後就能更好的理解啦!

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

相關文章