[data structure] graphic explanation of magical single linked list and double linked list

1, Foreword

In our usual job, we may encounter the need to insert a number or a batch of numbers into the array. Here, if we use traditional violent insertion, the time complexity of each insertion is O(n). Once we insert more times, it is easy to timeout. Here, we need to learn a data structure - linked list.

This blog suggests learning and understanding with another blog( [graph theory] chain forward star)

2, Explanation of single linked list concept

In the linked list, we still use the array to simulate. In the array, we have the number of numbers in the array and the number itself, such as a[3]=7. The number is 3 and the number is 7. However, in the linked list, we also need to add a variable next to each number to store the number of the next number. For example, next[3]=9 means that the next number of a[3] is not a[4], but a[9].

Array: e[N],ne[N]

Note: it is specified that when ne[x]=-1, X is the last element of the linked list

In the single linked list, there is another important variable head, which represents the number of the first number in the current linked list. If we want to traverse the whole linked list, we need to start from head.

Didn't you understand? This is normal. Let's try to understand the linked list through code.

3, Explanation of single linked list code

Example link: Acwing single linked list

Implement a single linked list. The linked list is initially empty and supports three operations:

Insert a number into the chain header;
Delete paragraph k The number after the number of inserts;
In the first k Insert a number after an inserted number.
Now you want to modify the linked list M After all operations, the whole linked list is output from beginning to end.

be careful:Title No k The number of inserts does not refer to the number of inserts in the current linked list k number. For example, a total of n Number, then according to the chronological order of insertion, this n The number is: the number of the first insertion, the number of the second insertion n Number of inserts.

Input format
 The first line contains integers M,Indicates the number of operations.

next M Lines. Each line contains an operation command. The operation commands may be the following:

H x,Indicates that a number is inserted into the chain header x. 
D k,Indicates the deletion of section k Number after the number of inserts (when k When it is 0, it means to delete the header node).
I k x,Indicates on page k Insert a number after the inserted number x(In this operation k Are greater than 0).
Output format
 A total of one row, the entire linked list output from beginning to end.

Data range
1≤M≤100000
 All operations shall be legal.

Input example:
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
 Output example:
6 4 6 5

Correct code

//#pragma GCC optimize(2)
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef  pair<int, int> PII;
const int N = 1e6 + 7;

int head, e[N], ne[N], idx=1;  //Linked list implementation of the array, idx represents the number of numbers

void add_head(int x)  //Add x to the first of the linked list
{
	ne[idx] = head;
	e[idx] = x;
	head = idx++;
}

void insert(int k, int x)  //Insert x after the k th element
{
	e[idx] = x;
	ne[idx] = ne[k];
	ne[k] = idx;
	idx++;
}

void del(int k)  //Delete the next element after the k-th element
{
	ne[k] = ne[ne[k]];
}

void solve()
{
	head = -1;  //The initialization linked list header is - 1
	int m;
	cin >> m;
	while (m--)
	{
		char op;
		cin >> op;
		if (op == 'H')
		{
			int x;
			cin >> x;
			add_head(x);
		}
		else if (op == 'D')
		{
			int k;
			cin >> k;
			if (k == 0)
				head = ne[head];
			else
				del(k);
		}
		else
		{
			int k, x;
			cin >> k >> x;
			insert(k, x);
		}
	}
	for (int i = head;i!=-1;i=ne[i])
	{
		cout << e[i] << ' ';
	}
}

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	solve();
	return 0;
}


Through the code or not understand it? Let's pick out the functions in the code and explain them.

add_head() function

void add_head(int x)  //Add x to the first of the linked list
{
	ne[idx] = head;
	e[idx] = x;
	head = idx++;
}

First, let's assume head=3, idx=9, x=5. Through the previous concept introduction, we can know that the number of the first element of the linked list is 3

Then we should set ne[9] to head, that is, the current chain head - point 3, which means that point 3 becomes the next point of point 9. Then modify head=9, which means that the chain head is now point 9!

Then set e[9]=5 to represent the value of point 9 as 5

insert() function

