2.双指针

1.快慢指针

主要解决链表中的问题,比如典型的判定链表中是否包含环

解决的问题包括:

(1)判定链表中是否含有环

经典解法就是用两个指针,一个每次前进两步,一个每次前进一步。
如果不含有环,跑得快的那个指针最终会遇到 null,说明链表不含环;如果含有环,快指针最终会超慢指针一圈,和慢指针相遇,说明链表含有环。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode slow=head,fast=head;
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow){
                return true;
            }
        }
        return false;
    }
}

(2)已知链表中含有环,返回这个环的起始位置

当快慢指针相遇时,让其中任一个指针重新指向头节点,然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置。
在这里插入图片描述
下面代码包含三种情况:
(1)无环,则返回null
(2)有环+小尾巴
(3)有环+无尾巴
其实无尾巴是有尾巴的特殊情况

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head!=null && head.next==null){
            return null;
        }

        ListNode slow=head,fast=head;
        
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow) break;//找到环的入口
        }
        //如果没有环,则经过while循环之后fast和slow指向位置不同,则返回null
        if(fast!=slow){
            return null;
        }
        //寻找环的入口
        //如果是环形链表带小尾巴,就会进入while循环寻找环的入口;
        //如果不带尾巴,则上个while循环fast和slow总会在入口处(head)相遇:因为fast速度是slow的二倍,相遇时fast走了两圈,slow走了一圈,相遇处就是head,则不会进入下面的while循环。
        fast=head;
        while(fast!=slow){
            fast=fast.next;
            slow=slow.next;
        }
        return fast;
    }
}

(3)寻找链表的中点

让快指针一次前进两步,慢指针一次前进一步,当快指针到达链表尽头时,慢指针就处于链表的中间位置。
当链表的长度是奇数时,slow 恰巧停在中点位置;如果长度是偶数,slow 最终的位置是中间偏右。

/**
 * 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 middleNode(ListNode head) {
        ListNode fast=head,slow=head;
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
        }
        return slow;
    }
}

(4)寻找链表的倒数第 k 个元素

让快指针先走 k 步,然后快慢指针开始同速前进。这样当快指针走到链表末尾 null 时,慢指针所在的位置就是倒数第 k 个链表节点。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        if(head==null){
            return null;
        }
        //防止k超过链表长度
        int len=0;
        ListNode temp=head;
        while(temp!=null){
            temp=temp.next;
            len++;
        }
        if(k!=len){
            k=k%len;
        }

        ListNode fast=head,slow=head;
        //fast先走k步
        while(k>0){
            fast=fast.next;
            k--;
        }
        //一起走,最后slow的位置就是倒数第k个节点
        while(fast!=null){
            fast=fast.next;
            slow=slow.next;
        }

        return slow;

    }
}

(5)有序数组/链表去重

快指针遇到不同的数就和慢指针交换,最后慢指针以及慢指针之前的数组部分都是不重复的

数组:

class Solution {
    public int removeDuplicates(int[] nums) {
        int n=nums.length;
        int fast=0,slow=0;
        while(fast<n){
            if(nums[fast]!=nums[slow]){
                slow++;
                nums[slow]=nums[fast];
            }
            fast++;
        }
        return slow+1;
    }
}

链表:

/**
 * 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 deleteDuplicates(ListNode head) {
        if(head==null) return null;
        ListNode fast=head,slow=head;
        while(fast!=null){
            if(fast.val!=slow.val){
                slow.next=fast;
                slow=slow.next;
            }
            fast=fast.next;
        }
        slow.next=null;//处理末尾
        return head;
    }
}

(6)移除元素

设要移除的元素是val,则快指针遇到不等于val元素的时候,就和慢指针指向的元素进行交换,最后从数组开头到慢指针的位置都是不包含要移除元素的,元素长度即为慢指针+1

class Solution {
    public int removeElement(int[] nums, int val) {
        int n=nums.length;
        if(n==0) return 0;

        int fast=0,slow=0;
        while(fast<n){
            if(nums[fast]!=val){
                nums[slow]=nums[fast];
                slow++;
            }
            fast++;
        }
        return slow;
    }
}

(7)移动零

fast指针遇到不为0的元素就和slow指针指向的元素进行交换

class Solution {
    public void moveZeroes(int[] nums) {
        int n=nums.length;
        if(n==0) return;

        int fast=0,slow=0;
        while(fast<n){
            if(nums[fast]!=0){
                int temp=nums[slow];
                nums[slow]=nums[fast];
                nums[fast]=temp;
                slow++;
            }
            fast++;
        }
    }
}

2.左右指针

左右指针在数组中实际是指两个索引值,一般初始化为 left = 0, right = nums.length - 1

解决的问题包括:

(1)二分查找

(2)两数之和

在一个有序数组中寻找两个数,这两个数的和是题目要求的target

(3)翻转数组

(4)滑动窗口

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值