827. 双链表

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int e[N];
int l[N],r[N];
int idx;
void init()
{
	r[0]=1,l[1]=0;
	idx=2;
}
void remove(int k)
{
	l[r[k]]=l[k];
	r[l[k]]=r[k];
}
void add_to_right(int k,int x)
{
	e[idx]=x;
	l[idx]=k,r[idx]=r[k];
	l[r[k]]=idx,r[k]=idx;
	idx++;
}
int main()
{
	init();
	int t;
	cin>>t;
	while(t--)
	{
		char s[10];
		cin>>s;
		if(s[0]=='L')
		{
			int x;
			cin>>x;
			add_to_right(0,x);
		}
		else if(s[0]=='R')
		{
			int x;
			cin>>x;
			add_to_right(l[1],x);
		}
		else if(s[0]=='D')
		{
			int k;
			cin>>k;
			remove(k+1);
		}
		else if(s[1]=='L')
		{
			int k,x;
			cin>>k>>x;
			add_to_right(l[k+1],x);
		}
		else
		{
			int k,x;
			cin>>k>>x;
			add_to_right(k+1,x);
		}
	}
	for(int i=r[0];i!=1;i=r[i])
	{
		cout<<e[i]<<" ";
	}
	puts("");
	return 0;
}

双链表,其实和单链表还是有一些相似之处,但是双链表更加精巧,尤其体现在有两个端点,单链表只有一个head头节点,双链表有左端点和右端点两个端点

设置了三个函数,第一个函数是初始化函数,把0作为左端点,1作为右端点,刚开始的时候,刚开始的时候0的右边是1,1的左边是0,链表除了两个端点,没有其他的元素,索引下标idx从2开始计数

第二个函数是删除函数,详细的来说是,删除第k个插入的数,改变指针即可,比较巧妙,原来的元素的右边的元素的左指针指向原来元素左边的元素,相当于跳过了原来的元素,原来元素左边元素的右指针指向原来元素右边的元素,假设是这样子的:本来是 a ,b ,c,b表示插入的第k个元素,让c的左指针指向a,让a的右指针指向c,就可以实现删除中间的元素b的效果

第三个函数是插入一个元素,详细的说是,在第k个插入的元素右边插入一个元素,其实比较简单,就是插入的元素需要断开第k个插入的元素和该元素右边元素的链接,然后重新建立一个链接,假设把第k个插入的元素记作a,a右边的元素记作b,我们需要让插入的元素的左指针指向a,右指针指向b,让a的右指针指向插入的元素,b的左指针指向插入的元素

实现五种操作

第一种操作是在最左端插入一个元素,其实就是在0的右边插入一个元素,直接调用第三个函数即可

第二种操作是在最右端插入一个元素,其实就是在右端点的左边一个元素的右边插入一个元素,所以把第一个参数设置为右端点的左边一个元素即可

第三种操作是删除第k个插入的元素,直接调用函数即可,注意索引下标从2开始计数,所以第k个插入的元素的索引下标是k+1(表现在函数里面 ),假设索引下标从0开始计数,第k个插入的元素的索引下标是k-1,假设索引下标从1开始计数,第k个插入的元素的索引下标是k

第四种操作是在第k个插入的元素左边插入一个数字,其实就是在第k个插入的元素左边一个元素右边插入一个元素,所以把调用函数的第一个参数设置为第k个插入元素的左边一个元素即可,还是需要注意索引下标

第五种操作是在第k 个插入的元素右边插入一个数字,直接调用函数即可,注意索引下标

输出的时候注意,从左端点的右边开始循环,一直到右端点截止,每一次找右指针指向的元素输出

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int e[N];
int l[N],r[N];//左指针和右指针
int idx;//索引下标
void init()//初始化
{
	r[0]=1,l[1]=0;
	idx=2;
}
void remove(int k)//删除
{
	l[r[k]]=l[k];
	r[l[k]]=r[k];
}
void add_to_right(int k,int x)//在第k个插入的元素右边插入一个元素
{
	e[idx]=x;
	l[idx]=k,r[idx]=r[k];
	l[r[k]]=idx,r[k]=idx;
	idx++;
}
int main()
{
	init();
	int t;
	cin>>t;
	while(t--)
	{
		char s[10];
		cin>>s;
		if(s[0]=='L')//最左端插入一个元素
		{
			int x;
			cin>>x;
			add_to_right(0,x);
		}
		else if(s[0]=='R')//最右端插入一个元素
		{
			int x;
			cin>>x;
			add_to_right(l[1],x);
		}
		else if(s[0]=='D')//删除第k 个插入的元素
		{
			int k;
			cin>>k;
			remove(k+1);
		}
		else if(s[1]=='L')//在第k个插入的元素左边插入一个元素
		{
			int k,x;
			cin>>k>>x;
			add_to_right(l[k+1],x);
		}
		else//在第k个元素的右边插入一个元素
		{
			int k,x;
			cin>>k>>x;
			add_to_right(k+1,x);
		}
	}
	for(int i=r[0];i!=1;i=r[i])//输出双链表
	{
		cout<<e[i]<<" ";
	}
	puts("");
	return 0;
}

