代码随想录算法训练营第4天 | 链表2

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

题目: 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
链接: https://leetcode.cn/problems/swap-nodes-in-pairs/
思路:

  1. 设置dummyNodedummyNode->next 指向head
  2. currNodedummyNode 开始处理,dummyNode->next指向2,2指向1,1指向3。处理过程中存在暂存下一个节点的操作。
  3. 循环条件:currNode->nextcurrNode->next->next都不为空。
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyNode = new ListNode(0);
        dummyNode->next = head;
        ListNode* currNode = dummyNode;
        while(currNode->next != nullptr && currNode->next->next != nullptr){
            ListNode* tempNode = currNode->next;
            currNode->next = tempNode->next;
            ListNode* tempNode1 = currNode->next->next;
            currNode->next->next = tempNode;
            tempNode->next = tempNode1;
            currNode = currNode->next->next;
        }
        return dummyNode->next;
    }
};

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

题目: 给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
链接: https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
思路:

  1. 无法直接通过内置库函数获得链表的长度,所以需要依靠遍历去确定倒数第N个节点在哪里。
  2. 双指针法:定义一个指针fast,一个指针slowfast先移动n步,当fast移动到链表末尾的nullptr时,slow刚好指向倒数第N个节点。(当fast移动到链表的最后一个元素时,操作slow->next的指向即可删除倒数第n个节点。)
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
       ListNode* dummyNode = new ListNode(0);
       dummyNode->next = head;
       ListNode* fastNode = dummyNode;
       ListNode* slowNode = dummyNode;
       while(n--){
           fastNode = fastNode->next;
       }
       while(fastNode->next != nullptr){
           slowNode = slowNode->next;
           fastNode = fastNode->next;
       }
       slowNode->next = slowNode->next->next;
       return dummyNode->next;
    }
};

面试题02.07. 链表相交

题目: 给你两个单链表的头节点headA和headB,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回null。题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
链接: https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci
思路:
【双指针法】两个链表的交点,不是数值相等,而是指针相等。
求出两个链表的长度,并求出两个链表长度的差值,然后让链表A的指针currA移动到使链表A和链表B末尾对齐的位置。此时再进行currA和currB的比较,如果遇到currA==currB则找到交点。

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* currA = headA;
        ListNode* currB = headB;
        int lenA  = 0, lenB = 0;
        while(currA != nullptr){
            lenA++;
            currA = currA->next;
        }
        while(currB != nullptr){
            lenB++;
            currB = currB->next;
        }
        currA = headA;
        currB = headB;
        if(lenA < lenB){
            swap(currA, currB);
            swap(lenA, lenB);
        }
        int diff = lenA - lenB;
        while(currA != nullptr && diff--){
            currA = currA->next;
        }
        while(currA != nullptr){
            if(currA == currB){
                return currA;
            }
            currA = currA->next;
            currB = currB->next;
        }
        return nullptr;
    }
};

时间复杂度: O(m+n)
空间复杂度: O(1)
【哈希集合】使用哈希集合存储链表节点,首先遍历链表A,并将链表A中的每个节点都加入哈希集合中。然后遍历链表B,对于每个节点,判断该节点是否在哈希集合中。

  1. 如果当前节点不在哈希集合中,则继续遍历下一个节点;
  2. 如果当前的节点在哈希集合中,则后面的节点都在哈希集合中,即从当前节点开始的所有节点都在两个链表相交的部分,因此在链表B中遍历到的第一个在哈希集合中的节点就是两个链表相交的节点,返回该节点。
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        unordered_set<ListNode*> set;
        ListNode* currNode = headA;
        while(currNode != nullptr){
            set.insert(currNode);
            currNode = currNode->next;
        }
        ListNode* resNode = headB;
        while(set.find(resNode) == set.end() && resNode != nullptr){
            resNode = resNode->next;
        }
        return resNode;
    }
};

142.环形链表II

题目: 给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
链接: https://leetcode.cn/problems/linked-list-cycle-ii
思路:
【哈希表法】一个节点一个节点地存,遇到存过的就是环形起点。

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        unordered_set<ListNode*> set;
        ListNode* currNode = head;
        while(currNode != nullptr){
            if(set.find(currNode) == set.end()){
                set.insert(currNode);
                currNode = currNode->next;
            }
            else{
                return currNode;
            }
        }
        return nullptr;
    }
};

【双指针法】

  1. 定义两个指针fastslowfast一次走两步,slow一次走一步。
  2. 如果有环,fastslow两个指针必定会在环中相遇。相遇是满足
    x + n(y + z) + y = 2(x + m(y+z) + y)
    x = (k - 1) (y + z) + z
  3. fastslow两个指针在环中相遇的位置设为index1 ,原点位置设为index2,由上述公式可推出
    x + k'(y + z) = z + k'(y + z),所以index1index2同时向前移动,每次移动一步,必定会在环的入口处相遇。
  4. 思路为先求fastslow两个指针在环中相遇的位置,再求index1index2相遇的位置。
  5. 注意:(注释中已标注)
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != nullptr && fast->next != nullptr){
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow)//fast和slow起始位置相同,所以要先移动再找相遇点
                break;
        }
        if(fast == nullptr || fast->next == nullptr){
            return nullptr;
        }
        else{
            ListNode* index1 = head;
            ListNode* index2 = fast;
            while(index1 != index2){//有可能fast和slow直接在入口处相遇,所以要先判断是否相等再移动两个index
                index1 = index1->next;
                index2 = index2->next;
            }
            return index1;
        }
        return nullptr;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值