【数据结构】单链表--进阶题目

在实现单链表的基本与基础的操作后:

  我们可以在单链表中考虑环与相交以及复杂单链表的问题。

1.判断单链表是否带环?若带环,求环的长度?求环的入口点?并计算每个算法的时间复杂度&空间复杂度:

   首先判断是否有环,可以定义快慢指针,fast走俩步,slow走一步,如果是带环链表fast最后一定会在环内与slow相遇,无环的fast会先到头。

  因为fast比slow走的快,所以进入环是fast一定在slow前面(或相遇),假设领先n步,fast每次必slow先走一步,所以每走一次俩者间距离减少为n-1步,最后俩者一定会相遇。相遇时返回相遇点。

  最后我写了一个制作环的函数,用以测试以后的代码。

ListNode* IsCycle(ListNode *pList)//是否带环
{
	if ((pList == NULL) || (pList->next == NULL))
		return NULL;
	ListNode *fast = pList->next->next;
	ListNode *slow = pList->next;
	while ((fast != slow) && fast && fast->next)
	{
		fast = fast->next->next;
		slow = slow->next;
	}
	if (fast == slow)
		return fast;
	else
		return NULL;
}
//pos在链表内
void GetCycle(ListNode **ppList, ListNode *pos)//做带环链表
{
	assert(ppList && *ppList);
	ListNode *tail = *ppList;
	while (tail->next)
	{
		tail = tail->next;
	}
	tail->next = pos;
}

    首先为了求环的长度,我们想到只要让某个指针在环内走一圈(不停走,再次遇到自己)时,所走的步数就是环的长度。我们不知道尾巴的长度无法判断一个指针何时进入环,所以可以利用上面的快慢指针,求出快慢指针的相遇点,然后走一圈即可。因为此情况必定带环,所以不用考虑控的情况。

   为求环的入口点我们对环进行推论。fast在与slow相遇时比他多走了N圈,最后得出结论L=NC-X 所以让俩个指针分别从相遇点与头节点开始走,最后一定会在入口处相遇。


//该链表带环
ListNode* CycldeMeNode(ListNode *pList)//求环的相遇点
{
	ListNode *fast = pList->next->next;
	ListNode *slow = pList->next;
	while (fast != slow)
	{
		fast = fast->next->next;
		slow = slow->next;
	}
		return fast;
}

int GetCycleLen(ListNode *meetNode)//求环长度
{
	ListNode *cur = meetNode->next;
	int count = 1;
	while (cur != meetNode)
	{
		cur = cur->next;
		count++;
	}
	return count;
}

ListNode* GetCycleEntry(ListNode* pList, ListNode *meetNode)//求环入口点
{
	ListNode* cur = pList;
	ListNode* ent = meetNode;
	while (ent != cur)
	{
		ent = ent->next;
		cur = cur->next;
	}
	return ent;
}

用以测试上面代码的主函数,注意空链表等特殊条件。


void TestList()
{
	ListNode *list = NULL;
	Pushback(&list, 1);
	Pushback(&list, 2);
	Pushback(&list, 3);
	Pushback(&list, 4);
	Pushback(&list, 5);
	PrintList(list);
	ListNode *pos = Find(list, 2);
	GetCycle(&list, pos);
	ListNode *meetNode = IsCycle(list);
	if (meetNode)
		printf("***带环***\n");
	else
		printf("***不带***\n");
	printf("***meetNode: %d\n", meetNode->data);
	int len = GetCycleLen(meetNode);
	printf("***Len: %d\n", len);
	ListNode *ent = GetCycleEntry(list, meetNode);
	printf("***Enter: %d\n", ent->data);
	//DestoryList(&list);
}
int main()
{
	TestList();
	return 0;
}

2.判断两个链表是否相交,若相交,求交点。(假设链表不带环)

 首先分析相交情况,只有一种 >— 其余都为他的特殊情况。

  1判断俩链表是否相交,同时找到俩链表的尾节点看他们是否指向同一块空间。

  2找交点,算出俩链表的长度差gap,然后让长的链表指针向前走gap步,另一链表指针指向头,俩指针同时开始走,当指向同一块空间时就为交点。

  3如果俩链表中,有为空即没有交点。

void GetCress(ListNode **ppList, ListNode *pos)//做相交链表
{
	assert(ppList);
	if (*ppList == NULL)
		return;
	ListNode *tail = *ppList;
	while (tail->next)
	{
		tail = tail->next;
	}
	tail->next = pos;
}