自己又独立敲了一遍,WA了一次,瞪眼法调试了出来,成功ac,有两个细节需要注意,先把WA的代码和AC的代码依次放在下面

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int e[N],l[N],r[N];
int idx;
void init()
{
    r[0]=1,l[1]=0;
    idx=2;
}
void remove(int k)
{
    l[r[k]]=l[k];
    r[l[k]]=r[k];
}
void add_to_right(int k,int x)
{
    e[idx]=x;
    l[idx]=l[k];
    r[idx]=r[k];
    r[l[k]]=idx;
    l[r[k]]=idx;
    idx++;
}
int main()
{
    init();
    int t;
    cin>>t;
    while(t--)
    {
        char s[10];
        cin>>s;
        if(s[0]=='L')
        {
            int x;
            cin>>x;
            add_to_right(0,x);
        }
        else if(s[0]=='R')
        {
            int x;
            cin>>x;
            add_to_right(l[1],x);
        }
        else if(s[0]=='D')
        {
            int k;
            cin>>k;
            remove(k+1);
        }
        else if(s[1]=='L')
        {
            int k,x;
            cin>>k>>x;
            add_to_right(l[k+1],x);
        }
        else
        {
            int k,x;
            cin>>k>>x;
            add_to_right(k+1,x);
        }
    }
    for(int i=r[0];i!=1;i=r[i])
    {
        cout<<e[i]<<" ";
    }
    puts("");
    return 0;
}
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int e[N],l[N],r[N];
int idx;
void init()
{
    r[0]=1,l[1]=0;
    idx=2;
}
void remove(int k)
{
    l[r[k]]=l[k];
    r[l[k]]=r[k];
}
void add_to_right(int k,int x)
{
    e[idx]=x;
    l[idx]=k;
    r[idx]=r[k];
    l[r[k]]=idx;
    r[k]=idx;
    idx++;
}
int main()
{
    init();
    int t;
    cin>>t;
    while(t--)
    {
        char s[10];
        cin>>s;
        if(s[0]=='L')
        {
            int x;
            cin>>x;
            add_to_right(0,x);
        }
        else if(s[0]=='R')
        {
            int x;
            cin>>x;
            add_to_right(l[1],x);
        }
        else if(s[0]=='D')
        {
            int k;
            cin>>k;
            remove(k+1);
        }
        else if(s[1]=='L')
        {
            int k,x;
            cin>>k>>x;
            add_to_right(l[k+1],x);
        }
        else
        {
            int k,x;
            cin>>k>>x;
            add_to_right(k+1,x);
        }
    }
    for(int i=r[0];i!=1;i=r[i])
    {
        cout<<e[i]<<" ";
    }
    puts("");
    return 0;
}

第一个是因为没有注意其实只有两个元素,不要扯到k元素左边的元素,这里是指往k元素右边插入一个元素的时候(k元素是第k个插入的元素的简称),不要牵扯到k元素左边的元素,是需要在k元素和k元素右边元素之间插入一个新的元素

第二个是注意操作的顺序,如果经过我们的操作,变量发生了我们不想要的改变,那就会出错,这个非常难发现,所以一定要小心,还是上面提到的函数,WA的那份代码,好吧,其实是完全没有经过思考,应该是k元素的右边是新插入的元素,但是我们不可以先写

cpp 复制代码
r[k]=idx;
cpp 复制代码
l[r[k]]=idx;

这样子写,r[k]发生了改变,和我们的愿意有所区别,代码就是这样,有一些区别结果就是天差地别

所以我们要先写下面一行,再写上面一行

相关推荐
passer__jw7671 小时前
【LeetCode】【算法】3. 无重复字符的最长子串
算法·leetcode
passer__jw7671 小时前
【LeetCode】【算法】21. 合并两个有序链表
算法·leetcode·链表
sweetheart7-71 小时前
LeetCode22. 括号生成(2024冬季每日一题 2)
算法·深度优先·力扣·dfs·左右括号匹配
李元豪3 小时前
【智鹿空间】c++实现了一个简单的链表数据结构 MyList,其中包含基本的 Get 和 Modify 操作,
数据结构·c++·链表
我不是星海3 小时前
1.集合体系补充(1)
java·数据结构
UestcXiye3 小时前
《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项
c++·计算机网络·ip·tcp
一丝晨光4 小时前
编译器、IDE对C/C++新标准的支持
c语言·开发语言·c++·ide·msvc·visual studio·gcc
景鹤4 小时前
【算法】递归+回溯+剪枝:78.子集
算法·机器学习·剪枝
_OLi_4 小时前
力扣 LeetCode 704. 二分查找(Day1:数组)
算法·leetcode·职场和发展
丶Darling.4 小时前
Day40 | 动态规划 :完全背包应用 组合总和IV(类比爬楼梯)
c++·算法·动态规划·记忆化搜索·回溯