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

两两交换链表中的节点

leetcode

代码随想录

  • 前提

    • 只能交换节点,不能交换值
  • 要点

    • 添加虚拟头节点

    • 画出改链步骤图更清晰,最小单元是cur->target1->target2(最后指向NULL节点也画出来)

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

    • 单指针(cur):按步骤顺序执行,丢失的值用tmp、tmp1存储,单次循环结束时cur移动两个节点。(双指针:单次循环结束时时移动两个指针)

    • /* 判断cur->next->next(奇个数)是否为空前,要先判断cur->next(偶个数)是否为空,防止cur->NULL->next。
      * 判断cur->next是否为空则不用判断cur是否为空,因* 为没有跨节点。
      */
      cur->next != NULL && cur->next->next != NULL
      
    • 当循环体内的操作是cur->next->next->next;时,只需要满足条件cur->next->next != NULL,因为要避免cur->next->NULL->next,cur->next->next->NULL则无所谓。即不管什么操作都要防止NULL->next这种情况。

迭代版本

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* swapPairs(struct ListNode* head) {
    struct ListNode* fakehead = malloc(sizeof(struct ListNode));
    fakehead -> next = head;
    struct ListNode* cur = fakehead;
    struct ListNode* tmp = NULL;
    struct ListNode* tmp1 = NULL;

    while (cur->next != NULL   &&   cur->next->next != NULL){
        tmp = cur -> next;
        tmp1 = cur -> next -> next -> next;

        cur -> next = cur ->next -> next;
        cur ->next -> next = tmp;
        tmp -> next = tmp1;

        cur = cur -> next -> next; 
    }

    head = fakehead -> next;
    free(fakehead);

    return head;
}

!递归版本

  • 不添加虚拟节点
  • 最小单元是head + newhead,返回新头结点地址给上一级递归,用下一级递归的返回值赋值给旧头结点的next。
    在这里插入图片描述
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* swapPairs(struct ListNode* head) {

    while (!head || !head->next){
        return head;
    }

    struct ListNode* newhead = head ->next;
    head -> next = swapPairs(newhead -> next);
    newhead -> next = head;

    return newhead;
}

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

leetcode

代码随想录

  • 要点
    • 使用虚拟头节点
    • 双指针法
    • right先走n+1步,因为left要停留在删除节点的前一个节点
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
    struct ListNode* fakeHead = malloc(sizeof(struct ListNode));
    fakeHead -> next = head;
    struct ListNode* right = fakeHead;
    struct ListNode* left = fakeHead;
    int i = 0;

    while (i < n && right -> next != NULL){
        right = right -> next;
        i++;
    }

    if (i != n){
        return -1;
    }

    while(right -> next != NULL){
        right = right -> next;
        left = left -> next;
    }

    left -> next = left -> next -> next;
    head = fakeHead -> next;
    free(fakeHead);

    return head;
    
}

链表相交

leetcode

代码随想录

  • 前提
    • 整个链式结构中不存在环
    • 函数返回结果后,链表必须保持其原始结构
  • 要点
    • 是指针相交,不是值相交(比如两个不相交的等价链表),也就是两个链表连接到同一个节点。
    • 两个相交链表的最后节点始终相同。一旦它们相交,之后的所有节点将相等。
    • 先将两个链表处理成等长(从后向前算),方便同时移动比较,将时间复杂度降为O(n)。
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
    struct ListNode* nodeA = headA;
    struct ListNode* nodeB = headB;
    int lenA = 0;
    int lenB = 0;

    while(nodeA != NULL){
        nodeA = nodeA -> next;
        lenA++;
    }
    nodeA = headA;

    while(nodeB != NULL){
        nodeB = nodeB -> next;
        lenB++;
    }
    nodeB = headB;

    if (lenA > lenB){
        int cnt = lenA -lenB;
        while (cnt){
            nodeA = nodeA -> next;
            cnt--;
        }
    }
    else {
        int cnt = lenB - lenA;
        while (cnt){
            nodeB = nodeB -> next;
            cnt--;
        }
    }

    while(nodeA != NULL || nodeB != NULL){
        if (nodeA == nodeB){
            return nodeA;
        }
        nodeA = nodeA -> next;
        nodeB = nodeB -> next;
    }
    
    return NULL;
}

环形链表2

leetcode

代码随想录

  • 前提
    • 不允许修改链表
  • 要点
    • 判断链表是否有环:快慢指针法,快指针走两步,慢指针走一步,如果快慢指针相遇则代表这个链表有环。
    • 如果有环,怎么找到这个入口:一个指针从头节点出发,一个指针从相遇节点出发,两个指针相遇的地方就是入口节点,详情参照代码随想录
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode* fast = head;
    struct ListNode* slow = head;

    while(fast != NULL && fast -> next != NULL){
        fast = fast -> next -> next;
        slow = slow -> next;

        if (fast == slow){
            slow = head;
            while (fast != slow){
                fast = fast -> next;
                slow = slow -> next;
            }
            return fast;
        }
    }

    return NULL;
}

总结

  • 主要用法有双指针法
  • 迭代和递归的基本模型不同
  • 注意循环结束条件,既要覆盖目标操作,又要防止NULL->next(有可能出现在循环体逻辑操作也有可能出现在循环判断条件)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值