真的是大道至简,小问题大学问
双指针在数组中的应用
例题1:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。请注意 ,必须在不复制数组的情况下原地对数组进行操作。(力扣:283)
// 简简单单一道简单题却有大学问,使用双指针,总体思路就是借助双指针将非零元素从前往后填充,最后再补充回零
// 其中一个指针的作用是用来保存当前数组填充的位置,另一个指针用来不断在数组中找零
class Solution {
public void moveZeroes(int[] nums) {
// 用来保存当前数组填充的位置
int point1=0;
// 用来保存当前查找的位置
int point2=0;
for(;point2<nums.length;point2++){
if(nums[point2]!=0){
nums[point1]=nums[point2];
// 将填充位置往后移
point1++;
}
}
// 将point1后面的位置全部填充为零
while(point1<nums.length){
nums[point1++]=0;
}
}
}
例题2: 给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a ,b ,c ,使得 a + b + c = 0 ?请找出所有和为 0 且 不重复 的三元组。(剑指 offer 007)
// 看了看其他大神的思路,是用三重循环就不说了,是个人都能做出来
// 下面是其他一些大神的思路,使用循环加上双指针
class Solution {
// 创建一个数组保存所有符合条件的集合
List< List<Integer>> result=new ArrayList<List<Integer>>();
public List<List<Integer>> threeSum(int[] nums) {
// 将数组进行排序
Arrays.sort(nums);
for(int i=0;i<nums.length;i++){
// 首先去除元素重复的问题
if(i>0&&nums[i]==nums[i-1]){
continue;
}
// 其次是去除一些不可能的情况
// 因为数组已经拍好序了,所以后面的元素肯定都大于0,所以和肯定大于0
if(nums[i]>0){
break;
}
int target=-nums[i];
// 使用双指针再该数的右侧寻找和为合适的两个数
int left=i+1;
int right=nums.length-1;
while(left<right){
// 找到了后将三个数的组合加入集合中
if(nums[left]+nums[right]==target){
List<Integer> list=new ArrayList<Integer>();
list.add(nums[i]);
list.add(nums[left]);
list.add(nums[right]);
result.add(list);
// 去重
while(left<right&&nums[left]==nums[++left]);
while(left<right&&nums[right]==nums[--right]);
}else if(nums[left]+nums[right]<target){
left++;
}else{
right--;
}
}
}
return result;
}
}
双指针在字符串中的应用
例题1: 给定两个 01 字符串 a 和 b ,请计算它们的和,并以二进制字符串的形式输出。
输入为 非空 字符串且只包含数字 1 和 0。(剑指 Offer II 002. 二进制加法)
// 直接按照二进制的计算方式来计算
class Solution {
public String addBinary(String a, String b) {
// 首先创建两个指针指向两个数组的最后的一个之母,通过判断这两个指针是否为零来判断
// 该字符串该位置上是否还存在字母
int apoint=a.length()-1;
int bpoint=b.length()-1;
// 创建一个变量来保存每个位置求和后的余数
int c=0;
// 创建一个字符串保存求和后的结果
String str="";
//注意循环的范围
while(apoint>=0||bpoint>=0){
int ai;
int bi;
// 将字符串对应位置上的字符转化为整数
if(apoint<0){
ai=0;
}else{
ai=a.charAt(apoint)-'0';
}
if(bpoint<0){
bi=0;
}else{
bi=b.charAt(bpoint)-'0';
}
// 求出每个位置求和后的结果
str=(ai+bi+c)%2+str;
// 求出每个位置求和后的余数
c=(ai+bi+c)/2;
apoint--;
bpoint--;
}
// 这里还要判断一下是否含有进制数
if(c==0){
return str;
}else{
return 1+str;
}
}
}
例题2: 给定一个非空字符串 s,请判断如果 最多 从字符串中删除一个字符能否得到一个回文字符串。(offer 019)
// 因为题目的意思时只删除一个字符后整个字串能否是一个回文字符串
// 所以我们可以使用判断一个字符串是否是回文字符串的思路,如果出现不相同的字符,分别删除
// 不相同的字符,然后对剩余的字符串
// 判断其是否是回文字符串即可
class Solution {
String s;
public boolean validPalindrome(String s) {
this.s=s;
int lp=0;
int rp=s.length()-1;
while(lp<rp){
if(s.charAt(lp)==s.charAt(rp)){
lp++;
rp--;
}else{
break;
}
}
// 先判断是否字符串已经是回文字符串
if(lp==rp){
return true;
}else{
return pd(lp+1,rp)||pd(lp,rp-1);
}
}
// 创建一个判断一个函数是否是回文字符串的函数
public boolean pd(int left,int right){
while(left<right){
if(s.charAt(left)!=s.charAt(right)){
return false;
}
left++;
right--;
}
return true;
}
}
例题3: 给你一个字符串 s ,根据下述规则反转字符串:
所有非英文字母保留在原有位置。
所有英文字母(小写或大写)位置反转。
返回反转后的 s 。
// 第一思路:使用双指针
class Solution {
public String reverseOnlyLetters(String s) {
// 将字符串转化为数组方便修改
char[] arr=s.toCharArray();
// 创建两个指针方便后面进行位置的交换
int lpoint=0;
int rpoint=s.length()-1;
// 通过循环移动指针的位置
while(lpoint<rpoint){
// 将指针所指向的字符先记录下来
char lchar=s.charAt(lpoint);
char rchar=s.charAt(rpoint);
// 首先判断初始左右指针所指向的字符是否是字母
while(!('z'>=lchar&&lchar>='a')&&!('Z'>=lchar&&lchar>='A')&&lpoint<rpoint){
lpoint++;
lchar=s.charAt(lpoint);
}
while(!('z'>=rchar&&rchar>='a')&&!('Z'>=rchar&&rchar>='A')&&lpoint<rpoint){
rpoint--;
rchar=s.charAt(rpoint);
}
// 交换左右指针所指向的字母
char temp=arr[lpoint];
arr[lpoint]=arr[rpoint];
arr[rpoint]=temp;
lpoint++;
rpoint--;
}
// 将数组重新变为字符串后返回
return new String(arr);
}
}
# 双指针再链表中的应用
例题1: 给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意,pos 仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
/**
* 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) {
// 创建一个变量记录是否有环
boolean pd=false;
// 创建两个快慢指针
ListNode fast=head;
ListNode slow=head;
// 判断是否有环,注意循环中的条件避免发生异常
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
slow=slow.next;
// 一但快慢指针相遇说明有环,直接跳出循环
if(fast==slow){
pd=true;
break;
}
}
// 如果有环,找出相遇的节点
fast=head;
if(pd){
while(fast!=slow){
fast=fast.next;
slow=slow.next;
}
return fast;
}else{
return null;
}
}
}