代码训练营第4天|链表part2|leetcode24 两两交换|leetcode19 删除链表倒数节点|leetcode 面试题02.07 链表相交|leetcode 142 环形链表2

leetcode24:两两交换

文章讲解:leetcode24

leetcode19:删除倒数第N个节点

文章讲解:leetcode19

面试题02.07:链表相交

文章讲解:链表相交

leetcode142:环形链表2

文章讲解:环形链表2​​​​​​​

目录

1,leetcode24 两两交换链表中的节点

2,删除链表倒数第N个节点

3,面试题02.07 链表相交:

4,环形链表2


今天这些题都没见过,看上去第一眼没有太好的思路,加上这两天比较忙,就直接看文章和视频,总结一下算法,下周忙完了再回来看看

1,leetcode24 两两交换链表中的节点

代码训练营第4天|链表part2|leetcode24 两两交换|leetcode19 删除链表倒数节点|leetcode 面试题02.07 链表相交|leetcode 142 环形链表2_第1张图片

代码训练营第4天|链表part2|leetcode24 两两交换|leetcode19 删除链表倒数节点|leetcode 面试题02.07 链表相交|leetcode 142 环形链表2_第2张图片

这道题关键的地方在于理清楚应该是几个节点一组,每一组中间怎么动,cur指针怎么往下走三个问题。

1,从操作的角度看,两两交换,cur每次应该移动两个,相当于一组进行操作

2,怎么动和怎么往下走是一个问题。就是我该记录哪些节点,节点的指针怎么操作。

3,首先,cur指针应该是从虚拟头节点出发,每次都是在待操作的两个节点之前,这样才能保存住第一个待操作的节点的地址

4,到这里,思路已经清晰了,cur保存了第一个待操作的节点的去向,那只要保存着第二个待操作的节点的去向,也就是第一个节点(temp1),和这一组节点的下一个节点,也就是第三个(temp2),就记录了所有的指针,已经可以完整交换了

5,循环终止条件,不能简单的认为下一个是空就行,因为奇数个节点最后一个没办法操作,因此cur的下一个和下下个都为空才能进行移动。

6,画一下图,移动顺序就出来了

代码训练营第4天|链表part2|leetcode24 两两交换|leetcode19 删除链表倒数节点|leetcode 面试题02.07 链表相交|leetcode 142 环形链表2_第3张图片

代码按顺序写出来即可:

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* temp1 = cur->next;
            ListNode* temp2 = cur->next->next->next;

            cur->next = temp1->next;
            temp1->next->next = temp1;
            temp1->next = temp2;
            cur = cur->next->next;   
        }
        return dummyhead->next;

    }
};

核心是移动的思路,思路有了,代码自然好写。

2,删除链表倒数第N个节点

这道题数据结构考过,还是有点印象,快慢指针,一个在前先走N步,一个在后,前面的走到头后面的就到了。

没通过:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* slow = dummyHead;
        ListNode* fast = dummyHead;
        while(n-- && fast != NULL) {
            fast = fast->next;
        }
        while (fast != NULL) {
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next; 
        return dummyHead->next;
    }
};

看了半天,才反应过来,不能去指向第n个,slow应该去找第n-1个,才能删掉第n个,那么fast应该往前走n+1个就对了。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* slow = dummyHead;
        ListNode* fast = dummyHead;
        n = n+1;
        while(n-- && fast != NULL) {
            fast = fast->next;
        }
        while (fast != NULL) {
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next; 
        return dummyHead->next;
    }
};

还差一点,去掉的节点的内存应该释放掉。很明显,释放的应该是slow的下一个节点的位置和dummyhead。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        ListNode* slow = dummyHead;
        ListNode* fast = dummyHead;
        n = n+1;
        while(n-- && fast != NULL) {
            fast = fast->next;
        }
        while (fast != NULL) {
            fast = fast->next;
            slow = slow->next;
        }
        ListNode* ondelete = slow->next;
        slow->next = slow->next->next; 
        delete ondelete;
        ListNode* result = dummyHead->next;
        delete dummyHead;
        return result;
    }
};

这样就完备了。

3,面试题02.07 链表相交:

这道题我只有O(nm)的思路,挨个遍历比较地址是否相同。但显然应该有更好的的算法

代码训练营第4天|链表part2|leetcode24 两两交换|leetcode19 删除链表倒数节点|leetcode 面试题02.07 链表相交|leetcode 142 环形链表2_第4张图片

确实,这道题关键的破题点在于,两个链表相交后的部分应该是一致的,这意味着,如果两个链表相交了,相交的节点一定在一致的几个中。而找一致的方法就是,链表的末尾对齐。

代码如下:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA = headA;
        ListNode* curB = headB;
        int lenA = 0, lenB = 0;
        while (curA != NULL) {
            lenA++;
            curA = curA->next;
        }
        while (curB != NULL) { 
            lenB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;
        if (lenB > lenA) {
            swap (lenA, lenB);
            swap (curA, curB);
        }
        int gap = lenA - lenB;
        while (gap--) {
            curA = curA->next;
        }
        while (curA != NULL) {
            if (curA == curB) {
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
    }
};

和卡哥的代码是一致的。

4,环形链表2

链表找环这个思路我见过,就是一个指针一次走两步,一个指针一次走一步,如果相遇了,就说明有环,问题在于如何找到环的入口:

代码训练营第4天|链表part2|leetcode24 两两交换|leetcode19 删除链表倒数节点|leetcode 面试题02.07 链表相交|leetcode 142 环形链表2_第5张图片

在这道题里,z+y是环的长度,相遇的时候,两个链表的数量关系是

slow:x+y

fast:x+n*(y+z)

他们相遇,即

2*slow = fast =>   x = (n - 1) (y + z) + z 

这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点

这道题核心就在于这个数学证明。思路很简单,先一快一慢,相遇了,就两个指针同时前进,再相遇时就是环形节点的入口。

这个数学过程还得在理解一下

代码如下:

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;
    }
};

和卡哥的是一致的。

5,总结

1,今天这几道题的思路很重要,交换方法,寻找方法,证明过程还需要再理解理解。

你可能感兴趣的:(算法,leetcode,链表)