算法题中常用的数据结构——链表

211 阅读3分钟

“我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第1篇文章,点击查看活动详情

链表

struct ListNode {
	int val;
	ListNode* next;
};

一.单链表结构

1.寻找链表的中点 作为链表题中较为简单的题,中点问题无外乎就是用快慢指针来完成 当链表的个数为偶数时 对于快指针的next进行判断,则会使慢指针在mid前一个停下 对于快指针的next的next进行判断,则会使慢指针在mid前一个停下

ListNode* findlistmid(ListNode *root)//当链表的个数为偶数时返回mid前一个的值
{
	ListNode* n1 = root,*n2=root;
	while(n2->next!=nullptr&&n2->next->next!=nullptr)
	{
		n1 = n1->next;
		n2 = n2->next->next;
	}
	return n1;
}

ListNode* FindListmid(ListNode* root)//当链表的个数为偶数时返回mid后一个的值
{
	ListNode* n1 = root, * n2 = root;
	while (n2 != nullptr && n2->next != nullptr)
	{
		n1 = n1->next;
		n2 = n2->next->next;
	}
	return n1;
}

2.翻转链表 首先定义一个空的节点表示翻转后尾节点(原头节点指向位置) curr表示的是当前遍历的节点 prev表示的是当前节点的上一节点(也就是翻转后当前节点所指向的节点)

ListNode* TurnList(ListNode* root)
{
			ListNode* prev = nullptr;
			ListNode* curr = root;
			while (curr) {
				ListNode* next = curr->next;
				curr->next = prev;
				prev = curr;
				curr = next;
			}
			return prev;
}

二.双链表结构 判断两单链表是否相交 时间复杂度O(N) 空间复杂度O(1)

1.首先第一步需要判断的是两个链表是否是有环的 第一种方法:哈希表 判断节点地址是否在哈希表内注册判断链表有无环 第二钟方法:快慢指针(快慢指针的所在环内转的圈数少于两圈) (1).当快指针指向空->无环 (2).当快指针与慢指针相遇->有环(且将快指针返回开头(每次走一步),当再次相遇时,得到入环节点)

ListNode*FindListIncorporate(ListNode*node)
{
	if(node==nullptr)return nullptr;
	ListNode* slow;
	ListNode* fast;
	if(node->next!=nullptr)slow=node->next;
	else return nullptr;
	if(node->next->next!=nullptr)fast=node->next->next;
	else return nullptr;
	while(1)
	{
		if(fast==nullptr||fast->next==nullptr)return nullptr;
		if(fast==slow)break;
		fast=fast->next->next;
		slow=slow->next;
	}
	fast=node;
	while(fast!=slow)
	{
		fast=fast->next;
		slow=slow->next;
	}
	return slow;
}

2.对两个链表的的有无环进行情况分类

(1).当两单链表都无环(如果相交,最后部分一定共有) 首先得到两链表的尾节点end1和end2和长度len1和len2 当end1!=end2时:无公共节点 当ende1==end2时:有公共节点 令长链表的头节点先出发走abs(len1-len2)再一起走 最终相遇的第一个节点为两链表第一个相交节点

优化:
	对len1计算链表长度时 n++
	对len2计算链表长度时 n--
	最终n>0 len1长 n<0 len2长
ListNode* Ringlesslist(ListNode *node1,ListNode*node2)
{
	ListNode *end1=node1,*end2=node2;
	int n=0;
	while(end1->next!=nullptr)
	{
			end1=end1->next;
			++n;
	}
	while(end2->next!=nullptr)
	{
		 end2=end2->next;
		 --n;
	}
	if(end1!=end2)return nullptr;
	ListNode *shot=n>0?node2?node1;
	ListNode *elder=n>0?node1?node2;
	n=abs(n);
	while(shot!=elder)
	{
		if(n>0)
		{
			elder=elder->next;
			n--;
		}else{
			shot=shot->next;
			elder=elder->next;
		}
	}
	return shot;

}

(2).当一链表有环一链表无环时 无相交

(3).当两链表都有环(入环节点为loop1,loop2)

   1).当两链表在入环前相交,此时则为两链表都无环时的情况(将入环节点看作尾节链表在环前就相交点)
	ListNode *Sameentrypoint(ListNode*node1,ListNode*node2,ListNode*loop)
	{
		ListNode *head1=node1;
		ListNode *head2=node2;
		int n=0;
	while(node1!=loop)
	{
			node1=node1->next;
			++n;
	}
	while(node2!=loop)
	{
		 node2=node2->next;
		 --n;
	}
	ListNode *shot=n>0?head2:head1;
	ListNode *elder=n>0?head1:head2;
	n=abs(n);
	while(shot!=elder)
	{
		if(n>0)
		{
			elder=elder->next;
			n--;
		}else{
			shot=shot->next;
			elder=elder->next;
		}
	}
	return shot;
	}
    2)各自成环:loop1在向下走的过程中遇不上loop2,无相交

    3)有不同的入环节点:loop1向下走的过程中遇上loop2,return(loop1或loop2)
ListNode*samering(ListNode *loop1,ListNode*loop2)
{
	ListNode *loop=loop1;
	loop1=loop1->next;
	while(loop1!=loop)
	{
		if(loop1==loop2)return loop1;//return loop2;
		loop1=loop1->next;
	}
	return nullptr;
}

双环情况下的判断函数

ListNode *Doublelooplist(ListNode*node1,ListNode*node2,ListNode*loop1,ListNode*loop2)
{
	if(loop1==loop2)
		return Sameentrypoint(node1,node2,loop1);
		else return samering(loop1,loop2);
}

主代码

ListNode *Findingintersectingnodes(ListNode *node1,ListNode*node2)
{
	ListNode*loop1=FindListIncorporate(node1);
	ListNode*loop2=FindListIncorporate(node2);


	if(loop1!=nullptr&&loop2!=nullptr)return Doublelooplist(node1,node2,loop1,loop2);
	if(loop1==nullptr&&loop2==nullptr)return Ringlesslist(node1,node2);
	return nullptr;
}