콘텐츠
1. 연결 리스트의 회문 구조
- 직접 링크:
- 주제:
- 아이디어:
중간 노드를 찾아서 반전시키는 방법은 매우 영리하다. 중간 노드를 찾는 것과 반전의 두 가지 내용은 이전 블로그 포스트에서 자세히 설명했다. 반전 후 노드와 새로운 헤드 노드가 값인지 판단한다. 같음, 순회 후 같으면 true를 반환하고 그렇지 않으면 false를 반환합니다.
- 코드 쇼는 아래와 같습니다.
class PalindromeList { public: //第一步:找到中间节点 struct ListNode* middleNode(struct ListNode* head) { struct ListNode* slow = head; struct ListNode* fast = head; while (fast && fast->next) { slow = slow->next; fast = fast->next->next; } return slow; } //第二部:逆置从中间节点开始往后的数据,并返回新的头节点 struct ListNode* reverseList(struct ListNode* head) { if (head == NULL) { return NULL; } struct ListNode* n1 = head; struct ListNode* n2 = head->next; struct ListNode* n3 = NULL; while (n1) { n1->next = n3; n3 = n1; n1 = n2; if (n2) { n2 = n2->next; } } return n3; } bool chkPalindrome(ListNode* A) { // write code here struct ListNode* mid = middleNode(A); struct ListNode* rHead = reverseList(mid); while (A && rHead) { if (A->val == rHead->val) { A = A->next; rHead = rHead->next; } else { return false; } } return true; } };
2. 교차 연결 리스트
- 직접 링크:
- 주제:
- 아이디어:
방법 1: A 연결 목록의 각 요소를 B 연결 목록과 차례로 비교하여 같으면 교집합, 첫 번째 같으면 교집합입니다.
이 방법의 시간복잡도는 O(M*N)이며, 이 방법의 장점은 교차하는 노드가 교차 여부를 판단한 후 직접 구한다는 점입니다. 참고: 비교할 때 값을 비교할 수 없으며, 주소를 비교하십시오. 이 방법의 단점은 효율이 상대적으로 낮고 개선할 수 있다는 점이다.
방법 2: 원래 연결 목록 탐색 + 빠르고 느린 포인터
먼저 두 연결 목록이 교차하는지 확인합니다.
두 개의 연결 리스트를 마지막으로 차례로 탐색하여 두 개의 꼬리 노드의 주소가 동일한지 확인하고 같으면 교차해야 합니다. 이 때의 시간 복잡도는 O(M+N)으로 첫 번째 방법보다 훨씬 최적화되어 있습니다.
첫 번째 교차로 찾기
이전 단계를 수행한 후 연결 목록이 교차한다는 것을 알고 교차점을 요청해야 합니다.먼저 두 연결 목록의 길이 La와 Lb를 구합니다. 그런 다음 두 개의 연결 리스트의 선두를 가리키는 두 개의 포인터를 정의하고 길이가 긴 연결 리스트 포인터가 먼저 | La-Lb | 단계를 수행하고 두 개의 연결 리스트 포인터가 함께 이동하도록 합니다. 발견, 그 다음 정지, 이때 교차로를 찾으십시오. 이 경우의 시간 복잡도도 O(M+N)입니다.
- 코드 쇼는 아래와 같습니다.
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) { int lenA = 0; int lenB = 0; struct ListNode* cur1 = headA; struct ListNode* cur2 = headB; //1、判断是否相交 while (cur1->next) { cur1 = cur1->next; lenA++; } while (cur2->next) { cur2 = cur2->next; lenB++; } //若相交,进入if判断 if (cur1 == cur2) { struct ListNode* shortList = headA, * longList = headB; if (lenA > lenB) { longList = headA; shortList = headB; } int num = abs(lenA - lenB); //让长的链表先走差距步 while (num--) { longList = longList->next; } while (shortList && longList) { if (shortList == longList) { return shortList; } //此时两链表一起走 else { shortList = shortList->next; longList = longList->next; } } } //若不相交,则直接返回空 return NULL; }
3. 임의의 포인터가 있는 연결 리스트 복사
- 직접 링크:
- 주제:
- 아이디어:
방법 1: 직접 복사(malloc) + 상대 거리 찾기
원래 연결 리스트의 첫 번째 요소를 가리키도록 포인터 cur를 정의하고 7의 첫 번째 값을 가리키도록 cur를 정의한 다음 7을 malloc하고 cur가 뒤로 이동하고 malloc을 계속한 다음 꼬리를 삽입하여 횡단합니다. . 새로운 연결 목록이 복사됩니다.핵심은 새 연결 목록의 임의 포인터를 처리하는 방법입니다.여기서 상대 거리를 찾는 방법을 사용하여 원래 연결 목록의 임의 지점 수를 찾은 다음 새로운 연결 리스트는 포인트의 수에 해당합니다. 그러나 이 방법은 너무 복잡하고 시간 복잡도가 O(N^2)에 이릅니다. 너무 추천하지 않음
법칙 2:
먼저 원본 노드 뒤에 복사 노드를 연결합니다.
struct Node* cur = head; //1、拷贝节点链接在原节点后面 while (cur) { struct Node* copy = (struct Node*)malloc(sizeof(struct Node)); copy->val = cur->val; copy->next = cur->next; cur->next = copy; cur = cur->next->next; }
다음으로, 링크 복사 노드의 랜덤은 원본 노드의 랜덤 뒤에 있습니다.
예를 들어 숫자 13을 복사하면 복사된 13의 무작위가 원본 13의 다음 무작위입니다. 원래 연결 목록에서 13의 임의의 값은 7을 가리키고 이제 복사된 13의 임의의 값이 복사된 7을 가리키고 싶기 때문입니다. 원래 연결 목록에서 7 중 다음은 복사된 7을 가리킵니다. 요약하면 복사된 13의 임의의는 원본 13의 임의의 다음입니다. 등등.
//2、更新拷贝节点的random cur = head; while (cur) { struct Node* copy = cur->next; if (cur->random == NULL) { copy->random = NULL; } else { copy->random = cur->random->next; } cur = cur->next->next; }
마지막으로 원본 연결 목록을 복원하고 복사한 연결 목록을 함께 연결합니다.
//3、拷贝节点剪下来,链接到一起 cur = head; struct Node* copyTail = NULL; struct Node* copyHead = NULL; while (cur) { struct Node* copy = cur->next; struct Node* next = copy->next; cur->next = next; if (copyTail == NULL) { copyHead = copyTail = copy; } else { copyTail->next = copy; copyTail = copyTail->next; } cur = next; } return copyHead; }
- 전체 코드는 다음과 같습니다.
struct Node* copyRandomList(struct Node* head) { struct Node* cur = head; //1、拷贝节点链接在原节点后面 while (cur) { struct Node* copy = (struct Node*)malloc(sizeof(struct Node)); copy->val = cur->val; copy->next = cur->next; cur->next = copy; cur = cur->next->next; } //2、更新拷贝节点的random cur = head; while (cur) { struct Node* copy = cur->next; if (cur->random == NULL) { copy->random = NULL; } else { copy->random = cur->random->next; } cur = cur->next->next; } //3、拷贝节点剪下来,链接到一起 cur = head; struct Node* copyTail = NULL; struct Node* copyHead = NULL; while (cur) { struct Node* copy = cur->next; struct Node* next = copy->next; cur->next = next; if (copyTail == NULL) { copyHead = copyTail = copy; } else { copyTail->next = copy; copyTail = copyTail->next; } cur = next; } return copyHead; }