void insert(int k, int x)  //Insert x after the k th element
{
	e[idx] = x;
	ne[idx] = ne[k];
	ne[k] = idx;
	idx++;
}

First, let's assume that the current linked list is like this, and idx is the element to be inserted

After execution, ne[idx] = ne[k]; ne[k] = idx; After the operation, the linked list becomes like this

Be sure to pay attention to the execution order of the above two codes, otherwise... Try it on the diagram yourself?

del() function

After understanding the above insert function, the understanding of del function is very simple

void del(int k)  //Delete the next element after the k-th element
{
	ne[k] = ne[ne[k]];
}

Draw a picture to understand

This actually achieves the effect of deletion.

4, Double linked list

If you can fully understand the single linked list, then the double linked list is not difficult to understand

Compared with the single linked list, the double linked list turns the next array into the left and right arrays L[N],R[N]. In this way, we can find the numbers of the left and right numbers of a number.

In the process of double linked list initialization, we artificially set the points numbered 0 and 1 as the leftmost and rightmost points of the linked list for convenient operation. See the example code explanation below for specific steps

Example link: Acwing double linked list

Implement a double linked list. The double linked list is initially empty and supports five operations:

Insert a number on the leftmost side;
Insert a number on the far right;
Will be the first k Delete the number of inserts;
In the first k Insert a number to the left;
In the first k Insert a number to the right
 Now you want to modify the linked list M After all operations, the whole linked list is output from left to right.

be careful:Title No k The number of inserts does not refer to the number of inserts in the current linked list k number. For example, a total of n Number, then according to the chronological order of insertion, this n The number is: the number of the first insertion, the number of the second insertion n Number of inserts.

Input format
 The first line contains integers M,Indicates the number of operations.

next M Lines. Each line contains an operation command. The operation commands may be the following:

L x,Indicates the number of inserts at the leftmost end of the linked list x. 
R x,Indicates the number of inserts at the rightmost end of the linked list x. 
D k,Indicates that the second k Number of inserts deleted.
IL k x,Indicates on page k Insert a number to the left.
IR k x,Indicates on page k Insert a number to the right.
Output format
 A total of one row, output the whole linked list from left to right.

Data range
1≤M≤100000
 All operations shall be legal.

Input example:
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
 Output example:
8 7 7 3 2 9

Correct code

//#pragma GCC optimize(2)
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<set>
#include<bitset>
#include<ctime>
#include<cstring>
#include<list>
#define ll long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef  pair<int, int> PII;
const int N = 1e6 + 7;

int L[N], R[N], e[N], idx;  //Used to implement double linked list

void init()  //Initialization function
{
	L[0] = 1;//Right node 0
	R[1] = 0;//Left node 1
	idx = 2;
}

void insert(int k, int x)  //Insert x after point k
{
	e[idx] = x;
	R[idx] =R[k];
	L[idx] = k;
	L[R[k]] = idx;
	R[k] = idx;
	idx++;
}

void del(int k)  //Delete function
{
	R[L[k]] = R[k];
	L[R[k]] = L[k];
}

void solve()
{
	init();
	int m;
	cin >> m;
	while (m--)
	{
		string op;
		cin >> op;
		if (op == "L")
		{
			int x;
			cin >> x;
			insert(1, x);
		}
		if (op == "R")
		{
			int x;
			cin >> x;
			insert(L[0], x);
		}
		if (op == "D")
		{
			int k;
			cin >> k;
			del(k+1);
		}
		if (op == "IL")
		{
			int k, x;
			cin >> k >> x;
			insert(L[k+1], x);
		}
		if (op == "IR")
		{
			int k, x;
			cin >> k >> x;
			insert(k+1, x);
		}
	}
	for (int i = R[1]; i != 0; i = R[i])  //Traversal of linked list
	{
		cout << e[i] << ' ';
	}
}

int main()
{
	std::ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	solve();
	return 0;
}


If the code is not easy to understand, drawing a picture may be a good choice.

Author: Avalon Demerzel, just like my blog. For more knowledge about graph theory and data structure, see the author's column graph theory and data structure

Tags: data structure linked list

Posted on Tue, 07 Sep 2021 01:39:07 -0400 by coool