单链表相关OJ题(中)

单链表OJ

  • 六、单链表的逆置
  • 七、寻找单链表的中间节点
  • 八、求相交链表相交的起始节点
  • 九、判断链表中是否有环
  • 十、求链表中环的入口点(重点理解)

六、单链表的逆置

SListNode* reserve(SListNode*head)
{
//只有一个节点 或 链表为空
	if (head == NULL || head->next == NULL)
		return head;

	SListNode* pre = NULL;
	SListNode* cur = head;
   SListNode* after=NULL;
   
//修改指针的指向即可
	while (cur)
	{
		after = cur->next;
		cur->next = pre;
		pre = cur;
		cur = after;
	}
	return pre;
}

分析:
起始各个节点的位置:
单链表相关OJ题(中)_第1张图片

第一次 while 循环:
单链表相关OJ题(中)_第2张图片
修改指针指向,重复前边步骤:
单链表相关OJ题(中)_第3张图片

七、寻找单链表的中间节点

SListNode* middlenode1(SListNode*head)
{
	//奇数节点返回中间
	//偶数返回中间的第二个节点

	if (head == NULL || head->next == NULL)
		return head;

	SListNode*slow = head;
	SListNode*fast = head;

	while (fast)
	{
		if (fast->next != NULL)
		{
			slow = slow->next;
			fast = fast->next->next;
		}
	}
	return slow;
}

分析:
起始 slow fast 位置,fast 每次走两步,slow每次走一步:
单链表相关OJ题(中)_第4张图片
则:此时刚好奇数个节点,fast 达到最后一个节点时,slow 刚好停在中间节点的位置:
单链表相关OJ题(中)_第5张图片

若有偶数个节点时:
单链表相关OJ题(中)_第6张图片
此时,fast 到达 NULL 位置时,slow 刚好停在中间第二个节点位置:
单链表相关OJ题(中)_第7张图片

SListNode* middlenode2(SListNode* head)
{
	//奇数节点返回中间
	//偶数返回中间的第一个节点

	if (head == NULL || head->next == NULL)
		return head;

	SListNode*slow = head;
	SListNode*fast = head;
	SListNode* pre = NULL;
	while (fast)
	{
		if (fast->next != NULL)
		{
			pre = slow;
			slow = slow->next;
			fast = fast->next->next;
		}
	}
	if (fast)
		return slow;  //奇数个节点
	else
		return pre;  //偶数个节点

}

分析:
起始位置的节点,pre 用来标记 slow 的前一个节点:
单链表相关OJ题(中)_第8张图片
此时 ,fast !=NULL 时,slow 刚好是中间节点(即有奇数个节点时):
单链表相关OJ题(中)_第9张图片
若有偶数个节点:
单链表相关OJ题(中)_第10张图片
此时,pre 记录了 slow 的前一个节点位置,即偶数个节点的中间第一个节点位置:
单链表相关OJ题(中)_第11张图片

八、求相交链表相交的起始节点

SListNode* samenode(SListNode* list1, SListNode* list2)
{
	if (list1 == NULL || list2 == NULL)
		return NULL;

	SListNode* cur1 = list1;
	SListNode* cur2 = list2;

	int count1 = 1, count2 = 1;
	while (cur1->next != NULL)
	{
		count1++;
		cur1 = cur1->next;
	}
	while (cur2->next != NULL)
	{
		count2++;
		cur2 = cur2->next;
	}
	if (cur1 != cur2)
		return NULL;  //不相交


	//相交
	int gap = count1 - count2;
	cur1 = list1; cur2 = list2;
	if (gap > 0) {
		while (gap--)
			cur1 = cur1->next;
	}
	else {
		while (gap++)
			cur2 = cur2->next;
	}

	while (cur1 != cur2) {
		cur1 = cur1->next;
		cur2 = cur2->next;
	}
	return cur1;
}

分析:

单链表除了头节点和尾节点以外,其余节点有且仅有一个直接前驱节点和一个直接后继节点,因此两条相交链表相交的可能性有以下可能:
单链表相关OJ题(中)_第12张图片

因此求第一个交点:
单链表相关OJ题(中)_第13张图片

首先,找到两条链表的表尾节点,判断两个表尾节点是否为同一个节点,若是则两个链表相交,否则不交;
其次,判断两条链表长度是否相等,若相等则定义两个节点分别从两个链表头节点开始比对,找到第一个相同节点(不只是值相等)即为第一个交点,若不相等,则需要让较长的链表先走链表长度差值步,然后在统一向后比对寻找相同节点。

九、判断链表中是否有环

bool hasCycle(SListNode* head)
{
	if (head == NULL || head->next == NULL)
		return false;

	SListNode* cur1 = head;
	SListNode* cur2 = head;
	while (cur2 && cur2->next) {
		cur2 = cur2->next->next;    //快指针每次走两步
		cur1 = cur1->next;        //满指针每次走一步

		if (cur1 == cur2)
			return true;
	}
	return false;
}

分析:
定义快慢指针,快指针每次走两步,慢指针每次走一步

单链表相关OJ题(中)_第14张图片
则它们总会在环中某一个位置相遇:
单链表相关OJ题(中)_第15张图片

十、求链表中环的入口点(重点理解)

SListNode* detectCycle(SListNode* head)
{
	SListNode* fast = head;
	SListNode* slow = head;
	int cycle = 0;

	while (fast && fast->next) {
		fast = fast->next->next;
		slow = slow->next;
		if (fast == slow) {       //相遇点
			cycle = 1;
			break;
		}
	}
	if (!cycle)
		return NULL;      //不带环


	//带环,求入口点
	SListNode* pH = head;
	SListNode* pM = fast;

	while (pH != pM) {
		pH = pH->next;
		pM = pM->next;
	}
	return pM;
}

分析:
首先需要分析链表中是否带环,其次寻找环的入口点:

假设环外长度为 L,环中从入口位置到相遇点距离为 X,环长度为 r
单链表相关OJ题(中)_第16张图片
又因为 fast 与 slow 分别为快慢指针,fast 每次走两步,slow 每次走一步,因此 fast 走过的距离应为 slow 的两倍,故有如下关系:
(n 为自然常数,假设找到相遇点之前 fast 绕环行走了 nr 步,即转了 n 圈)
L+X+nr = 2*(L+X) ---------》 化简有:
nr=L+X ---------------> L= nr - X
说明定义一个 pH指针从链表头节点开始,一个指针 pM 从相遇点开始,每次分别前进一步,最终两个节点会在环的入口点相遇(因为走过的距离 L==nr - X 相等)

ps:博文内容为个人原创,有问题欢迎留言讨论呀~~

你可能感兴趣的:(数据结构,数据结构,链表,算法)