剑指offer面试题26之复杂链表的复制问题

 @复杂链表的定义

   之前实现都是单链表,单链表的结构中有数据域和指针域,而今天我们就要由单链表过渡到复杂链表。那仫什仫是复杂链表呢?首先从结构上来说复杂链表比单链表多了一个随机指针域:指向链表的任意结点或者空,复杂链表的结构定义如下:
   
typedef struct ComplexLinkNode
{
	DataType data;
	struct ComplexLinkNode *next;
	struct ComplexLinkNode *random;
}ComplexLinkNode,*pComplexLinkNode;
 
    下图是一个我自己理解的复杂链表的定义图:
     剑指offer面试题26之复杂链表的复制问题_第1张图片

 @复杂链表的分析实现

     在刚开始看到这道题目的时候我的想法是如果要复制复杂链表首先就要复制复杂链表的所有结点啦,并且复制后的结点用next域连接起来,接下来就是如何复制复杂链表的random(随机指针域)了,假设源链表结点N的random域指向结点S,因为random的指向是随机的所以S结点可能在N结点的前面也可能在N结点的后面,要找到N结点的随机指针域指向的结点S就必须从头遍历链表知道找到结点s;假设原始链表从头遍历链表s步才找到结点S那仫要复制的结点N*的random结点就必须也从复制链表的头往后走s步才能找到S*结点,这样复制链表结点N*的random域也找到了
      假设一个复杂链表的总结点数为n,那仫上面这种方法的时间复杂度至少为O(n*n) ,我们知道上面思路的复杂度主要是找结点的random指向,那仫我们是不是可以进行优化呢?下面就是我们今天要实现的复杂度为O(n)的优化算法的思路分析了:
      第一步当然还是复制复杂链表的结点了,不过这次复制复杂链表是将复制后的结点N*结点连接到对应结点N的后面;
      第二步就是复制复杂链表的random域了,因为这次是将复制的结点N*连接到结点N的后面,所以找到结点的random域就相对于前面的想法快速了许多;假设原始链表结点N 的random域指向结点S,那仫复制出来的结点N*是结点N的next指针指向的结点,同样S*也是S结点的next指针指向的结点;
     在上述两步中复制了新结点的next域random域那仫第三步就是如何将结点分离出来了:通过观察我们发现奇数位置的结点用next连接起来就是原始结点,偶数位置的结点用next连接起来就是复制后的结点啦!
      最后将以上三步连接起来就是复制后的复杂链表啦!
      第一第二步实现的结果为:
      剑指offer面试题26之复杂链表的复制问题_第2张图片
        第三步实现结果如下:
        

@复杂链表的实现和实现结果:

        第一步.复制复杂链表的结点
      
void CloneNode(pComplexLinkNode head)
{
	//复制所有结点,将复制后的结点连接到对应结点的后面
	pComplexLinkNode newNode=NULL;
	pComplexLinkNode cur=head;
	while(cur)
	{
		newNode=CreateComplexNode(cur->data);
		newNode->next=cur->next;
		cur->next=newNode;
		cur=newNode->next;
	}
}

      第二步.复制复杂链表的random域
      
void ConnectRandomNode(pComplexLinkNode head)
{
	//复制结点的随机指针域
	pComplexLinkNode pclone=NULL;
	pComplexLinkNode cur=head;
	while(cur)
	{
		pclone=cur->next;
		if(cur->random != NULL)
		{
			pclone->random=cur->random->next;
		}
		cur=pclone->next;
	}
}

      第三步.将原始链表与复制后的链表分离
      
pComplexLinkNode SplitLink(pComplexLinkNode head)
{
	//将复制的所有结点分离出来,构成复制后的链表
	pComplexLinkNode cur=head;
	pComplexLinkNode Node=NULL;
	pComplexLinkNode head2=NULL;
	if(cur)
	{
		Node=head2=cur->next;
		cur->next=Node->next;
		cur=cur->next;
	}
	while(cur)   //找出偶数位置的结点连接构成复制后的链表
	{
		Node->next=cur->next;
		Node=cur->next;
		cur->next=Node->next;
		cur=cur->next;
	}
	return head2;
}

     测试结果:
     剑指offer面试题26之复杂链表的复制问题_第3张图片

 @复杂链表的源代码:

     ComplexLinkNode.h
   
#ifndef __COMPLEXLINKNODE_H__
#define __COMPLEXLINKNODE_H__

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int DataType;

typedef struct ComplexLinkNode
{
	DataType data;
	struct ComplexLinkNode *next;
	struct ComplexLinkNode *random;
}ComplexLinkNode,*pComplexLinkNode;

pComplexLinkNode CreateComplexNode(DataType x);
void CreateComplexList(pComplexLinkNode *phead);
void Print_ComplexLink(pComplexLinkNode head);

