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

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

// O(n)
// 思路: 关键是理清楚三个步骤的先后顺序,cur指针一定要指向待交换的两个节点的前一个接点,因此要设置虚拟头节点更好的操作,其次是要保存后面的节点防止断链
// 遍历终止条件: 当cur->next == NULL时结束说明链表节点个数时数。当cur->next->next == NULL时说明链接节点个数是奇数。

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode * _dummyHead = new ListNode(0);  // 设置虚拟头节点
        ListNode *cur = _dummyHead;  // 遍历指针
        _dummyHead->next = head;
        while (cur->next != NULL && cur->next->next != NULL) {
            ListNode *tmp = cur->next;  // 保存临时节点
            ListNode *tmp1 = tmp->next->next;

            cur->next = cur->next->next;  // 步骤1,断掉虚拟头节点和head节点之间的链
            cur->next->next = tmp;  // 步骤2 
            cur->next->next->next = tmp1;  // 步骤3

            cur = cur->next->next;  // cur向后移动2个位置,进行下一轮交换
        } 
        return _dummyHead->next;

    }
};

在这里插入图片描述
19. 删除链表的倒数第 N 个结点

// 双指针法
// O(n)
// 思路: 先让fast指针先走n+1步,因为只有先走n+1步才能同时移动的时候**slow才能指向删除节点的上一个节点(方便做删除操作)**
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode *_dummyHead = new ListNode(0);
        _dummyHead->next = head;
        ListNode *fast = _dummyHead;
        ListNode *slow = _dummyHead;
        while (n-- && fast->next != NULL) {  // 先让fast先走n步
            fast = fast->next;
        }
        fast = fast->next;  // 再多走1步,共n+1步
        while (fast != NULL) {  // 保证fast和slow同步移动,找到倒数第N个节点
            fast = fast->next;
            slow = slow->next;
        }
        
        // slow->next = slow->next->next;
        ListNode *tmp = slow->next;  // 释放内存
        slow->next = slow->next->next;  // 删除倒数第N个节点
        delete tmp;
        return _dummyHead->next;
    }
};

面试题 02.07. 链表相交

// O(n+m)
// 思路: 求两个链表交点节点的指针,交点不是数值相等,而是指针相等。
// 求两个链表的长度差,让curA始终位最长的哪个链表,
// 利用长度差先让curA移动gap个长度,再让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;  // 让curA重新回到headA,没有这两句回爆空指针错误
        curB = headB;

        if (lenB > lenA) {  //  // 让curA为最长链表的头,lenA为其长度
            swap (lenA, lenB);  
            swap (curA, curB);
        }

        int gap = lenA - lenB;  // 长度差

        while (gap--) {  // 让curA和curB在同一起点上(末尾位置对齐)
            curA = curA->next;
        }
        while (curA != NULL) {  // 遍历curA 和 curB,遇到相同则直接返回
            if (curA == curB) {
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return NULL;
        
    }
};

142.环形链表I
在这里插入图片描述

// 双指针法
// O(n)
// 思路: 见解析https://programmercarl.com/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II.html#%E6%80%9D%E8%B7%AF
// 考查两点
// 一、判断链表是否环: 可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
// fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇,这是毋庸置疑的。
// 二、如果有环,如何找到这个环的入口 
// **从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。**
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *fast = head;
        ListNode *slow = head;
        while (fast != NULL && fast->next != NULL) {
            fast = fast->next->next;  // fast每次走两步
            slow = slow->next;  // slow每次走一步
            if (fast == slow) {  // 相遇时
                ListNode *index1 = fast;  // 相遇时重新定义一个指针指向相遇时的节点
                ListNode *index2 = head;  // 定义一个指针指向头节点
                while (index1 != index2) {  // x=z,看图
                    index1 = index1->next;  // 让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。
                    index2 = index2->next;
                }
                return index1;
            }
        }
        return NULL;
        
    }
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值