ListNode* CressNoc(ListNode* pList1, ListNode* pList2)//无环链表是否相交
{
	if ((pList1 == NULL) && (pList2 == NULL))
		return NULL;
	ListNode *tail1 = pList1;
	ListNode *tail2 = pList2;
	int count1 = 1;
	int count2 = 1;
	while (tail1->next)
	{
		tail1 = tail1->next;
		count1++;
	}
	while (tail2->next)
	{
		tail2 = tail2->next;
		count2++;
	}
	if (tail1 != tail2)
		return NULL;
	int gap = abs(count1 - count2);
	ListNode *cur1 = pList1;
	ListNode *cur2 = pList2;
	if (count1 > count2)
	{
		while (gap--)
		{
			cur1 = cur1->next;
		}
	}
	else
	{
		while (gap--)
		{
			cur2 = cur2->next;
		}
	}
	while (cur1 != cur2)
	{
		cur1 = cur1->next;
		cur2 = cur2->next;
	}
	return cur1;
}

3.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】

  图为可能带环所有的状况,将状况条理化分析并逐步解决。

   1.俩者都无环,判断同无环函数(无环链表判断相交函数)

   2.其中一个有环, 必不相交

   3.俩个都有环,交点在尾部。可类似无环链表的判断,把环的入口点看作相交链表的尾部,判断同无环函数

   4.俩个都有环,交点为环。定义快慢指针,分别在俩入口点走,一圈之内必相交。

   5.俩个都有环,不相交。在4的条件下不相交。


ListNode* CressMc(ListNode* pList1, ListNode* pList2)//可能带环链表是否相交
{
	if ((pList1 == NULL) && (pList2 == NULL))
		return NULL;
	ListNode* c1 = IsCycle(pList1);
	ListNode* c2 = IsCycle(pList2);
	//1.俩个都不带环
	if ((c1 == NULL) && (c2 == NULL))
	{
		return CressNoc(pList1, pList2);
	}
	//2.只有一个带环 必不相交
	else if (c1 == NULL || c2 == NULL)
		return NULL;
	//俩都带环 
	else
	{
		ListNode* ent1 = GetCycleEntry(pList1, c1);
		ListNode* ent2 = GetCycleEntry(pList1, c2);
		//3.俩都带环 交点在尾巴
		if (ent1 == ent2)
		{
			ListNode *tail1 = pList1;
			ListNode *tail2 = pList2;
			int count1 = 1;
			int count2 = 1;
			while (tail1->next != ent1)
			{
				tail1 = tail1->next;
				count1++;
			}
			while (tail2->next != ent2)
			{
				tail2 = tail2->next;
				count2++;
			}
			int gap = abs(count1 - count2);
			ListNode *cur1 = pList1;
			ListNode *cur2 = pList2;
			if (count1 > count2)
			{
				while (gap--)
				{
					cur1 = cur1->next;
				}
			}
			else
			{
				while (gap--)
				{
					cur2 = cur2->next;
				}
			}
			while (cur1 != cur2)
			{
				cur1 = cur1->next;
				cur2 = cur2->next;
			}
			return cur1;
		}
		//4.俩个都带环 交点为环
		else
		{
			ListNode *fast = ent1->next->next;
			ListNode *slow = ent2->next;
			int count = GetCycleLen(pList1);
			while ((fast != slow) && (--count))
			{
				fast = fast->next->next;
				slow = slow->next;
			}
			if (fast == slow)
				return ent1;
			else
				return NULL;
		}
	}
}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是使用C语言实现的单链表原地逆转的代码: ```c #include <stdio.h> #include <stdlib.h> struct Node { int data; struct Node* next; }; void reverseList(struct Node** head) { struct Node* prev = NULL; struct Node* current = *head; struct Node* next = NULL; while (current != NULL) { next = current->next; current->next = prev; prev = current; current = next; } *head = prev; } void printList(struct Node* head) { while (head != NULL) { printf("%d ", head->data); head = head->next; } printf("\n"); } void push(struct Node** headRef, int newData) { struct Node* newNode = (struct Node*)malloc(sizeof(struct Node)); newNode->data = newData; newNode->next = *headRef; *headRef = newNode; } int main() { struct Node* head = NULL; push(&head, 3); push(&head, 2); push(&head, 1); printf("Original list: "); printList(head); reverseList(&head); printf("Reversed list: "); printList(head); return 0; } ``` 在上述代码中,我们首先定义了一个 `Node` 结构体来表示单链表中的每个节点,包括节点的值和指向下一个节点的指针。然后我们定义了 `reverseList` 函数来实现原地逆转单链表的功能。该函数接受一个指向指针的指针 `head`,这是因为我们需要通过指针来修改单链表的头节点,所以我们传递指向指针的指针。在函数内部,我们使用三个指针 `prev`、`current` 和 `next` 来依次遍历单链表,并将每个节点的指针指向前一个节点,从而实现原地逆转单链表的目的。 最后,我们定义了一个 `push` 函数来添加新节点到单链表的头部,并定义了 `printList` 函数来打印单链表中所有节点的值。在 `main` 函数中,我们创建了一个包含三个节点的单链表,并调用 `reverseList` 函数来原地逆转该单链表,最后打印出原始和逆转后的单链表
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值