剑指Offer___关于复杂链表的复制算法剖析

本文的源代码和测试代码:https://github.com/clearoff/learngit/blob/master/ComplexLinklist.c

1、复杂链表的定义:

      一般的,我们把只有一个数据于和一个指针的节点所组成的链表叫做单链表,由单链表我们引出了复杂链表的定义,如下图:


【复杂链表在C语言中的定义】:

typedef struct ComplexLinkNode
{
	Datatype data;
	struct ComplexLinkNode *p_Next;    //指向下一个节点的指针
	struct ComplexLinkNode *p_sibling;  //指向任意一个节点或者空
}ComplexLinkNode;

由图可知,复杂链表不光含有单链表中的指向下一个元素的指针,还含有一个指针可以指向任意一个节点或者NULL!

下来我们就来分析一下如何对这个复杂链表进行复制:

方法1:首先我们可以拷贝复杂链表中的单链表部分,并且让拷贝出来的链表所有节点的p_Sibling都指向NULL,

随后我们就要明确所拷贝链表中每一个p_Sibling指针的指向,我们可以先遍历源链表,用两个变量分别记录从头节点到p_Sibling指针不为NULL的节点和p_Sibling所指向节点的步数,然后从所拷贝的链表中找到相应的节点,并进行连接。

【相应C语言代码】:

ComplexLinkNode* Clone_Nodes(ComplexLinkNode* list)      //时间复杂度为O(n*n)的方法
{
	assert(list);
	ComplexLinkNode* list2 = NULL;
	ComplexLinkNode* cur1  = list;
	ComplexLinkNode* tmp   = NULL;
	int count1 = 0;             //记录原链表找到指针的步数
	int count2 = 0;             //记录找到指针所指向节点的步数
	while (cur1)       //先拷贝复杂链表中的单链表部分
	{
		ComplexLinkNode* New  = NULL;
		New = (ComplexLinkNode*)malloc(sizeof(ComplexLinkNode)* 1);     //开辟一个新节点
		if (New == NULL)
		{
			printf("Out of memory!\n");
			exit(EXIT_FAILURE);
		}
		New->data = cur1->data;
		New->p_Next = NULL;
		New->p_sibling = NULL;
		if (list2 == NULL)
		{
			list2 = New;
			tmp = list2;
		}
		else
		{
			tmp->p_Next = New;
			tmp = New;
		}
		cur1 = cur1->p_Next;
	}
	cur1 = list;
	while (cur1)
	{
		ComplexLinkNode *pos1 = NULL;
		ComplexLinkNode *pos2 = NULL;
		count1 = 0;
		count2 = 0;
		if (cur1->p_sibling != NULL)
		{
			ComplexLinkNode *pcur1 = list;
			ComplexLinkNode *pcur2 = list;
			pos1 = cur1;
			pos2 = cur1->p_sibling;
			while (pcur1 != pos1)
			{
				count1++;
				pcur1 = pcur1->p_Next;
			}
			while (pcur2 != pos2)
			{
				count2++;
				pcur2 = pcur2->p_Next;
			}
			pcur1 = list2;
			pcur2 = list2;
			while ((pcur1 != NULL)&&count1)
			{
				count1--;
				pcur1 = pcur1->p_Next;
			}
			while ((pcur2 != NULL) && count2)
			{
				count2--;
				pcur2 = pcur2->p_Next;
			}
			pcur1->p_sibling = pcur2;
		}
		cur1 = cur1->p_Next;
	}
	printf("复杂链表复制成功!\n");
	return list2;
}
由于这种算法需要两次遍历原链表,所以时间复杂度很高,为了减少时间复杂度,我们来参考一下第二种方法:


方法二:在克隆复杂链表中的单链表时,我们不重新建立头节点,而是直接把N节点的拷贝节点链在N节点的后面,

如下图:


【该步骤的C语言代码】:

ComplexLinkNode* CloneNodes(ComplexLinkNode *list)              //复制复杂链表的pNext部分!
{
	assert(list);
	ComplexLinkNode *pNode = list;
	while (pNode)
	{
		ComplexLinkNode *pClone = (ComplexLinkNode*)malloc(sizeof(ComplexLinkNode)* 1);
		if (pClone == NULL)
		{
			printf("Out of memory!\n");
			exit(EXIT_FAILURE);
		}
		pClone->data = pNode->data;
		pClone->p_Next = pNode->p_Next;
		pClone->p_sibling = NULL;
		pNode->p_Next = pClone;
		pNode = pClone->p_Next;
	}
	return list;
}
随后,我们在明确经过上面一个人步骤后链表中每一个节点的指向,找到了N节点p_Sibling指针不为NULL的节点后,我们只需要把N节点的下一个节点指向N节点p_Sibling所指向的节点的下一个节点就可以了。

【该步骤的C语言代码】:

ComplexLinkNode* ConnectSiblingNodes(ComplexLinkNode* head)       //复制出来结点的p_sibling指针
{
	assert(head);
	ComplexLinkNode* pNode = head;
	while (pNode)
	{
		ComplexLinkNode* pClone = pNode->p_Next;
		if (pNode->p_sibling != NULL)
		{
			pClone->p_sibling = pNode->p_sibling->p_Next;
		}
		pNode = pClone->p_Next;
	}
	return head;
}

最后一步:切断原链表和拷贝出来的链表

【C语言代码】:

ComplexLinkNode* CutList(ComplexLinkNode* head)
{
	assert(head);
	ComplexLinkNode* head2 = NULL;
	ComplexLinkNode* pNode = head;
	ComplexLinkNode* pClone = NULL;
	if (pNode)                             //将克隆出来的节点连接头节点
	{
		head2 = pNode->p_Next;
		pClone = pNode->p_Next;
		pNode->p_Next = pClone->p_Next;
		pNode = pNode->p_Next;
	}
	while (pNode)
	{
		pClone->p_Next = pNode->p_Next;
		pClone = pNode->p_Next;
		pNode->p_Next = pClone->p_Next;
		pNode = pNode->p_Next;
	}
	return head2;
}


如果本文有问题,希望大家能提些意见!!!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值