오늘 배운 기사 및 비디오 링크
24 기사 링크: 링크
24 비디오 설명 링크: 링크
19 기사 링크: 링크
19 비디오 설명 링크: 링크 인터뷰 질문 02.07 기사 링크: 링크 인터뷰 질문 02.07 비디오 설명 없음 142 기사 링크: 링크 142 비디오 설명 링크: 링크
24. 연결 리스트의 노드를 둘씩 교환
제목보고 첫생각
제목:
연결 리스트를 주고, 그 안의 인접 노드를 둘씩 교환하고, 교환 후 연결 리스트의 헤드 노드를 반환합니다. 노드 내부의 값을 수정하지 않고(즉, 노드 스왑만) 이 연습을 완료해야 합니다.
다음과 같은 아이디어가 있습니다:
쌍별 교환을 완료하기 위해 두 개의 인접한 노드의 교환을 루프에 작성합니다. 루프의 핵심은 노드 포인터의 순서 와 임시 노드 의 레코드 를 수정하는 것입니다 .
코드 변덕을 읽은 후의 생각
코드 랜덤 레코드는 그림을 그려서 포인터의 작동 단계를 보여주고 포인터의 작동 순서는 한 눈에 명확합니다.
처음에 cur은 가상 헤드 노드를 가리키고 다음 세 단계를 수행합니다.
작업 후 연결 목록은 다음과 같습니다.
위의 세 단계를 통해 인접한 두 요소의 교환을 완료합니다.
다음으로 전체 연결 목록에서 인접 노드의 쌍 교환을 완료하기 위해 이 작업을 반복하기만 하면 됩니다.
구현 중 발생하는 어려움
노드 2가 노드 3과 연결이 끊어져 2단계에서 노드 1의 다음 노드가 노드 3의 위치를 찾을 수 없기 때문에 임시 노드가 처음에 기록되지 않았습니다 .
따라서 노드 포인터에서 작업하기 전에 임시 노드를 기록 해야 합니다 .
ListNode* cur = dummyHead;
가상 헤드 노드를 가리키도록 포인터 커서 설정
코드
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0);//设置一个虚拟头节点
dummyHead->next = head;
ListNode* cur = dummyHead;
while(cur->next != nullptr && cur->next->next != nullptr){
ListNode* tmp = cur->next;
ListNode* tmp1 = cur->next->next->next;
//以下三步与图中操作相对应
cur->next = cur->next->next;
cur->next->next = tmp;
cur->next->next->next = tmp1;
cur = cur->next->next;
}
return dummyHead->next;
}
};
19. 연결된 목록의 마지막 N 노드 삭제
제목보고 첫생각
제목:
연결된 목록을 제공하고 연결된 목록의 n
두 번째 하고 연결된 목록의 헤드 노드를 반환합니다.
나는 다음과 같은 아이디어를 가지고 있습니다:
연결 리스트에서 지정된 상호 노드를 삭제하는 것은 빠르고 느린 포인터를 통해 수행할 수 있습니다. 빠른 포인터가 n 단계 먼저 이동하도록 한 다음 느린 속도가 빠른 속도에 도달할 때 동시에 빠르게 이동하기 시작합니다. 연결 리스트의 끝에서 slow 가 가리키는 포인터를 삭제하면 됩니다.
코드 변덕을 읽은 후의 생각
코드 랜덤 녹음은 다음 단계로 나뉩니다.
- 빠른 포인터와 느린 포인터를 정의합니다. 초기 값은 그림과 같이 가상 헤드 노드입니다.
- Fast는 먼저 n+1 단계를 수행합니다. 왜 n+1 단계입니까? 그림과 같이 동시에 이동할 때만 slow가 삭제된 노드의 이전 노드를 가리킬 수 있기 때문입니다.
- 그림과 같이 fast가 끝을 가리킬 때까지 fast 및 slow 이동을 동시에 수행합니다.
- 그림과 같이 slow가 가리키는 다음 노드를 삭제합니다.
구현 중 발생하는 어려움
구현할 때 우리는 빠르게 n+1 단계를 이동하도록 한 다음 빠르고 느린 포인터를 동시에 이동하여 slow가 삭제할 노드의 이전 노드를 가리킬 수 있도록 주의해야 합니다.
그런 다음 슬로우의 다음 노드가 삭제할 노드의 다음 지점을 가리키도록 하여 노드 삭제를 완료합니다.
코드
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
n++;
while(n-- && fast != NULL){
fast = fast->next;
}
while (fast != NULL){
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return dummyHead->next;
}
};
인터뷰 질문 02.07 연결 리스트 교차점
제목보고 첫생각
제목 설명: 두
개의 단일 연결 목록의 헤드 노드가 주어졌고 두 개의 단일 연결 목록이 교차하는 시작 노드를 찾아서 반환하십시오. 두 개의 연결된 목록 사이에 교차가 없으면 를 반환합니다 .headA
headB
null
다음 아이디어가 있습니다.
포인터가 교차하려는 경우 교차하는 노드에서 시작하는 노드의 요소는 모두 동일하고 길이 차이가 n이라고 가정하고 두 연결 목록 간의 길이 차이를 찾은 다음 더 긴 연결 리스트의 포인터가 n+ 1 위치로 이동한 다음, 두 연결 리스트의 포인터가 교차점을 찾기 위해 만날 때까지 두 연결 리스트의 포인터를 동시에 이동합니다.
코드 변덕을 읽은 후의 생각
생각은 나랑 똑같다
구현 중 발생하는 어려움
어려움 없음
코드
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
while (curA != NULL) {
// 求链表A的长度
lenA++;
curA = curA->next;
}
while (curB != NULL) {
// 求链表B的长度
lenB++;
curB = curB->next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
142. 순환 연결 목록 II
제목보고 첫생각
제목 설명:
연결된 목록의 헤드 노드가 주어지면 head
연결된 목록이 링에 진입하기 시작하는 첫 번째 노드를 반환합니다. 연결된 목록이 비주기적이면 null을 반환합니다.
다음과 같은 아이디어가 있습니다.
- 링이 있는지 판단: 빠르고 느린 포인터 방법을 사용하여 빠르고 느린 포인터를 정의하고 헤드 노드에서 시작하여 빠른 포인터는 한 번에 두 노드를 이동하고 느린 포인터는 한 번에 한 노드를 이동합니다. 도중에 두 개의 포인터가 만나면 연결된 목록에 링이 있습니다.
- 링의 입구를 찾는 방법: 링의 입구 노드 와 포인터가 만나는 노드의 두 가지 핵심 포인트가 있습니다 .
헤드 노드에서 링 진입 노드까지의 노드 수를 x, 링 진입 노드에서 두 포인터가 만나는 노드까지의 노드 수를 y, 만남 노드에서 링 진입 노드까지의 수를 가정합니다. 지입니다.
두 포인터가 만났을 때 느린 포인터가 통과한 노드의 수는 x+y이고 빠른 포인터가 통과한 노드의 수는 x+y+n(y+z)입니다. 여기서 n은 노드의 수입니다
. 빠른 포인터가 링에서 처음으로 느린 포인터를 만났다는 것을 링에서 n번 원을 그리며 걷습니다.
(y + z)는 원의 노드 수입니다.
그리고 fast포인터는 2스텝, slow포인터는 1스텝(물리적으로 fast의 속도는 slow의 2배임을 알 수 있음)이므로 fast가 이동한 거리는 slow의 2배가 된다. (x + y) * 2 = x + y + n (y + z
)
수식을 제거하고 전치하면 다음이 생성됩니다.
x = n (y + z) - y
수식에서 뺄셈을 피하기 위해 n(y+z)에서 하나의 (y+z)를 제안하고, 수식을 마친 후 수식은 다음과 같습니다. x = (n - 1) (y + z) +
z
이 공식의 의미는 포인터가 헤드 노드에서 시작되고 포인터도 회의 노드에서 시작된다는 것입니다. 이 두 포인터는 한 번에 하나의 노드에만 이동하므로 두 포인터가 만날 때 노드입니다. 링 입구.
코드 변덕을 읽은 후의 생각
코드 카프리스의 애니메이션은 포인터가 만나는 상황과 원형 입구를 찾는 방법을 보다 직관적으로 보여줍니다.
고속 포인터와 저속 포인터의 만남:
원형 연결 리스트의 키 노드 간 거리의 개략도:
진입 노드를 찾는 개략도:
구현 중 발생하는 어려움
이 질문을 본 것은 이번이 두 번째이기 때문에 어떤 인상이 있습니다. 그런데 이 질문을 처음 봤을 때, 링에서 처음 만났을 때 왜 x + 몇 개의 원의 길이 + y가 아니라 느린 걸음의 수 x+y가 되는 걸까요?
이유는 다음과 같습니다.
첫째, 슬로우가 링에 진입할 때 패스트는 이미 링에 진입한 것입니다.
슬로우가 링 입구에 들어가고 패스트도 링 입구에 있으면 아래 그림과 같이 링을 직선으로 확장합니다. 슬로우와 패스트가 링 입구에서 걷기 시작하는 것을 볼 수 있습니다
. 동시에 반지, 그럼 그들은 반드시 반지의 입구 3에서 만날 것입니다. 이때 느리게 한 바퀴, 빠르게 두 바퀴를 걸었습니다.
slow가 링에 진입하면 그림과 같이 fast가 링의 모든 위치에 있다고 가정합니다.
그런 다음 fast 포인터가 링 항목 3에 도달하면 k + n 노드가 이미 이동했으며 그에 따라 slow가 이동해야 합니다(k + n) / 2 노드.
k < n이므로 (k + n) / 2도 n보다 작아야 합니다.
이것은 천천히 걷는 첫 번째 고리에서 빠르게 만날 것임을 의미합니다.
그리고 빠름은 한 번에 두 개의 노드를 이동하고 느린 것은 한 번에 한 노드를 이동하므로 상대 속도는 빠른 추적이 한 번에 한 노드의 속도로 느려지기 때문에 느리게 건너뛰지 않습니다 .
코드
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next !=NULL){
slow = slow->next;
fast = fast->next->next;
if(slow==fast){
ListNode* index1 = fast;
ListNode* index2 = head;
while(index1!=index2){
index1 = index1->next;
index2 = index2->next;
}
return index2;
}
}
return NULL;
}
};
오늘 수확
1. pairwise 교환 연결 목록에서 임시 노드 레코드의 문제를 기억하십시오.
2. Linked List의 끝에서 두 번째 노드를 삭제함으로써 이중 포인터 방식에 대한 이해가 다시 깊어집니다.
3. Linked List 교차점의 특성을 검토한다.
4. 순환 연결 리스트 이 질문은 더 어렵습니다. 이 질문을 통해 순환 연결 리스트 처리에 대한 이해를 심화하십시오.
오늘 공부시간은 3시간
이 기사의 사진은 모두 Carl의 코드 카프리스에서 가져온 것이며 대단히 감사합니다.