链表中的双指针

链表中的双指针,一般是指初始时设置两个移动指针,它们可能以不同的初始状态或不同的移动方式进行移动。

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

题目描述:给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2. 当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:给定的 n 保证是有效的。

进阶:你能尝试使用一趟扫描实现吗?

思路:不同的初始状态,一个指针从头开始移动,一个先移动n个位置,然后以相同的移动方式移动(指向next)。

参考代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        
        //the given n is effective
        //if(head == NULL) return NULL;

        //p & q are move-node
        ListNode *p = head, *q = head; 

        for(int i = 0; i < n; i ++){  
            q = q->next;
            //the length of list < n
            if(q == NULL) return head->next;
        }
       
        while(q->next){//end at q arrive at the tail of list
            p = p->next;
            q = q->next;
        }

        p->next = p->next->next;

        return head;
    }
};

2.环形链表

题目描述:给定一个链表,判断链表中是否有环。

思路:看我下图的抽象画法,意思是说在环形的“路上”,A和B以不同的速度移动,肯定能在某点相遇。于是可以联想到常说的快慢指针。

参考代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        //空或只有一个节点
        if(head == NULL || head->next == NULL) return false;
        ListNode* slow = head;
        ListNode* fast = head->next;
        while(slow != fast){//没相遇
            // 快的已经走完了还未遇到
            if(fast == NULL || fast->next == NULL) return false;
            slow = slow->next;
            fast = fast->next->next;
        }

        return true;
    }
};

3.链表的中间结点

题目描述:给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。

思路:快慢指针移动。

参考代码:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* middleNode(ListNode* head) {

        ListNode *slow = head, *fast = head;

        while(fast->next){
            slow = slow->next;

            if(fast->next->next) fast = fast->next->next;
            else fast = fast->next;
        }

        return slow;
    }
};

以上就是这篇的主要内容,如有疑问,请您指出,谢谢!

<think>我们使用双指针方法实现链表反转。双指针通常包括一个前驱指针(prev)一个当前指针(curr),通过遍历链表并逐个反转指针方向来实现。步骤:1.初始化两个指针:prev指向None,curr指向链表的头节点。2.遍历链表,每次迭代中:a.保存当前节点curr的下一个节点(因为即将改变curr的指向)。b.将当前节点curr的next指针指向prev(反转操作)。c.将prevcurr都向前移动一步:prev移动到curr的位置,curr移动到之前保存的下一个节点的位置。3.当curr为None时,遍历结束,此时prev指向反转后链表的头节点。下面用Python代码实现:```classListNode:def__init__(self,val=0,next=None):self.val=valself.next=nextdefreverseList(head:ListNode)->ListNode:prev=Nonecurr=headwhilecurr:next_temp=curr.next#保存下一个节点curr.next=prev#反转当前节点的指针prev=curr#前指针后移curr=next_temp#当前指针后移returnprev#最后prev是新的头节点```为了更直观,我们可以用图示说明每一步:初始状态:prev=None,curr=1->2->3->None第一步:保存next_temp=2,然后curr.next指向prev(即None),然后prev移动到1,curr移动到2。此时链表变为:None<-12->3->None第二步:保存next_temp=3,将2的next指向1,然后prev移动到2,curr移动到3。此时链表变为:None<-1<-23->None第三步:保存next_temp=None,将3的next指向2,然后prev移动到3,curr移动到None,循环结束。最终链表:None<-1<-2<-3返回prev,即3,作为新链表的头节点。注意:这里我们使用了两个指针prevcurr,以及一个临时变量next_temp(保存curr的下一个节点),所以也可以称为三指针,但核心思想是双指针(prevcurr)。引用[2][5]中提到的双指针(或三指针)方法也是类似的思路。§§相关问题§§1.双指针反转链表的时间复杂度空间复杂度是多少?2.递归方法如何实现链表反转?与双指针方法相比有什么优缺点?3.反转链表在实际开发中有哪些应用场景?</think>### 双指针实现链表反转详解 链表反转是算法中的常见问题,双指针法是最经典的迭代解决方案,通过两个指针的协同操作逐步反转节点指向。以下是详细实现步骤代码示例: #### 核心步骤 1. **初始化指针** - `prev` 指针:初始化为 `None`,表示反转后链表的尾部 - `curr` 指针:初始化为头节点 `head`,表示当前待处理节点 2. **遍历反转** ```mermaid graph LR A[prev=None] --> B[curr=head] B --> C[保存 next=curr.next] C --> D[反转指针 curr.next=prev] D --> E[prev移动到curr] E --> F[curr移动到next] ``` 3. **终止条件** 当 `curr` 遍历到链表尾部(`curr=None`)时结束,此时 `prev` 指向新链表的头节点 #### Python 代码实现 ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def reverseList(head: ListNode) -> ListNode: prev = None # 前驱指针 curr = head # 当前指针 while curr: next_temp = curr.next # 临时保存下一节点 [^5] curr.next = prev # 反转当前节点指针 [^2] prev = curr # 前驱指针前移 curr = next_temp # 当前指针前移 return prev # prev最终指向新链表头 [^4] ``` #### 执行过程示例(链表:1→2→3→None) | 步骤 | prev | curr | next_temp | 链表状态 | |------|------|------|-----------|----------------------| | 初始 | None | 1 | - | 1→2→3→None | | 1 | 1 | 2 | 3 | None←1 2→3→None | | 2 | 2 | 3 | None | None←1←2 3→None | | 3 | 3 | None | - | None←1←2←3 | #### 复杂度分析 - **时间复杂度**:$O(n)$,遍历链表一次 - **空间复杂度**:$O(1)$,仅使用固定数量指针 [^2] #### 关键点说明 1. **临时保存**:`next_temp` 保留后续节点引用,防止断链 [^5] 2. **指针移动顺序**:先修改节点指向,再移动双指针 3. **终止处理**:当 `curr` 为 `None` 时,`prev` 正好指向新头节点 > 此方法相比递归方案节省栈空间,适合处理大型链表 [^3][^4]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值