데이터 구조: 연결 리스트 벨트 루프 문제 해결

여기에 이미지 설명 삽입

개인 홈페이지 : 개인 홈페이지
개인 컬럼 : "데이터 구조" "C 언어"


1. 체인시계의 벨트링을 확인합니다.

  • 아이디어
    Slow와 Fast 두 개의 변수를 정의하는데, Slow 포인터는 한 단계씩 진행하고, Fast 포인터는 두 단계씩 진행합니다. Linked List가 링을 가지고 있다면 Slow와 Fast는 링에서 만나야 합니다. 아이디어는 매우 간단합니다. 그런데 왜 그럴까요? ? ?

  • 설명 1
    느림이 링 부분에 막 들어왔을 때 빠름과 느림은 N개의 노드만큼 떨어져 있습니다.
    천천히는 한걸음씩 가고, 빠르게는 두걸음씩 나아갑니다.
    다음과 같습니다.
    여기에 이미지 설명 삽입
    이때 빠름과 느림의 단계 크기 차이는 1입니다.
    느린 속도와 빠른 속도를 한 번 연속으로 이동하면 빠른 속도와 느린 속도 사이의 거리는 (N-1) 노드이며, 간격은 0 노드입니다.
    이렇게
    여기에 이미지 설명 삽입
    느린 것과 빠른 것은 반드시 링에서 만나야 하며 놓칠 수 없습니다.

  • 확장
    만약 fast가 n 단계를 밟는다면 (n > 2)? 느린 것과 빠른 것은 링에서 반드시 만나게 되는 걸까?

  • 설명 2
    느림이 링 부분에 막 들어왔을 때 빠름과 느림은 N개의 노드만큼 떨어져 있습니다.
    느리게는 한 번에 한 걸음씩 가고, 빠르게는 한 번에 세 걸음씩 걷는다고 가정해 보세요.
    다음과 같습니다.
    여기에 이미지 설명 삽입
    이때 느림과 빠름의 단계 크기 차이는 2입니다.

느림과 빠름이 한 번 연속으로 움직인다면 느림과 빠름의 거리는 (N-2), 느림과 빠름의 거리는 (N-4)가 되며 그 주기는 앞뒤로 돌아간다. 짝수, 느리고 빠르게 이동 N/ 2회, 거리가 0이 되고 둘이 만난다. N이 홀수일 때 느리고 빠른 움직임은 N/2번이고 둘 사이의 거리는 1이 된다. 다시 느리고 빠른 움직임이라면 둘 사이의 거리는 -1, 즉 C(의 크기)가 된다. 반지) -1. C-1이 짝수이면 느린 것과 빠른 것은 (C-1)/2번 움직여서 만나게 됩니다. C-1이 홀수이면 느리고 빠른 이동(C-1)/2번이고 둘 사이의 거리는 여전히 -1입니다. 즉, 둘은 끝없이 반복되어 영원히 놓칠 것입니다.

여기에 이미지 설명 삽입
, 빠르게 3보를 움직일 때 느리게 만나지 못하고 놓치는 경우가 발생할 수 있습니다.
빠르게 움직이는 4단계는 어떻습니까?
다음과 같이
여기에 이미지 설명 삽입
거꾸로 살펴보면 N이 빠른 것과 느린 것의 단계 차이의 정수배일 때만 느린 것과 빠른 것이 만난다는 것을 알 수 있습니다.

  • 암호
//141. 环形链表
bool hasCycle(struct ListNode* head) {
    
    
    struct ListNode* slow = head;
    struct ListNode* fast = head;

    if (head == NULL)
    {
    
    
        return false;
    }

    while (fast && fast->next)
    {
    
    
        fast = fast->next->next;
        slow = slow->next;

        if (fast == slow)
        {
    
    
            return true;
        }
    }

    return false;

}

2. 연결리스트의 헤드 노드를 반환합니다.

1. 공식 방법


여기에 이미지 설명 삽입
, newhead와 Meet이 동시에 이동하며, 두 사람이 만날 때 만나는 지점이 Linked List의 Ring 부분의 Head Node가 됩니다.

  • 코드는 아래와 같이 표시됩니다.
142. 环形链表 II(返回环的头节点)
// 
// 公式法
// 到相遇的距离                         C是环的大小
// slow走的距离是L+X,fast走的距离是L+N*C+X
// 则2*slow == fast
//   2*(L + X) = L + N*C + X
//      L = N * C - X
//		L = (N - 1) * C + C - X
//		L = C - X
//链表除环的长度    相遇点到换头的距离  
//        
struct ListNode* detectCycle(struct ListNode* head) {
    
    
    struct ListNode* fast = head;
    struct ListNode* slow = head;

    if (head == NULL)
    {
    
    
        return NULL;
    }

    //外层循环找环
    while (fast && fast->next)
    {
    
    
        slow = slow->next;
        fast = fast->next->next;

        if (slow == fast)
        {
    
    
            struct ListNode* meet = slow;
            struct ListNode* newhead = head;
            //找环头节点
            while (newhead != meet)
            {
    
    
                meet = meet->next;
                newhead = newhead->next;
            }

            return newhead;
        }
    }

    return NULL;
}

2. 문제를 두 개의 연결된 목록의 교차점으로 변환하고 교차점 문제를 찾습니다.

  • 아이디어
    연결된 목록에 링이 있으면 즉, 빠른 것과 느린 것이 만나는 경우 모임(만남 지점)을 연결 목록의 꼬리 노드로 간주하고 모임 다음의 다음 노드를 헤드 노드로 간주합니다. 새로운 연결 리스트의 새로운 헤드 노드와 기존 헤드 노드가 동시에 이동하도록 두 노드가 만날 때 만나는 지점은 연결 리스트의 링 부분의 헤드 노드입니다.
    다음과 같이:
    여기에 이미지 설명 삽입

  • 아래와 같이 코드 쇼


// 将找环头结点转化为两个链表相交问题
// 让slow与fast相遇的点做为两个链表的尾节点,meet的下一个节点作为其中一条链表的头结点
// 原链表的头结点是另一条链表的头结点
#include <stdlib.h>

struct ListNode* detectCycle(struct ListNode* head) {
    
    
    struct ListNode* fast = head;
    struct ListNode* slow = head;

    if (head == NULL)
    {
    
    
        return NULL;
    }
	//外层循环找是否有环
    while (fast && fast->next)
    {
    
    
        fast = fast->next->next;
        slow = slow->next;
		
        if (fast == slow)
        {
    
    
            struct ListNode* list2 = slow->next;
            struct ListNode* list1 = head;
			
            int len1 = 0;
            int len2 = 0;
			
            while (list2 != slow)
            {
    
    
                list2 = list2->next;
                len2++;
            }
			
            while (list1 != slow)
            {
    
    
                list1 = list1->next;
                len1++;
            }
			//区分长和短的链表
            int differ = abs(len1 - len2);
            struct ListNode* longlist = head;
            struct ListNode* shortlist = slow->next;
			
            if (len1 < len2)
            {
    
    
                longlist = slow->next;
                shortlist = head;
            }
			//长的链表先走
            while (differ--)
            {
    
    
                longlist = longlist->next;
            }
			//两个链表同时移动
            while (longlist != shortlist)
            {
    
    
                longlist = longlist->next;
                shortlist = shortlist->next;
            }

            return longlist;
        }
    }

    return NULL;
}

요약하다

위는 체인시계의 벨트링에 대한 저의 요약입니다.
여기에 이미지 설명 삽입

시청해주셔서 감사합니다! ! !

추천

출처blog.csdn.net/li209779/article/details/130817840