Leetcode刷题07-双指针

双指针

基础知识

1.介绍

双指针是一种思想或一种技巧,并不是特别具体的算法。

具体就是用两个变量动态存储两个结点,方便我们进行一些操作。通常用在线性的数据结构中。

特别是链表类的题目,经常需要用到两个或多个指针配合来记忆链表上的节点,完成某些操作。

2.分类

常见的双指针方式:

  • 同速指针:链表上两个指针,一个先出发,另一个后出发并以相同的速度跟随。
    • 求链表的逆:通过临时指针让双指针同步前行
    • 求链表倒数第k个元素:先让第一个指针前进k步,然后两个指针同样的速度前进,第一个指针走到尽头,则后面的指针为倒数第k个元素
  • 快慢指针:链表上两个指针从同一节点出发,其中一个指针前进速度比另一个指针快(比如,是另一个指针的两倍)
    • 计算链表的中点:快慢指针从头节点出发,快指针向前移动两个节点,满指针向前移动一个节点,最终当快指针到达终点的时候,满指针指向中间的节点。
    • 判断链表是否有环:快慢指针从头节点出发,如果链表中存在环,两个指针最终会在环中相遇
    • 求链表中环的长度:只要相遇后一个指针不动,另一个指针前进直到相遇计算长度即可。

题目解析

反转链表

1.题目描述

题目链接

在这里插入图片描述

2.解析思路及代码

  • 迭代:使用两个指针cur和prev,初始情况cur指向head,prev指向null,然后保证cur.next = prev,两个指针同时向右移动继续该操作直到cur只想空,这个时候prev指向的节点就是head
  • 递归:将大问题分成小问题,将整个链表逆转,先假设链表后部分已经逆转,因此只需要修改当前节点的next的下一个指向当前节点即可,当前节点的next指向null,返回新节点,详细思路如下。

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null)  return head;
        ListNode prev = null;
        ListNode cur = head;
        
        while (cur != null) {
            ListNode next = cur.next;
            cur.next = prev;
            prev = cur;
            cur = next;
        }
        
        return prev;
    }
    
    // recursive
    public ListNode reverseList(ListNode head) {
        if (head == null || head.next == null)  return head;
        
        ListNode newHead = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return newHead;
    }
}
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        cur = head
        prev = None
        while cur:
            next = cur.next
            cur.next = prev
            prev = cur
            cur = next
        return prev
    
    # recursive
    def reverseList(self, head: ListNode) -> ListNode:
        if not head or not head.next:
            return head
        newHead = self.reverseList(head.next)
        
        head.next.next = head
        head.next = None
        return newHead

删除链表的倒数第 N 个结点

1.题目描述

题目链接

在这里插入图片描述

2.解题思路及代码

双指针:让两个指针分别指向head和空节点(空节点的下一个节点是head),然后让指向head的走n步,然后指向head和空节点的两个指针同时往下走直到指向head的为空,这个时候第二个指针就是指向倒数第n个节点的前一个节点,然后删除下一个节点即可。

	public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode head1 = new ListNode();
        head1.next = head;
        ListNode fast = head;
        ListNode slow = head1;
        while (n > 0) {
            fast = fast.next;
            n -- ;
        }
        
        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return head1.next;
    }
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        head1 = ListNode(0, head)
        slow = head1
        fast = head
        
        while n > 0:
            fast = fast.next
            n -= 1
        
        while fast:
            fast = fast.next
            slow = slow.next
            
        slow.next = slow.next.next
        
        return head1.next

排序链表

1.题目描述

题目链接

在这里插入图片描述

2.解题思路及代码

  • 使用哈希表映射每个数字的出现次数,然后找出只出现一次的元素
  • 获取答案的每一位上的数字,因为其他数都出现三次,因此答案的每一位数字等于所有数字出现的结果和模3,注意如果目标语言不区分有符号数和无符号数,最高位需要特判。
	public ListNode sortList(ListNode head) {
        return sortList(head, null);
    }
    
    public ListNode sortList(ListNode head, ListNode tail) {
        if (head == null) {
            return head;
        }
        
        if (head.next == tail) {
            head.next = null;
            return head;
        }
        
        ListNode slow = head, fast = head;
        
        while (fast != tail) {
            slow = slow.next;
            fast = fast.next;
            if (fast != tail)   fast = fast.next;
        }
        
        ListNode mid = slow;
        ListNode list1 = sortList(head, mid);
        ListNode list2 = sortList(mid, tail);
        return merge(list1, list2);
    }
    
    public ListNode merge(ListNode head1, ListNode head2) {
        ListNode dummyHead = new ListNode(0);
        ListNode cur = dummyHead, cur1 = head1, cur2 = head2;
        
        while (cur1 != null && cur2 != null) {
            if (cur1.val > cur2.val) {
                cur.next = cur2;
                cur2 = cur2.next;
            } else {
                cur.next = cur1;
                cur1 = cur1.next;
            }
            cur = cur.next;
        }
        
        cur.next = (cur1 == null ? cur2 : cur1);
        return dummyHead.next;
    }
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        def sortFunc(head: ListNode, tail: ListNode) -> ListNode:
            if not head:
                return head
            if head.next == tail:
                head.next = None
                return head
            slow = fast = head
            while fast != tail:
                slow = slow.next
                fast = fast.next
                if fast != tail:
                    fast = fast.next
            mid = slow
            return merge(sortFunc(head, mid), sortFunc(mid, tail))
            
        def merge(head1: ListNode, head2: ListNode) -> ListNode:
            dummy = ListNode(0)
            cur, cur1, cur2 = dummy, head1, head2
            while cur1 and cur2:
                if cur1.val <= cur2.val:
                    cur.next = cur1
                    cur1 = cur1.next
                else:
                    cur.next = cur2
                    cur2 = cur2.next
                cur = cur.next
            cur.next = cur2 if not cur1 else cur1
            return dummy.next
        
        return sortFunc(head, None)

删除排序链表中的重复元素

1.题目描述

题目链接

在这里插入图片描述

2.解题思路及代码

如果当前节点的下一个节点值等于当前节点,跳过下一个节点。

	public ListNode deleteDuplicates(ListNode head) {
        if (head == null)   return head;
        
        ListNode cur = head;
        
        while (cur.next != null) {
            if (cur.next.val == cur.val) {
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return head;
    }
class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head:
            return head
        
        cur = head
        
        while cur.next:
            if cur.val == cur.next.val:
                cur.next = cur.next.next
            else:
                cur = cur.next
        return head

环形链表

1.题目描述

题目链接

在这里插入图片描述

2.解题思路及代码

  • 哈希表:有环一定会相遇,将节点保存到哈希表中,无环则一定可以遍历完
  • 快慢指针:快指针指向head的下一节点,慢指针指向head,然后快指针一次走两步,慢指针一次走一步,如果有环一定会相遇,否则fast指针一定会指向空,此时返回false
	public boolean 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;
            fast = fast.next.next;
            slow = slow.next;
        }
        return true;
    }
class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        if not head or not head.next:
            return False
        
        slow = head
        fast = head.next
        
        while fast != slow:
            if not fast or not fast.next:
                return False
            fast = fast.next.next
            slow = slow.next
        return True
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值