【题目】复制含有随机指针节点的链表C++

【题目描述】

一种特殊的单链表,节点结构如下所示

struct Node
{
	int _value;
	Node* _next;
	Node* _rand;
	Node(int val, Node*nx = nullptr, Node* rd = nullptr) : _value(val), _next(nx), _rand(rd) {}
	Node(Node* other) : _value(other->_value), _next(other->_next), _rand(other->_rand) {}
};

除了next指针外还具有一个随机指向的rand指针,rand指针可能指向链表中的任一节点,也可能为null。给定一个由Node组成的无环单链表的头节点head,请实现一个函数完成对该链表的复制。

要求:时间复杂度O(N),额外空间复杂度O(1)

【对照组算法】

如果不要求额外空间复杂度,我们可以开辟一个map来保存新旧节点的对应关系,然后连接新链表。代码如下

Node* copyList(Node *head)
{
	//创建一个哈希表
	std::unordered_map<Node*, Node*> mapList;

	//遍历传入的老链表,将老链表每个节点克隆出一个新节点,并存入对应的哈希表中
	Node* temp = head;
	while (nullptr != temp)
	{
		Node* newN = new Node(temp);
		mapList[temp] = newN;
		temp = temp->_next;
	}

	//遍历老链表,连接新链表
	Node* newHead = mapList[head];
	Node* newTemp = newHead;
	temp = head;
	while (nullptr != temp)
	{
		newTemp->_next = mapList[temp->_next];
		newTemp->_rand = mapList[temp->_rand];
		newTemp = newTemp->_next;
		temp = temp->_next;
	}
	return newHead;
}

【测试用例参考】

int main(int argc, char *argv[])
{
	Node n1(21);
	Node n2(1,&n1);
	Node n3(5, &n2);
	Node n4(7, &n3);
	Node n5(9, &n4);
	Node n6(54, &n5);
	Node n7(29, &n6);
	Node n8(33, &n7);

	n1._rand = &n7;
	n3._rand = &n1;
	n4._rand = &n6;
	n8._rand = &n3;

	Node *temp = &n8;
	while (nullptr != temp)
	{
		cout << temp->_value;
		if (nullptr != temp->_rand)
		{
			cout << "(" << temp->_rand->_value << ")";
		}
		cout << "  ";
		temp = temp->_next;
	}

	//拷贝链表
	Node* newList = copyList(&n8);

	cout << endl;
	temp = newList;
	while (nullptr != temp)
	{
		cout << temp->_value;
		if (nullptr != temp->_rand)
		{
			cout << "(" << temp->_rand->_value << ")";
		}
		cout << "  ";
		temp = temp->_next;
	}

	system("pause");
	return 0;
}

但是题目中要求空间复杂度为O(1),此算法的额外空间复杂度为O(N),不满足要求。

为什么要设置此对照组?因为正常情况下,这种算法思想是最快能想到的。在我们求职过程中,如果题目没有要求空间复杂度,可以用于在笔试中快速求解问题。

【算法描述】

本题的难点在于克隆的节点的rand指针无法在生成新链表节点同时确认出来;但是生成新链表后,新旧链表的节点(在不适用额外空间的情况下)不好进行一一对应,所以不方便通过旧链表节点的rand指针找到新链表节点的rand指针。

但是,如果我们想办法使新旧链表的对应节点能够一一对应起来,使找到旧节点计划能很快找到新节点,并且不影响遍历全部节点。就可以解决这个问题。

这里提供的算法思想是:将新节点连入旧链表中,使旧节点的next指针指向新节点,根据这一连接关系确认rand指针。即

旧链表:A -> B -> C -> D

连接成:A -> A' -> B -> B' -> C -> C' -> D -> D'

等确认完新链表的rand指针后,再进行分离。

【代码实现】

Node* copyListFun2(Node *head)
{
	//1)遍历老链表,将老链表每个节点克隆出一个新节点,并放入对应老链表节点的next位置
	//原链表:A->B->C->D
	//新链表:A->A'->B->B'->C->C'->D->D'
	Node* temp = head; //temp用于遍历原节点
	while (nullptr != temp)
	{
		Node* newN = new Node(temp);
		newN->_next = temp->_next;
		temp->_next = newN;
		temp = newN->_next;
	}

	//根据节点和克隆节点的位置关系,我们只要确认了原节点,就可以找到对应的克隆节点

	//2)确认每个克隆节点的rand指针
	temp = head; //temp用于遍历原节点
	while (nullptr != temp)
	{
		if (nullptr != temp->_rand)
		{
			temp->_next->_rand = temp->_rand->_next;
		}
		temp = temp->_next->_next;
	}

	//分离原链表和克隆链表
	Node* newHead = head->_next;
	Node* newTemp = newHead; //temp用于遍历克隆节点
	temp = head; //temp用于遍历原节点
	while (nullptr != temp)
	{
		temp = temp->_next = newTemp->_next;
		if (nullptr == temp)
		{
			break;
		}
		newTemp = newTemp->_next = temp->_next;
	}
	return newHead;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值