@[目录基本结构]((这里写自定义目录标题)
描述
复杂链表的复制, 首先理解什么叫做复杂链表, 复杂链表就是每个节点有两个指针, 一个指正是指向后面节点的, 另一个指针是指向链表当中任意一个节点的
clone /copy a linked list with next and random pointers
A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null.
首先理解题意, 什么是链表复制, 意思就是复制一个完完全全的链表处理, 即是真正的new所有的节点处理, 并且保持他们的关系, 各种节点之间的链接关系。
==而不仅仅是复制一下引用, 是深度复制而不是引用一下而已的基本情况 ==
所以在接下来的算法实现
总体思路情况
最传统的方法 时间复杂度为o(n^2)
这道题目如果给我的话就是, 很传统做法就是分为两部分,第一部分就是先把链表节点复制下来,这个过程当中不记录链表的随机节点1情况, 第二部分才是查找随机链表当中的指针复制情况
这样的话时间复杂度就是O(n2), 为何时间复杂度为0(n^2),主要是有一个嵌套循环在里面
我们在原始链表当中找到随机指针的时间复杂度为(1),可是如果我们是在复制后的链表查找该随机节点的话是时间复杂度也是(N), 外层循环也是O(N), 叠加起来的就是O(n^2)的时间复杂度
这种算法时间复杂度就是比较大的情况
分为两个步骤:
- 复制单链表的基本情况
- 从头开始遍历两个链表(查找相同情况)
具体代码如下该种情况
class Solution {
public:
RandomListNode* FindInNew(RandomListNode *oldHead, RandomListNode *newHead, RandomListNode *randNode)
{
RandomListNode *currNode = oldHead;
RandomListNode *newNode = newHead;
while(currNode != NULL && newNode != NULL)
{
if(randNode == currNode)
{
return newNode;
}
currNode = currNode->next;
newNode = newNode->next;
}
return NULL;
}
RandomListNode *copyRandomList(RandomListNode *phead) {
if(phead == NULL)
{
return NULL;
}
RandomListNode *currNode = phead;
RandomListNode *newHead = NULL, *preNode = NULL, *newNode = NULL;
/// 首先复制原链表的普通指针域, 一次遍历即可完成
while(currNode != NULL)
{
newNode = new RandomListNode(currNode->label);
currNode = currNode->next;
if(preNode == NULL)
{
newHead = newNode;
}
else
{
preNode->next = newNode;
}
preNode = newNode;
}
// 接着复制随机指针域, 需要两次遍历
currNode = phead;
newNode = newHead;
while(currNode != NULL && newNode != NULL)
{
RandomListNode *randNode = currNode->random; /// 随机指针域randNode
RandomListNode *newRandNode = FindInNew(phead, newHead, randNode); /// 查找到新链表中与randNode对应的指针域
newNode->random = newRandNode;
/// 链表同步移动
currNode = currNode->next;
newNode = newNode->next;
}
return newHead;
}
};
将上面
hashtable
空间换时间复杂度为 O(n), 记录
空间复杂度分析
Leetcode最佳解法
分为三次遍历分别解决这个为
主要是使用一个hashmap 记住下面
map(旧节点,新节点)
==旧节点的random r ==
map(r, 新链表当中的random)
新节点.random=新链表当中的random
我们在创建好基本的节点情况后,可以
while(new && old){
node tmp = old. random // 得到旧节点的随机节点情况
// 可以获取得随机节点在新链表当中
new_random = map[old_random];
new_list.random = new_random;
}
/**
* Definition for singly-linked list with a random pointer.
* struct RandomListNode {
* int label;
* RandomListNode *next, *random;
* RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
* };
*/
class Solution {
public:
RandomListNode *copyRandomList(RandomListNode *phead) {
if(phead == NULL)
{
return NULL;
}
RandomListNode *currNode = phead->next;
// 头结点, 还有最后节点情况
RandomListNode *newHead =new RandomListNode(phead->label), *preNode = newHead;
// 中间元素的基本情况
RandomListNode * newNode=NULL;
unordered_map<RandomListNode* ,RandomListNode*> map;// 在c++当中不需要new 一个基础数据类型
map[phead]= newHead;
/// 首先复制原链表的普通指针域, 一次遍历即可完
while(currNode != NULL)
{
newNode = new RandomListNode(currNode->label);
//添加映射关系;
map[currNode] = newNode;
preNode->next= newNode;
preNode = newNode;
currNode = currNode->next;
}
// 接着复制随机指针域, 需要两次遍历
currNode = phead;
newNode = newHead;
while(currNode != NULL && newNode != NULL)
{
// 获得下一个随机节点情况
RandomListNode *old_randNode = currNode->random; /// 随机指针域randNode
RandomListNode *new_randNode = map[old_randNode];
// 修改下面的关系情况
newNode->random = new_randNode;
currNode = currNode->next;
newNode = newNode->next;
}
return newHead;
}
};
自己利用hashmap的关系, 利用其保存的下面一个节点的关系图, 还是另外一个东西
hash
*/
public class Solution {
public RandomListNode copyRandomList(RandomListNode head) {
if(head==null) { return null; }
RandomListNode cur = head.next;
HashMap<RandomListNode, RandomListNode > map =new HashMap<>();
RandomListNode new_link= new RandomListNode(head.label);
map.put(head, new_link);
while(cur!=null){
RandomListNode tmp= new RandomListNode(cur.label);
map.put(cur, tmp);
cur = cur.next;
}
cur= head;
while(cur!=null){
// 直接利用下面的基本关系即可
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur =cur.next;
}
return map.get(head);
}
}
直接利用链接关系找到
问题的关键就是如何在 如何查找记录随机节点的信息等情况
所以这是关键问题
等情况
如图分三步:
-
插入拷贝节点, 将新链表的节点都插入在旧节点的后面
-
复制random指针,遍历一遍旧节点, 找到旧节点后面的随机节点, 其中新链表当中的随机节点在旧节点的后面,
-
分解至两个独立列表, 重新遍历,断开,改变新旧链表的下一个节点情况
总体关系如下所示的基本情况
A.random = c
在第二部的时候就是 A = C
都是下面一个元素的基本情况
public class Solution {
public RandomListNode copyRandomList(RandomListNode head) {
RandomListNode cur = head;
while(cur!=null)
{
RandomListNode temp = new RandomListNode(cur.label);
temp.next = cur.next;
cur.next = temp;
cur = temp.next;
}
// copy random pointer
cur = head;
while(cur != null)
{
RandomListNode temp = cur.next;
if(cur.random != null)
temp.random = cur.random.next;
cur = temp.next;
}
//decouple two links
// 将中间节点
cur = head;
RandomListNode dup = head == null? null:head.next;
while(cur != null)
{
RandomListNode temp = cur.next;
cur.next = temp.next;
if(temp.next!=null)
temp.next = temp.next.next;
cur = cur.next;
}
return dup;
}
}
关于第三个办法是参照如下的链接的情况