双指针法在链表中的应用(《代码随想录》学习笔记)

一、算法思想

在链表中寻找某一元素大多需要通过它的相邻元素才能找到该元素的地址。当我们对该元素进行删除、排序等操作时需要改变这个元素及其相邻元素的指针,但由于程序只能逐条执行,某些间接节点的地址在更改的过程中有丢失的风险,需要另设一个乃至多个指针保存这些将会丢失的地址,以免出现更改一个节点以后找不到下一节点的情况。

二、算法特性

1.适用条件

  • 数据结构为链表
  • 对链表中的某一元素进行删除移位操作

2.时空复杂度

  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

三、算法实例

206.反转链表

题目要求:

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
示例1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
示例2:
输入:head = [1,2]
输出:[2,1]

解题思路: 本题可以通过创建一个新链表,遍历所给链表并将其元素节点逐个用头插法插入新链表中解决,该方法时间复杂度为O(n),空间复杂度为O(n),需要较多内存空间。实际上也可以直接在原有链表上进行修改,只需要将所有链表的指针域中存储的地址调换为上一个节点的地址即可实现反转,该方法时间复杂度仍为O(n),但空间复杂度降为O(1)。

代码示例

/**
 * 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* reverseList(ListNode* head) {
        ListNode* ptr = nullptr; 
        ListNode* cur = head;
        ListNode* temp;            // 用于存储cur的下一个节点,因为将要修改cur的指针域
        while(cur) {
            temp = cur->next;
            cur->next = ptr;       // 反转操作
            // 更新节点位置
            ptr = cur;
            cur = temp;            // 借助temp不通过指针跳转到下一节点
        }
        return ptr;
    }
};

注意事项: ptr初值不宜设为head,否则反转后的链表尾节点的指针将指向head而不是nullptr。

92.反转链表II

题目要求:

给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回反转后的链表 。
示例1:
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
示例2:
输入:head = [5], left = 1, right = 1
输出:[5]
提示:
链表中节点数目为 n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n

解题思路: 本题在反转设定部分时仍可采用双指针法,过程与第一题相同,不同的是本题需要先遍历数组找出第left个元素的位置,并设一个指针保存第(left-1)个节点的地址。在完成指定部分的反转以后,需要让第(left-1)个节点的next指针指向原来的第right个节点,让第left个节点的next指针指向第(right+1)个节点。此外,由于我们每一次求解时均调用了第(left-1)个节点,当left = 1时,还需要额外的处理,为了避免建立分类的逻辑结构,可以设置虚拟头结点dummyHead作为哨兵指向头结点,保证第(left-1)个节点的存在(此处的头结点是有数据的节点)。

推荐讲解:反转链表.灵茶山艾府

代码示例:

/**
 * 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* reverseBetween(ListNode* head, int left, int right) {
        // 建立虚拟头结点
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head;
        int sublen = right - left + 1;

        ListNode* leftptr = dummyHead;   // 指向第(left-1)个节点
        while (--left > 0)
            leftptr = leftptr->next;
 
        ListNode* prv = leftptr->next;
        ListNode* cur = prv->next;
        while (--sublen > 0) {
            ListNode* temp = cur->next;
            cur->next = prv;
            prv = cur;
            cur = temp;
        }
        leftptr->next->next = cur;  // 让原第left个节点的next指针指向原第(right+1)个节点
        leftptr->next = prv;            // 让原第(left-1)个节点的next指针指向原来的第right个节点
        return dummyHead->next;
    }
};
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值