void CloneNode(pComplexLinkNode head);	
void ConnectRandomNode(pComplexLinkNode head);	
pComplexLinkNode SplitLink(pComplexLinkNode head);
void FreeComplexLink(pComplexLinkNode head);

#endif //__COMPLEXLINKNODE_H__

   ComplexLinkNode.c
   
#include"ComplexLinkNode.h"

pComplexLinkNode CreateComplexNode(DataType x)
{
	pComplexLinkNode newNode=(pComplexLinkNode)malloc(sizeof(ComplexLinkNode));
	if(NULL == newNode)
	{
		printf("out of memory\n");
		exit(EXIT_FAILURE);
	}
	newNode->data=x;
	newNode->next=NULL;
	newNode->random=NULL;
	return newNode;
}

void CreateComplexList(pComplexLinkNode *phead)
{
	pComplexLinkNode n1=CreateComplexNode(2);
	pComplexLinkNode n2=CreateComplexNode(4);
	pComplexLinkNode n3=CreateComplexNode(6);
	pComplexLinkNode n4=CreateComplexNode(8);
	pComplexLinkNode n5=CreateComplexNode(0);
	//
	n1->next=n2;
	n2->next=n3;
	n3->next=n4;
	n4->next=n5;
	//
	n1->random=n5;
	n2->random=n3;
	n3->random=n2;
	n4->random=n1;
	n5->random=n4;
	*phead=n1;
}

void Print_ComplexLink(pComplexLinkNode head)
{
	//以此种方式输出random域不可为空
	pComplexLinkNode cur=head;
	while(cur)
	{
		printf("cur:%d   cur->random->data:%d\n",cur->data,cur->random->data);
		cur=cur->next;
	}
}

void CloneNode(pComplexLinkNode head)
{
	//复制所有结点,将复制后的结点连接到对应结点的后面
	pComplexLinkNode newNode=NULL;
	pComplexLinkNode cur=head;
	while(cur)
	{
		newNode=CreateComplexNode(cur->data);
		newNode->next=cur->next;
		cur->next=newNode;
		cur=newNode->next;
	}
}

void ConnectRandomNode(pComplexLinkNode head)
{
	//复制结点的随机指针域
	pComplexLinkNode pclone=NULL;
	pComplexLinkNode cur=head;
	while(cur)
	{
		pclone=cur->next;
		if(cur->random != NULL)
		{
			pclone->random=cur->random->next;
		}
		cur=pclone->next;
	}
}

pComplexLinkNode SplitLink(pComplexLinkNode head)
{
	//将复制的所有结点分离出来,构成复制后的链表
	pComplexLinkNode cur=head;
	pComplexLinkNode Node=NULL;
	pComplexLinkNode head2=NULL;
	if(cur)
	{
		Node=head2=cur->next;
		cur->next=Node->next;
		cur=cur->next;
	}
	while(cur)   //找出偶数位置的结点连接构成复制后的链表
	{
		Node->next=cur->next;
		Node=cur->next;
		cur->next=Node->next;
		cur=cur->next;
	}
	return head2;
}

void FreeComplexLink(pComplexLinkNode head)
{
	pComplexLinkNode del=NULL;
	pComplexLinkNode cur=head;
	if(cur == NULL)   //空链表
	{
		return ;
	}
	else
	{
		while(cur)
		{
			del=cur;
			cur=cur->next;
			free(del);
			del=NULL;
		}
	}
}

          text.c
      
#include"ComplexLinkNode.h"

void text()
{
	pComplexLinkNode ret;
	pComplexLinkNode head;
	CreateComplexList(&head);
	printf("原始的复杂链表为:\n");
	Print_ComplexLink(head);
	//三步复制复杂链表
	CloneNode(head);
	ConnectRandomNode(head);
	ret=SplitLink(head);
	printf("复制后的复杂链表为:\n");
	Print_ComplexLink(ret);
	FreeComplexLink(ret);
}

int main()
{
	text();
	system("pause");
	return 0;
}

      代码实现到这里复杂链表的复制问题就实现了,在这里我提供一种哈希表的思想: 我们知道第一种思路主要是在寻找结点的random域上花费了时间,如果我们能够将原始复杂链表结点N与复制后链表结点N*的配对信息放在一个表中,假若原始链表结点N的random域指向的结点是S,那仫在哈希表中结点N*的random域必然指向的是结点S*,此时我么用时间复杂度O(1)实现了复杂链表的复制问题,这种思路也存在缺憾就是它的空间复杂度是极大的相当于我们以空间换取时间、 有兴趣的读者可以自己实现,在这里我只实现复杂度为O(n)的优化算法.
      每天学习一点新东西方不负这美好的青春.

      

         

你可能感兴趣的:(链表,面试题,复杂链表的复制问题)