数组篇刷题模板总结二_双指针

数组篇刷题模板总结——双指针

双指针通常出现在数组和链表里面,可以将双指针粗略分为两类,一类是快慢指针,一类是左右指针。前者解决主要解决链表中的问题,比如典型的判定链表中是否包含环;后者主要解决数组(或者字符串)中的问题,比如二分查找。后文将详细探讨和总结。

快慢指针

快慢指针一般都初始化指向链表的头结点 head,前进时快指针 fast 在前,慢指针 slow 在后,巧妙解决一些链表中的问题。

1、判定链表中是否含有环

题目描述

给定一个链表,判断链表中是否有环。

解题思路

通过快慢指针进行辅助判断。若链表无环,则快指针最终会指向null,表明结果;若链表有环,则快指针最终会比慢指针快一圈直至相遇,此时同样表明结果。


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

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

题目描述

解题思路

不难得出,在第一次相遇时,假设慢指针 slow 走了 k 步,那么快指针 fast 一定走了 2k 步;设相遇点距环的起点的距离为 m,那么环的起点距头结点 head 的距离为 k - m,也就是说如果从 head 前进 k - m 步就能到达环起点。如果从相遇点继续前进 k - m 步,也恰好到达环起点。不用管 fast 在环里到底转了几圈,反正走 k 步可以到相遇点,那走 k - m 步一定就是走到环起点了。


代码实现
public ListNode detectCycle(ListNode head) {
	ListNode fast, slow;
	fast = head;
	slow = head;
	while (fast != null && fast.next != null) {
		fast = fast.next.next;
		slow = slow.next;
		if (fast == slow) {
			break;
		}
	}
    // 注意:这个判定条件不能少并且有两个作用:1.刚开始判定链表是否为空 2.双指针第一次相遇之后判断fast是否为空 
	if (fast == null || fast.next == null) {
		return null;
	}
	slow = head;
	while (fast != slow) {
	fast = fast.next;
	slow = slow.next;
	}
	return slow;
}

3、寻找链表的中点

解题思路

同上面,只要把快指针的速度设为慢指针的两倍,则快指针到链表尾时,慢指针恰好停在链表中间。


代码实现
public ListNode middleNode(ListNode head) {
	ListNode fast, slow;
	fast = head;
	slow = head;
	while (fast != null && fast.next != null) {
		fast = fast.next.next;
		slow = slow.next;
	}
	return slow;
}

4、寻找链表的倒数第 n 个元素

解题思路

同样采用快慢指针的方法,定义快慢指针初始位置为指向首元节点,让快指针先走n个位置,然后快慢指针以同一速度,直至快指针走到数组尾,此时slow对应的next即要删除的倒数第n个元素。


代码实现
public int removeNthFromEnd(ListNode head, int n) {
    ListNode fast, slow;
    fast = slow = head;
    while (n-- > 0) {
        fast = fast.next;
    }
    // 注意:n限制不超过链表长度
    if (fast == null) {
        return head.next;
    }
    while (fast != null && fast.next != null) {
        fast = fast.next;
        slow = slow.next;
    }
    slow.next = slow.next.next;
    return head;
}

左右指针

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

1、二分查找

不再赘述,手写一个框架即可。(查找某个数组中具体值位置)

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

2、经典老番:TwoSum 2.0

题目描述

解题思路

本题是twosum升级版,不过同样很简单。题目说明数组单调有序,则应该想到利用二分查找, 当左右指针对应值的和偏大,则调小;反之,亦然。


代码实现
public int[] twoSum(int[] numbers, int target) {
    if (numbers.length <= 0) {
        return new int[] {-1, -1};
    }
    int left = 0;
    int right = numbers.length - 1;
    while (left < right) {
        int sum = numbers[left] + numbers[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、反转数组

思路简单,就直接写代码。


代码实现
public void reverseString(char[] s) {
	int left = 0;
	int right = s.length - 1;
	while (left < right) {
		char tmp = s[left];
		s[left] = s[right];
		s[right] = tmp;
		left++;
		right--;
	}
}
4、滑动窗口

代码实现

public void reverseString(char[] s) {
	int left = 0;
	int right = s.length - 1;
	while (left < right) {
		char tmp = s[left];
		s[left] = s[right];
		s[right] = tmp;
		left++;
		right--;
	}
}
4、滑动窗口

详情见下文。[]( ̄▽ ̄)*

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值