算法训练营Day4|24.两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II

算法训练营Day4

24.两两交换链表中的节点,19.删除链表的倒数第N个节点,面试题 02.07. 链表相交,142.环形链表II

题目

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

题目链接:https://leetcode.cn/problems/swap-nodes-in-pairs/description/
文章讲解:https://programmercarl.com/0024.%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9.html

思路

链表题

  1. 双指针:从头开始,每次让cur指针指向位置的后两个节点交换位置,注意指针交换顺序
    易错点,执行结束后需要更新cur,cur指向1节点,因为他是交换1、2节点之后后面的一个节点
  2. 递归:需要画图,否则容易让指针丢失
    函数返回的是交换两个节点之后的头指针,递归结束的条件是只有一个节点或没有节点,交换完成后需要向后移动两个指针
    在这里插入图片描述
//双指针
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode *dummy = new ListNode(0, head);
        ListNode *cur = dummy;
        while(cur -> next != nullptr && cur -> next -> next != nullptr){
            ListNode *node1 = cur -> next, *node2 = cur -> next -> next -> next;
            cur -> next = node1 -> next;
            node1 -> next -> next = node1;
            node1 -> next = node2;
            cur = node1; //更新cur为两个节点中后面那个节点
        }
        return dummy -> next;
    }
};
// 递归
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(head == nullptr || head -> next == nullptr) return head;
        ListNode *newHead = head -> next; //需要用newHead存储
        head -> next = swapPairs(head -> next -> next);
        newHead -> next = head;
        return newHead;
    }
};

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

题目链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/
文章讲解:https://programmercarl.com/0019.%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E7%9A%84%E5%80%92%E6%95%B0%E7%AC%ACN%E4%B8%AA%E8%8A%82%E7%82%B9.html

思路
  1. 双指针:快慢指针,快指针比慢指针快n个单位,当快指针为NULL时,慢指针所指就是倒数第n个节点;要删除倒数第n个节点,所以慢指针要指向前一个节点,所以慢指针从dummy开始,快指针从dummy->next开始,两个指针之间间隔n+1个单位,保证慢指针指向前一个节点
  2. 栈:运用栈,将所有节点指针压入栈,再弹出n个,栈顶即为要删除节点的前一个节点指针
代码
//双指针
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(0, head);
        ListNode *slow = dummy, *fast = dummy -> next; //初始满足间隔一个单位
        // 题意保证n合理
        while (n--) {
            fast = fast -> next;
        }
        while (fast != nullptr) {
            slow = slow -> next;
            fast = fast -> next;
        }
        slow -> next = slow -> next -> next;
        return dummy->next;
    }
};
//栈
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(0, head);
        stack<ListNode*> stk;
        ListNode* cur =
            dummy; // 如果删除第一个节点,需要它的前一个节点,所以从dummy开始压入栈中
        while (cur != nullptr) {
            stk.push(cur);
            cur = cur->next;
        }
        while (n--) {
            stk.pop();
        }
        ListNode* pre = stk.top();
        pre->next = pre->next->next;
        return dummy -> next;
    }
};

面试题 02.07. 链表相交

题目链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/
文章讲解:
https://programmercarl.com/%E9%9D%A2%E8%AF%95%E9%A2%9802.07.%E9%93%BE%E8%A1%A8%E7%9B%B8%E4%BA%A4.html

思路

数值相同,不代表指针相同

  1. 模拟法:若两个链表有交点,则链表尾部应该对齐。求两个链表长度,在长的链表中将指针移动到与短的链表对齐的位置同时向后遍历,找到交点
  2. 双指针:一个很巧妙的方法,假设链表有相交部分且相交部分为c,链表A中不相交部分为a,链表B中不相交部分为b,则链表A为a+c,链表B为b+c;先遍历链表A再遍历链表B,a+c+b+c与先遍历链表B再遍历链表A,b+c+a+c一定会有最后的c相交,所以让一个指针先遍历A再遍历B,一个先遍历B再遍历A即可;
    这里用temp1 == nullptr而不是temp1 -> next在于当两个链表没有交点,最后两个指针都会为空,这样也会退出循环且返回nullptr
  3. 哈希表:看了题解才想到,将链表A中所有节点地址存入哈希表,遍历链表B,链表B中的第一个在哈希表中存在节点地址,即为相交的节点地址
代码
//模拟法
class Solution {
public:
    ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
        int a = 0, b = 0;
        ListNode *temp1 = headA, *temp2 = headB;
        while (temp1 != nullptr) {
            a++;
            temp1 = temp1->next;
        }
        while (temp2 != nullptr) {
            b++;
            temp2 = temp2->next;
        }
        int k = abs(a - b);
        temp1 = headA, temp2 = headB;
        if (a > b)
            while (k--)
                temp1 = temp1->next;
        else
            while (k--)
                temp2 = temp2->next;
        while (temp1 != nullptr) {
            if (temp1 == temp2)
                return temp1;
            else {
                temp1 = temp1->next;
                temp2 = temp2->next;
            }
        }
        return NULL;
    }
};
//双指针
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *temp1 = headA, *temp2 = headB;
        while(temp1 != temp2){
            temp1 = temp1 == nullptr? headB : temp1 -> next;
            temp2 = temp2 == nullptr? headA : temp2 -> next;
        }
        return temp1;
    }
};
//哈希表
class Solution {
public:
    ListNode* getIntersectionNode(ListNode* headA, ListNode* headB) {
        unordered_set<ListNode*> visited;
        ListNode* temp = headA;
        while (temp != NULL) {
            visited.insert(temp);
            temp = temp->next;
        }
        temp = headB;
        while (temp != NULL) {
            if (visited.count(temp) == 1)
                return temp;
            temp = temp -> next;
        }
        return NULL;
    }
};

142.环形链表II

题目链接:https://leetcode.cn/problems/linked-list-cycle-ii/description/
文章讲解:https://programmercarl.com/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II.html

思路
  1. 哈希表:有了上一题的经验,很快想到用哈希表存储,遍历链表,如果哈希表中已存在这个节点地址,则为入环的第一个节点
  2. 双指针:先讲结论,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环
    从头结点出发一个指针,从相遇节点也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是环形入口的节点,讲解中证明快慢指针一定在慢指针进入环的第一圈相遇,这里不再赘述
    在这里插入图片描述
代码
//哈希表
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        unordered_set<ListNode *> visited;
        ListNode *temp = head;
        while(temp != nullptr){
            if(visited.count(temp) != 0) return temp;
            visited.insert(temp);
            temp = temp -> next;
        }
        return nullptr;
    }
};
//双指针
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *slow = head, *fast = head;
        //前者保证head为NULL时退出循环
        while(fast != nullptr && fast -> next != nullptr){
            slow = slow -> next;
            fast = fast -> next -> next;
            if(slow == fast){
                ListNode *temp = head;
                while(temp != slow){
                    temp = temp -> next;
                    slow = slow -> next;
                }
                return temp;
            }
        }
        return nullptr;
    }
};

补充内容

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值