2018.8.30 《剑指Offer》从零单刷个人笔记整理(66题全)目录传送门
临近开学事情忽然有点多了,开学之后打算新开两个征程:LeetCode和PAT,假期已经有点囤货了,开学再整理记录一下。
这道题初看很简单,一个个结点复制就完事了,好像没什么不同。细细一想,不对,next的可以直接复制,但是random的要怎么对应呢。于是比较直接能够想到的解法是以下的思路一,通过对链表结点的记录,实现新链表random的赋值。下面提供四种思路并进行比较:
方法一:标记法,复杂度o(n^2)。第一次复制,先按next顺序从头复制到新链表,同时利用链表结点值对每一个结点的位置进行记录(例如头结点的label值记录为0,代表其location);第二次复制,将所有结点的random结点根据其所在位置复制到新链表上。
方法二:哈希表法,复杂度o(n),空间换时间,代码简洁。第一次遍历,将每一个结点和新结点一一映射到Hashmap上;第二次遍历,复制所有next值与random值。
方法三:加长拆分法,复杂度o(n),效率高。第一步,复制链表的每一个结点并插在该结点之后;第二步,复制每一个结点的ramdom值;第三步,分离长链表 (注意这道题不能返回pHead,还要保留原链表的结构)。
方法四:递归法,复杂度o(n),空间效率略低。转化为一个头结点和除去头结点剩余部分,剩余部分操作和原问题一致,递归复制每一个结点。
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
Java实现(四种方法):
/**
*
* @author ChopinXBP
* 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。
* (注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
*
*/
import java.util.HashMap;
public class RandomListNode_24 {
public static class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
RandomListNode newhead0 = new RandomListNode(0);
RandomListNode newhead1 = new RandomListNode(1);
RandomListNode newhead2 = new RandomListNode(2);
newhead0.next = newhead1;
newhead0.random = newhead2;
newhead1.next = newhead2;
newhead1.random = null;
newhead2.random = newhead1;
RandomListNode p = newhead0;
while(p.next != null){
System.out.print(p.label);
p = p.next;
}
System.out.print(p.label);
System.out.print('\n');
p = newhead0;
for(int i = 0; i < 3; i++){
if(p.random == null){
System.out.print('#');
}else{
System.out.print(p.random.label);
}
p = p.next;
}
System.out.print('\n');
RandomListNode newheadx = Clone2(newhead0);
p = newheadx;
while(p.next != null){
System.out.print(p.label);
p = p.next;
}
System.out.print(p.label);
System.out.print('\n');
p = newheadx;
for(int i = 0; i < 3; i++){
if(p.random == null){
System.out.print('#');
}else{
System.out.print(p.random.label);
}
p = p.next;
}
}
//方法一:标记法,复杂度o(n^2)
public static RandomListNode Clone(RandomListNode pHead)
{
if(pHead == null)return null;
RandomListNode newhead = new RandomListNode(pHead.label);
RandomListNode pnew = newhead;
RandomListNode pold = pHead;
int count = 0;
//先按next顺序从头复制链表
while(pold.next != null){
pold.label = count; //标记当前结点所在链表位置
RandomListNode ptmp = new RandomListNode(pold.next.label);
pnew.next = ptmp;
pold = pold.next;
pnew = pnew.next;
count++;
}
//若只有一个结点,可以直接返回。返回前需要考察其random指针指向的是null还是本结点,然后进行赋值。
if(count == 0){
if(pHead.random != null) newhead.random = newhead;
return newhead;
}
pold.label = count; //收尾工作,标记最后一个结点
pnew = newhead;
pold = pHead;
RandomListNode pnewtmp;
for(int i = 0; i <= count; i++){
//若该结点random值为空则保留,若不空则从头遍历到该结点所在位置对random进行赋值
if(pold.random != null){
int location = pold.random.label; //寻找random结点所在位置
pnewtmp = newhead;
while(location > 0){
pnewtmp = pnewtmp.next;
location--;
}
pnew.random = pnewtmp;
}
if(i != count){
pnew = pnew.next;
pold = pold.next;
}
}
return newhead;
}
//方法二:哈希表法,复杂度o(n),空间换时间
public static RandomListNode Clone2(RandomListNode pHead) {
HashMap<RandomListNode, RandomListNode> map = new HashMap<RandomListNode, RandomListNode>();
RandomListNode cur = pHead;
while (cur != null) {
map.put(cur, new RandomListNode(cur.label)); // 将新旧结点配对在哈希表里
cur = cur.next;
}
cur = pHead;
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(pHead);
}
//方法三:加长拆分法,复杂度o(n),效率高
public static RandomListNode Clone3(RandomListNode pHead)
{
if(pHead == null)return null;
RandomListNode p = pHead;
//若只有一个结点,可以直接返回。返回前需要考察其random指针指向的是null还是本结点,然后进行赋值。
if(p.next == null){
RandomListNode newhead = new RandomListNode(pHead.label);
if(pHead.random != null) newhead.random = newhead;
return newhead;
}
//第一步:复制链表的每一个结点并插在该结点之后
while(p != null){
RandomListNode ptmp = new RandomListNode(p.label);
ptmp.next = p.next;
p.next = ptmp;
p = ptmp.next;
}
//收尾工作,p回到表头
p = pHead;
//第二步:复制每一个结点的ramdom值
while(p != null){
if(p.random != null){
p.next.random = p.random.next;
}
p = p.next.next;
}
//第三步,分离长链表
//这道题不能返回pHead,还要保留原链表的结构(实际上pHead有其他引用记录着,所以可以进行操作,但是要保证原链表结构不变)
RandomListNode newhead = new RandomListNode(0);
p = newhead;
while(pHead!=null){
p.next = pHead.next;
p = p.next;
pHead.next = pHead.next.next;
pHead = pHead.next;
}
return newhead.next;
}
//方法四:递归,思路清晰,复杂度o(n),空间效率低
//此题转化为一个头结点和除去头结点剩余部分,剩余部分操作和原问题一致,递归复制每一个结点。
public static RandomListNode Clone4(RandomListNode pHead) {
if (pHead == null)
return null;
RandomListNode newNode = new RandomListNode(pHead.label);
newNode.random = pHead.random;
newNode.next = Clone(pHead.next);
return newNode;
}
}
C++实现(方法二):
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead==NULL) return NULL;
map<RandomListNode*,RandomListNode*> m;
RandomListNode* pHead1 = pHead;
RandomListNode* pHead2 = new RandomListNode(pHead1->label);
RandomListNode* newHead = pHead2;
m[pHead1] = pHead2;
while(pHead1){
if(pHead1->next) pHead2->next = new RandomListNode(pHead1->next->label);
else pHead2->next = NULL;
pHead1 = pHead1->next;
pHead2 = pHead2->next;
m[pHead1] = pHead2;
}
pHead1 = pHead;
pHead2 = newHead;
while(pHead1){
pHead2->random = m[pHead1->random];
pHead1 = pHead1->next;
pHead2 = pHead2->next;
}
return newHead;
}
};
C++实现(方法三):
/*
struct RandomListNode {
int label;
struct RandomListNode *next, *random;
RandomListNode(int x) :
label(x), next(NULL), random(NULL) {
}
};
*/
class Solution {
public:
//复制原始链表的任一节点N并创建新节点N',再把N'链接到N的后边
void CloneNodes(RandomListNode* pHead)
{
RandomListNode* pNode=pHead;
while(pNode!=NULL)
{
RandomListNode* pCloned=new RandomListNode(0);
pCloned->label=pNode->label;
pCloned->next=pNode->next;
pCloned->random=NULL;
pNode->next=pCloned;
pNode=pCloned->next;
}
}
//如果原始链表上的节点N的random指向S,则对应的复制节点N'的random指向S的下一个节点S'
void ConnectRandomNodes(RandomListNode* pHead)
{
RandomListNode* pNode=pHead;
while(pNode!=NULL)
{
RandomListNode* pCloned=pNode->next;
if(pNode->random!=NULL)
pCloned->random=pNode->random->next;
pNode=pCloned->next;
}
}
//把得到的链表拆成两个链表,奇数位置上的结点组成原始链表,偶数位置上的结点组成复制出来的链表
RandomListNode* ReConnectNodes(RandomListNode* pHead)
{
RandomListNode* pNode=pHead;
RandomListNode* pClonedHead=NULL;
RandomListNode* pClonedNode=NULL;
//初始化
if(pNode!=NULL)
{
pClonedHead=pClonedNode=pNode->next;
pNode->next=pClonedNode->next;
pNode=pNode->next;
}
//循环
while(pNode!=NULL)
{
pClonedNode->next=pNode->next;
pClonedNode=pClonedNode->next;
pNode->next=pClonedNode->next;
pNode=pNode->next;
}
return pClonedHead;
}
//三步合一
RandomListNode* Clone(RandomListNode* pHead)
{
CloneNodes(pHead);
ConnectRandomNodes(pHead);
return ReConnectNodes(pHead);
}
};
C++实现(方法四):
class Solution {
public:
RandomListNode* Clone(RandomListNode* pHead)
{
if(pHead==NULL)
return NULL;
//开辟一个新节点
RandomListNode* pClonedHead=new RandomListNode(pHead->label);
pClonedHead->next = pHead->next;
pClonedHead->random = pHead->random;
//递归其他节点
pClonedHead->next=Clone(pHead->next);
return pClonedHead;
}
};
测试代码:
// ====================测试代码====================
void Test(char* testName, ComplexListNode* pHead)
{
if(testName != NULL)
printf("%s begins:\n", testName);
printf("The original list is:\n");
PrintList(pHead);
ComplexListNode* pClonedHead = Clone(pHead);
printf("The cloned list is:\n");
PrintList(pClonedHead);
}
// -----------------
// \|/ |
// 1-------2-------3-------4-------5
// | | /|\ /|\
// --------+-------- |
// -------------------------
void Test1()
{
ComplexListNode* pNode1 = CreateNode(1);
ComplexListNode* pNode2 = CreateNode(2);
ComplexListNode* pNode3 = CreateNode(3);
ComplexListNode* pNode4 = CreateNode(4);
ComplexListNode* pNode5 = CreateNode(5);
BuildNodes(pNode1, pNode2, pNode3);
BuildNodes(pNode2, pNode3, pNode5);
BuildNodes(pNode3, pNode4, NULL);
BuildNodes(pNode4, pNode5, pNode2);
Test("Test1", pNode1);
}
// m_pSibling指向结点自身
// -----------------
// \|/ |
// 1-------2-------3-------4-------5
// | | /|\ /|\
// | | -- |
// |------------------------|
void Test2()
{
ComplexListNode* pNode1 = CreateNode(1);
ComplexListNode* pNode2 = CreateNode(2);
ComplexListNode* pNode3 = CreateNode(3);
ComplexListNode* pNode4 = CreateNode(4);
ComplexListNode* pNode5 = CreateNode(5);
BuildNodes(pNode1, pNode2, NULL);
BuildNodes(pNode2, pNode3, pNode5);
BuildNodes(pNode3, pNode4, pNode3);
BuildNodes(pNode4, pNode5, pNode2);
Test("Test2", pNode1);
}
// m_pSibling形成环
// -----------------
// \|/ |
// 1-------2-------3-------4-------5
// | /|\
// | |
// |---------------|
void Test3()
{
ComplexListNode* pNode1 = CreateNode(1);
ComplexListNode* pNode2 = CreateNode(2);
ComplexListNode* pNode3 = CreateNode(3);
ComplexListNode* pNode4 = CreateNode(4);
ComplexListNode* pNode5 = CreateNode(5);
BuildNodes(pNode1, pNode2, NULL);
BuildNodes(pNode2, pNode3, pNode4);
BuildNodes(pNode3, pNode4, NULL);
BuildNodes(pNode4, pNode5, pNode2);
Test("Test3", pNode1);
}
// 只有一个结点
void Test4()
{
ComplexListNode* pNode1 = CreateNode(1);
BuildNodes(pNode1, NULL, pNode1);
Test("Test4", pNode1);
}
// 鲁棒性测试
void Test5()
{
Test("Test5", NULL);
}
int _tmain(int argc, _TCHAR* argv[])
{
Test1();
Test2();
Test3();
Test4();
Test5();
return 0;
}
#Coding一小时,Copying一秒钟。留个言点个赞呗,谢谢你#