您现在的位置是:首页 >技术交流 >代码随想录算法训练营第四天|24. 两两交换链表中的节点 、19.删除链表的倒数第N个节点 、面试题 02.07. 链表相交 、142.环形链表II网站首页技术交流

代码随想录算法训练营第四天|24. 两两交换链表中的节点 、19.删除链表的倒数第N个节点 、面试题 02.07. 链表相交 、142.环形链表II

菜鸟的Zoom之旅 2024-06-17 10:19:00
简介代码随想录算法训练营第四天|24. 两两交换链表中的节点 、19.删除链表的倒数第N个节点 、面试题 02.07. 链表相交 、142.环形链表II

两两交换链表中的节点

题目链接:力扣

解题思路:虚拟头节点,然后进行模拟即可。

 我拿到这道题的时候,其实交换的思路是有的,但是首先没有设虚拟节点,这使得我的解答很乱,有很多if条件判断。其次卡哥的答案思路就很清晰,我的思路目标有点大,想使得一次交换完后1能直接指向4,这样也会使代码变得复杂。

这题就贴上卡哥的代码,供自己学习

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
        dummyHead->next = head; // 将虚拟头结点指向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; // cur移动两位,准备下一轮交换
        }
        return dummyHead->next;
    }
};
  • 时间复杂度:O(n)   空间复杂度:O(1)

删除链表的倒数第N个节点

题目链接:力扣

解题思路:
1、最开始想到的是暴力的方式:先算得总链表的长度,然后算出倒数第N个节点是正数第几个节点,再用删除链表节点的常规

操作。但是这样要用到两次遍历。
下面给出我的暴力解法:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {

        ListNode *VriNode = new ListNode(-1);   //虚拟头节点
        VriNode->next = head; 
        ListNode* Result = VriNode;
        int size = ListSize(VriNode);

        if(n>size)
        return nullptr;

        int Index = size-n-1;  //寻找的是删除节点的前一个节点

        while(Index--)
            VriNode = VriNode->next;

            VriNode->next = VriNode->next->next;

        return Result->next;
        
    }

    int ListSize(ListNode *head)
    {
        int size = 0;
        while(head!=nullptr){
            head = head->next;
            size++;
        }
        return size;
    }
};

快慢指针法:
设虚拟节点,快指针和慢指针同时指向虚拟节点,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
但是值得注意的是,因为要删除某个节点的话,就要使指针指向该节点的前一个节点,所以要想删除某一节点,就要是slow指向该节点的前一节点,所以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;
        while(n-- && fast != NULL) {
            fast = fast->next;
        }
        fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
        while (fast != NULL) {
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next; 
        
        // ListNode *tmp = slow->next;  C++释放内存的逻辑
        // slow->next = tmp->next;
        // delete nth;
        
        return dummyHead->next;
    }
};

面试题 02.07. 链表相交  

题目链接:力扣 

解题思路:
1、两次遍历,第一次用vector装链表A的节点,第二次遍历链表B中有没有A的节点,有则返回。
2、求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,此时就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。

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

环形链表II

题目链接:力扣

思路:环形链表的题目都是有套路的,这题在剑指offer里做过,其中给的思路是:
首先确定是否有环:快慢指针,慢指针走一步,快指针走两步,若能相遇则有环;
其次确定环的大小,就在上一步相遇的地方一直找节点的next,直到重新回到开始的节点,记录环数为n;
最后快慢指针重新回到起点,快指针先走n步,再快慢指针同时走一步,直到相遇,相遇的点就是环形链表的入口节点。
附上我的代码:

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {

        ListNode* low = head;
        ListNode* quick = head;
        int CircleSize = 0;

        while(1)
        {
            if(low)
            {low = low->next;  cout<<"low go"<<endl;}   //慢指针走一步
            else
            break;

            if(quick && quick->next)
            {quick = quick->next->next; cout<<"quick go"<<endl;}   //快指针走两步
            else
            break;

            if(low == quick)    //此时说明有环,计算出环的大小
            {
                ListNode *start = low;
                CircleSize = 1;
                while((low =low->next) != start)
                 {
                     CircleSize++;
                 }
                 cout<<CircleSize<<endl;
                 break;               
            }

        }

        if(CircleSize > 0)           //有环时候的代码
        { 
            low = head;
            quick = head;
            while(CircleSize--)
                quick= quick->next;   //链表中环有n个节点,则快指针多走一n步
            while(low != quick)    //之后以相同的频率向前走
            {
                low = low->next;
                quick = quick->next;
            }
            return low;
        }

        return nullptr;
        
    }
};

卡哥给出另一种思路:
确定有环后,慢指针回到起点,快指针还在之前相遇的环上,接着快慢指针同时走一步,直到快慢指针重合就是环形链表的入口节点。

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;
            // 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
            if (slow == fast) {
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2; // 返回环的入口
            }
        }
        return NULL;
    }
};


     

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。