1. 程式人生 > >資料結構: 兩個單鏈表相交的一系列問題

資料結構: 兩個單鏈表相交的一系列問題

資料結構: 兩個單鏈表相交的一系列問題

這個是一個比較綜合的問題:

  • 若兩個單鏈表一個為有環,一個無環. 那麼肯定不能相交.

  • 若二者都沒有環, 問題就轉化為 兩個無環單鏈表是否相交,方法就是 快慢指標 ,是否能找到第一個相交的節點.

  • 若二者都有環,那麼問題變成了兩個有換單鏈表是否相交.

    第一,先找到二者是否相交.

    第二,若相交則需要遍歷一遍找到相交點.

該題可以分解為三個問題:

1 判斷一個連結串列是否有環

#include "MyInclude.h"

/*
	問題一:
	判斷一個連結串列是否有環.
	如果有,返回第一個進入環的節點.
	如果沒有,返回NULL
*/

// 快慢指標,若存在環,則一定會在環中的某一個節點相遇.
// 然後再找第入口
// https://blog.csdn.net/l294265421/article/details/50478818

ListNode* getLoopNode(ListNode* head) {
	// 至少有三個節點
	if (head == NULL||head->next==NULL||head->next->next==NULL) {
		return NULL;
	}

	// 初始值,slow走了一步,fast走了兩步
	ListNode* slow = head->next;
	ListNode* fast = head->next->next;

	while (slow != fast) {
		// 若走到了NULL,則無環
		if (fast->next == NULL || fast->next->next == NULL) {
			return NULL;
		}
		slow = slow->next;
		fast = fast->next->next;
	}
	// 存在環,找環的第一個節點.
	slow = head;
	while (slow != fast) {
		slow = slow->next;
		fast = fast->next;
	}
	return slow;
}

2 如何判斷兩個無環連結串列相交?

/*
	若兩個無環連結串列相交,返回相交節點,
	否則,返回NULL
*/


int getLen(ListNode* head) {
	int len = 0;
	while (head != NULL) {
		len++;
		head = head->next;
	}
	return len;
}

ListNode* noLoop(ListNode* head1, ListNode* head2) {
	if (head1 == NULL || head2 == NULL)
		return NULL;

	ListNode* p1 = head1;
	ListNode* p2 = head2;

	// 統計長度
	int len1 = getLen(p1);
	int len2 = getLen(p2);

	// 假設p1是長度比較長的
	if (len1 < len2) {
		ListNode* tmp = p1;
		p1 = p2;
		p2 = tmp;
	}

	int absDiff = (len1 - len2);

	while (absDiff != 0) {
		p1 = p1->next;
		absDiff--;
	}

	while (p1 != NULL&&p2 != NULL&&p1 != p2) {
		p1 = p1->next;
		p2 = p2->next;
	}

	if (p1 == NULL || p2 == NULL) {
		return NULL;
	}
	else {
		return p1;
	}
}

3 如何判斷兩個有環連結串列是否相交?

/*
	判斷兩個有環連結串列是否相交
	若相交,返回第一個相交點
	若不相交,返回NULL
*/


// 求出有環連結串列的環入口節點
ListNode* getLoopEnter(ListNode* head) {
	ListNode* slow = head->next;
	ListNode* fast = head->next->next;

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

ListNode* BothLoop(ListNode* head1, ListNode* head2) {
	// 1.先獲得兩個連結串列各自的環的入口
	ListNode* loop1 = getLoopEnter(head1);
	ListNode* loop2 = getLoopEnter(head2);
	// 2.或在入口處相等,那麼返回
	if (loop1 == loop2) {
		return loop1;
	}
	// 不相等,則遍歷一遍一個環自身,看是否能找到另一個環的入口節點,有則為相交,沒有則為不相交
	ListNode* p = loop1->next;
	while (p != loop1) {
		if (p == loop2) {
			return p;
		}
		p = p->next;
	}
	return NULL;
}

原題解答:

/*
	檢測兩個單鏈表是否相交,
	若相交,返回第一個相交的節點
	若不相交,返回NULL
*/

/*
	分析:
		若一個連結串列有環,一個連結串列沒有換,則不可能相交

		若兩個單鏈表都有環,
		則需要檢測兩個有環連結串列是否相交

		若二者都沒有,
		則判斷兩個無環的單鏈表是否相交
*/


// 1.判斷一個連結串列是否有環
ListNode* hasLoop(ListNode* head) {

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

	ListNode* slow = head->next;
	ListNode* fast = head->next->next;

	while (slow != fast) {
		// 走到NULL,說明沒有環
		if (fast->next == NULL || fast->next->next == NULL) {
			return NULL;
		}
		slow = slow->next;
		fast = fast->next->next;
	}
	slow = head;
	while (slow != fast) {
		slow = slow->next;
		fast = fast->next;
	}
	return slow;
}

// 2.兩個無環連結串列是否相交
int getLen(ListNode* head) {
	int len = 0;
	while (head != NULL) {
		len++;
		head = head->next;
	}
	return len;
}

ListNode* noLoop(ListNode* head1, ListNode* head2) {
	if (head1 == NULL || head2 == NULL)
		return NULL;

	ListNode* p1 = head1;
	ListNode* p2 = head2;

	// 統計長度
	int len1 = getLen(p1);
	int len2 = getLen(p2);

	// 假設p1是長度比較長的
	if (len1 < len2) {
		ListNode* tmp = p1;
		p1 = p2;
		p2 = tmp;
	}

	int absDiff = (len1 - len2);

	while (absDiff != 0) {
		p1 = p1->next;
		absDiff--;
	}

	while (p1 != NULL&&p2 != NULL&&p1 != p2) {
		p1 = p1->next;
		p2 = p2->next;
	}

	if (p1 == NULL || p2 == NULL) {
		return NULL;
	} else {
		return p1;
	}
}


// 3.兩個有環連結串列是否相交,
ListNode* bothLoop(ListNode* Loop1, ListNode* Loop2) {
	if (Loop1 == Loop2) {
		return Loop1;
	}
	else {
		ListNode* p = Loop1->next;
		while (p != Loop1) {
			if (p == Loop2)
				return p;
			p = p->next;
		}
		return NULL;
	}
}

ListNode* getInterSectNode(ListNode* head1, ListNode* head2) {

	// 1. 判斷一個連結串列是否有環
	ListNode* Loop1 = hasLoop(head1);
	ListNode* Loop2 = hasLoop(head2);

	// 2. 若二者都是空,
	if (Loop1 == NULL&&Loop2 == NULL) {
		return noLoop(head1, head2);
	}
	if (Loop1 != NULL&&Loop2 != NULL) {
		return bothLoop(Loop1, Loop2);
	}
	return NULL;
}