链表中的双指针总结

链表中的双指针问题:

// 初始化快慢指针
ListNode slow = head;
ListNode fast = head;
/**
 * 对于每个问题具体改变
 * 注意避免空指针异常
 **/
while (slow != null && fast != null && fast.next != null) {
    slow = slow.next;           // 每次移动慢指针一步
    fast = fast.next.next;      // 每次移动快指针两步
    if (slow == fast) {         // 具体问题具体修改
        return true;
    }
}
return false;  

 需要把双指针技巧分为两类,一类是快慢指针,一类是左右指针。前者主要解决链表中的问题,比如典型的判别链表中是否包含环;后者主要解决数组(或者字符串)中的问题,比如二分查找。

一、快慢指针的常见算法

快慢指针一般都初始化指向链表的头结点head,前进时快指针fast在前,慢指针在后。

1.判断链表中是否有环

单链表的特点是每个节点只知道下一个节点,所以一个指针的话无法判断链表是否含有环。如果链表中有环,那么这个指针最终会遇到null.

boolean hasCycle(ListNode head){
    while(head != null){
        head = head.next;
    }
    return false;
}

 如果有环,那么这个指针就会陷入死循环。

用快慢指针,如果不含有环,跑得快的那个指针最终会遇到null,说明链表中不含有环;如果含有环,快指针最终会与慢指针相遇,说明有环。类似小学数学中环形操场赛跑的追击问题。

boolean hasCycle(ListNode head){
    ListNode fast,slow;
    fast = slow = head;
    while(fast != null && fast.next != null){
        fast = fast.next.next;
        slow = slow.next;    
        
        if (fast == slow)
            return true;
    }
    return false;
}

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

参见这篇博客

3.寻找链表的中点

让快指针一次前进2步,慢指针一次前进1步,当快指针到达链表尽头时,慢指针就处于链表的中间位置。

while(null != fast && null != fast.next){
    fast = fast.next.next;
    slow = slow.next;
}

// slow 就在中间位置
return slow;

如果链表长度是奇数,slow在中点位置;如果是偶数,slow在中间偏右;

4.寻找链表的倒数第K个元素【删除链表的第k个元素】

先让快指针走k步,然后快慢指针开始同速度前进。这样当快指针走到链表末尾null时,慢指针所在的位置就是倒数第k个链表节点【但是要注意特殊情况--只有一个节点、删除的是头结点】

ListNode slow, fast;
slow = fast = head;
while(k-- > 0){ // 让快指针先移动k步
    fast = fast.next;
}
while(fast != null){ // 此时两者的间隔刚刚是K,当fast到达结尾时,slow刚刚是倒数第k个
    slow = slow.next;
    fast = fast.next;
}
return slow;

二、左右指针的常用算法

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

1.二分查找

int binarySearch(int[] nums, int target){
    int left = 0;
    int right = nums.length - 1;
    while(left <= right){
        if (nums[mid] == target){
            return mid;
        } else if (nums[mid] < target){
            left = mid + 1;
        }else if (nums[mid] > target){
            right = mid - 1;
        }
    }
    return -1;
}
        

2.两数之和【leetcode第一道题】

只要数组有序,就应该想到双指针技巧。

int[] twoSum(int[] nums, int target){
    int left = 0, right = nums.length - 1;
    while(left < right){
        int sum = nums[left] + nums[right];
        if (sum == target){
            return new int[]{left + 1, right + 1};
        } else if (sum < target){
            left++;
        } else if (sum > target){    
            right--;
        }
    }
    return new int[]{-1, -1};
}

3.反转数组

void reverse(int[] nums){
    int left = 0;
    int right = nums.length - 1;
    while(left < right){
        // swap(a,b);
        int temp = nums[left];
        nums[left] = nums[right];
        nums[right] = temp;
        left++; right--;
    }
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值