代码随想录Day4|24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、02.07.链表相交、142.环形链表II

24. 两两交换链表中的节点

题目链接:Leetcode
文章讲解:代码随想录
视频讲解:bilibili

解题思路

  本题用虚拟节点比较方便处理,对于内部节点和头节点的处理可以统一起来,起初也预设了很多个指针变量来指向节点,但容易出现逻辑混乱,最后采取内部再新设置节点,在外部定义指向两个要交换节点的前一个节点的指针,这样实现起来逻辑清晰不容易出错。节点指向交换部分是链表部分的基础内容,实现起来很直接,但是如果还没理解链表这一结构实现起来可能会有点困难。

解法
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* headptr = new ListNode(0,head);
        ListNode* pre = headptr;
        while(pre->next && pre->next->next){
            ListNode* cur_1 = pre->next;	// 节点1
            ListNode* cur_2 = cur_1->next;	// 节点2
            cur_1->next = cur_2->next;
            cur_2->next = cur_1;
            pre->next = cur_2;		// 更新后继节点
            pre = cur_1;
        }
        ListNode* new_head = headptr->next;
        delete headptr;
        return new_head;
    }
};
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

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

题目链接:Leetcode
文章讲解:代码随想录
视频讲解:bilibili

解题思路

  一拿到这个题目的思路是用两个相对位置固定的指针来确定要删除的那个倒数第n个节点,首先确定两个节点的相对位置,在确定之后,先驱节点和当前节点同时向前遍历,直到先驱节点到达尾节点,此时当前节点就是要删除的目标节点,同时,需要保留当前节点的前一个节点来删除节点。

解法

  此处使用了虚设头节点,将头节点的处理和剩余节点的处理同一起来。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* headptr = new ListNode(0, head);
        ListNode* fir = headptr;
        ListNode* cur = headptr;
        ListNode* pre;
        while((n--)>1)
            fir = fir->next;
        while(fir->next){
            fir = fir->next;
            pre = cur;
            cur = cur->next;
        }
        pre->next = cur->next;
        delete cur;
        ListNode* new_head = headptr->next;
        delete headptr;
        return new_head;
    }
};
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

面试题 02.07. 链表相交

题目链接:Leetcode
文章讲解:代码随想录

解题思路

  一开始想到的就是最暴力的思路,双重循环遍历。用两个虚拟头节点统一对头节点和其他节点的操作,需要注意的是,如果指向的均为null并不能算作链表相交,需要额外判断。

解法1
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* tempA = new ListNode(0, headA);
        ListNode* tempB = new ListNode(0, headB);
        while(tempB->next){
            tempA->next = headA;
            while(tempA->next){
                if(tempB->next==tempA->next && tempA->next!=NULL){
                    ListNode* new_Node = tempA->next;
                    delete tempA;
                    delete tempB;
                    return new_Node;
                }
                tempA->next = tempA->next->next;
            }
            tempB->next = tempB->next->next;
        }
        return NULL;
    }
};
  • 时间复杂度: O ( m n ) O(mn) O(mn)
  • 空间复杂度: O ( 1 ) O(1) O(1)
解法2

  这道题其实有着隐含条件,即AB存在相交时,在相交之后不会再分叉,这样一来题目就变得简单了很多,只要在计算好两个链表的长度之后将较长的链表指针指向和较短链表长度相当的开始处,然后依次向下遍历比较即可。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* curA = headA;
        ListNode* curB = headB;
        int lengthA=0, lengthB=0;
        while(curA){
            lengthA++;
            curA = curA->next;
        }
        while(curB){
            lengthB++;
            curB = curB->next;
        }
        curA = headA;
        curB = headB;
        if(lengthA>=lengthB){
            int n = lengthA - lengthB;
            while(n--)
                curA = curA->next;
            while(curA && curB){
                if(curA==curB){
                    return curA;
                }
                curA = curA->next;
                curB = curB->next;
            }
        } else{
            int n = lengthB - lengthA;
            while(n--)
                curB = curB->next;
            while(curA && curB){
                if(curA==curB){
                    return curA;
                }
                curA = curA->next;
                curB = curB->next;
            }
        }
        return NULL;
    }
};
  • 时间复杂度: O ( n + m ) O(n+m) O(n+m)
  • 空间复杂度: O ( 1 ) O(1) O(1)

142.环形链表II

题目链接:Leetcode
文章讲解:代码随想录
视频讲解:bilibili

解题思路

  这条题判断环存在的方式有很多,例如深搜广搜,但还是快慢指针的方式最为简洁。此外,本题最大的难点在于找到环的入口,需要数学公式的推导模拟追及过程,这部分题解里面写的很详细。结论是从快慢指针在环中的相遇节点处环入口的距离与从头节点环入口的距离相等,由此可以找到环入口。

解法
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast  = head;
        ListNode* slow  = head;
        while(fast && fast->next){
            fast = fast->next->next;
            slow = slow->next;
            if(fast==slow){
                ListNode* index1 = fast;
                ListNode* index2 = head;
                int pos = 0;
                while(index1!=index2){
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;
            }
        }
        return NULL;
    }
};
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

今日总结

  补打卡()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值