leetcode刷题之双指针(C++&Java)

(学习参考书:LeetCode101)

双指针

双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。也可以延申多个数组的多个指针。

若两个指针指向同一数组,遍历方向相同且不会相交,则称为滑动窗口,常用于区间搜索。

若两个指针指向同一个数组,但是遍历方向相反,则可用于进行搜索,被搜索的数组往往是排好序的。

力扣例题

167.两数之和2-输入有序数组

分析:

因为数组已经排好序,我们可以采用方向相反的双指针来寻找这两个数字,一个初始执行最小的元素,即数组最左边,向右遍历;一个初始执行最大的元素,即数组最右边。当两个指针相加等于目标数,那即为结果;当相加小于目标数,左指针++;当相加大于目标数,右指针++;

题解:

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        vector<int> result(2);
        int left = 0,right = numbers.size()-1;

        while(left<right){
            if(numbers[left]+numbers[right]==target){
                result[0] = left+1;
                result[1] = right+1;
                break;
            }
            else if(numbers[left]+numbers[right]<target){
                ++left;
            }
            else if(numbers[left]+numbers[right]>target){
                --right;
            }
        }
        return result;
    }
};
class Solution {
    public int[] twoSum(int[] numbers, int target) {
        int result[] = new int[2];
        int left = 0;
        int right = numbers.length-1;
        
        while(left<right){
            if(numbers[left]+numbers[right]<target){
                ++left;
            }
            else if(numbers[left]+numbers[right]>target){
                --right;                
            }
            else{
                result[0] = left+1;
                result[1] = right+1;
                break;
            }
        }
        
        return result;
    }
}

633.平方数之和

分析:

与167题类似,同样使两个数位于可能得到答案的最前端和最末端。如果当前两数平方相加小于c,前数++;如果当前两数平方相加大于c,后数- -;

题解:

class Solution {
public:
    bool judgeSquareSum(int c) {
        long left = 0,right = (long)sqrt(c);//后数最大也不会超过c开平方
        while(left<=right){
            if(left*left+right*right==c){
                return true;
            }
            else if(left*left+right*right<c){
                ++left;
            }
            else if(left*left+right*right>c){
                --right;
            }
        }
        return false;
    }
};
class Solution {
    public boolean judgeSquareSum(int c) {
        long left = 0;
        long right = (long) Math.sqrt(c);

        while(left<=right){
            long sum = left*left+right*right;
            if(sum==c){
                return true;
            }
            else if(sum<c){
                ++left;
            }
            else if(sum>c){
                --right;
            }
        }

        return false;
    }
}

680.验证回文字符串2

分析:

在允许最多删除一个字符的情况下,同样可以使用双指针。初始化两个指针分别指向字符串的第一个字符和最后一个字符。同时向中间遍历,当遇到对称字符不相等时,则尝试删除左边或右边一个字符来继续遍历,如果不删除或删除一次后遍历至left==right,则说明可以为回文串;如果遍历任然有对称位置字符不等,则不能为回文串。

题解:

class Solution {
public:
    bool validPalindrome(string s) {
        int result = 1;
        int left = 0,right = s.length()-1;

        while(left<right){
            if(s[left]==s[right]){
                left++;
                right--;
            }
            else{
                return (checkPalindrome(s,left+1,right)|checkPalindrome(s,left,right-1));
            }
        }

        return true;
    }

    bool checkPalindrome(string& s, int left, int right) {
        for (int i = left, j = right; i < j; ++i, --j) {
            if (s[i] != s[j]) {
                return false;
            }
        }
        return true;
    }
};
class Solution {
    public boolean validPalindrome(String s) {
        int left = 0;
        int right = s.length()-1;

        while(left<right){
            if(s.charAt(left)==s.charAt(right)){
                ++left;
                --right;
            }
            else{
                return checkPalindrome(s,left+1,right)||checkPalindrome(s,left,right-1);
            }
        }

        return true;
    }

    public boolean checkPalindrome(String s,int left,int right){
        while(left<right){
            if(s.charAt(left)==s.charAt(right)){
                ++left;
                --right;
            }
            else{
                return false;
            }
        }
        return true;
    }
}

88.合并两个有序数组

分析:

因为两个数组有序,且nums1有预留空间。所以可以每次将nums1和nums2中最大的数比较,较大的一个放在nums1预留空间的最末尾,然后将nums1有效数字末尾指针和nums2末尾指针和nums1预留空间末尾指针前移一位,循环比较和存放。

当nums1比较完后,还需将nums2中剩余的数放入剩下的空间;当nums2比较完后,因为nums1原本的元素本来有序,所以不用再做其他处理。

题解:

class Solution {
public:
    void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {
        int last = m+n-1;
        --m;
        --n;
        while(m>=0&&n>=0){
            nums1[last--] = nums1[m]>nums2[n]?nums1[m--]:nums2[n--];
        }

        while(n>=0){
            nums1[last--] = nums2[n--];
        }
    }
};
class Solution {
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int last = m+n-1;
        --m;
        --n;

        while(m>=0&&n>=0){
            nums1[last--] = nums1[m]>nums2[n]?nums1[m--]:nums2[n--];
        }

        while(n>=0){
            nums1[last--] = nums2[n--];
        }
    }
}

142.快慢指针

分析:

对于链表找路问题,有一个通用的解法——快慢指针。即一个fast指针一个slow指针。每次fast前进2步,每次slow前进1步,如果存在环路则他们必定相遇。

相遇时的slow指针走过的路程为slow = a+b;

相遇时的fast指针走过的路程为fast = a+b+c+b;

因此差值运算可知:a = c;

因此将fast指针重新置为head,并每次走一步,则刚好在入环点与slow相遇。

题解:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *fast = head,*slow = head;
        do{
            if(!fast||!fast->next)
                return NULL;
            fast = fast->next->next;
            slow = slow->next;
        } while(fast!=slow);

        fast = head;
        while(fast!=slow){
            fast = fast->next;
            slow = slow->next;
        }

        return fast;
    }
};
public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;

        do {
            if (fast==null||fast.next==null){
                return null;
            }
            fast = fast.next.next;
            slow = slow.next;
        }
        while (fast!=slow);

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

        return fast;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值