15. 三数之和  (为0)

下标不能是重复的,必定右 i<l<r

1、先对数组排序(从小到大)

2、外层 i 遍历

    • 如果 nums[i] > 0 ,整个 nums[] 后面的必定无法有三元组为0(排过序了,后面的 nums[l] nums[r] 都会大于0)。break。
    • 如果 nums[i] = nums[i-1] 即当前这个值已经在前一个算过了,这个的答案是包含于前一个的,不用搞了。continue。

3、对于 nums[i] 后面的那段,用左右指针,l = i+1; r = len-1 往中间

  三数之和 = nums[i] + nums[l] + nums[r]

    • 如果三数之和小于0,那么左指针向右,右指针不动,使和变大
    • 如果三数之和大于0,那么左指针不动,右指针向左,使和变小
    • 如果三数之和等于0
      • while nums[l] = nums[l+1] l++ 左指针和它右边的一样了,那它右边的可以直接替换掉它,不然会重复
      • while nums[r] = nums[r-1] r--  右指针和它左边的一样了,那它左边的可以直接替换掉它,不然会重复
      • 添加结果,同时这个结果已经有了,那么继续往下走,l++; r--
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> result = new ArrayList();
        // 先从小到大排序,注意对数组的排序用 Arrays.sort
        Arrays.sort(nums);
        int len = nums.length;

        // 为了确保不重复 i<r<l
        for (int i=0;i<len;i++) {
            // 已经排过序了,nums[i]>0 必定无法有三元组(后面的 j,k 都会大于 0)
            if (nums[i]>0) {
                break;
            }
            // 对于 -4 -1 -1 0 1 2, 第一个 -1 就已经把 -1,-1,2 和 -1,0,1 两个答案搞出来了
            // 后面那个 -1 不用再继续搞了,必定被包含在前一个 -1 的答案集中,且答案集比前一个 -1 的答案集小
            if (i != 0 && nums[i] == nums[i-1]) {
                continue;
            }
            // 对于 i 右边这段左右指针进行操作
            // 左指针
            int l = i+1;
            // 右指针
            int r = len-1;
            while(l<r) {
                int sum = nums[i] + nums[l] + nums[r];
                if (sum == 0) {
                    // 左指针和它右边的一样了,那它右边的可以直接替换掉它。不替换的话会重复
                    // 注意加限制 l<r
                    while (l<r && nums[l] == nums[l+1]) {
                        l++;
                    }
                    // 右指针和它左边的一样了,那它左边的可以直接替换掉它。不替换的话会重复
                    // 注意加限制 l<r
                    while (l<r && nums[r] == nums[r-1]) {
                        r--;
                    }
                    result.add(Arrays.asList(nums[i], nums[l], nums[r]));
                    // 添加完结果,别忘了继续移动左右执政
                    l++;
                    r--;
                }
                // 左指针向右,右指针不动。可使和变大
                else if (sum<0) {
                    l++;
                }
                 // 左指针不动,右指针向左,可使和变小
                else if (sum>0) {
                    r--;
                }
            }
        }
        return result;
    }
}

 

 16. 最接近的三数之和 (最接近 target)

与上面的三数之和(为0)类似,只是设置一个  minGap,记录三数之和和 target 的最小差值

每次有更小的出现,就去更新这个 minGap ,同时更新此时 minGap 对应的三数之和 sum4MinGap

class Solution {
    public int threeSumClosest(int[] nums, int target) {

        Arrays.sort(nums);
        // 注意这里取最大值用的是 Integer.MAX_VALUE
        int minGap=Integer.MAX_VALUE;
        // 注意题目里要求的返回是最小差值时的三数之和,不是最小差值
        int sum4MinGap = 0;
        for (int i=0;i<nums.length;i++) {
            // 不重复算, 同样的数字之前已经算过了的话
            if (i!=0 && nums[i] == nums[i-1]) {
                continue;
            }
            // 对于 nums[i] 后面的 i+1 到 len-1这段
            int leftIndex = i+1;
            int rightIndex = nums.length-1;
            while(leftIndex < rightIndex) {
                int sum = nums[i] + nums[leftIndex] + nums[rightIndex];
                int curGap = Math.abs(target - sum);
                if (sum < target) {
                    // 偏小,左指针向右移,使和变大
                    leftIndex++;
                    // 记录最小差值
                    if (curGap < minGap) {
                        minGap = curGap;
                        sum4MinGap = sum;
                    }
                }
                else if (sum>target) {
                    // 偏大,右指针向左移,使和变小
                    rightIndex--;
                    // 记录最小差值
                    if (curGap < minGap) {
                        minGap = curGap;
                        sum4MinGap = sum;
                    }
                }
                else if (sum == target) {
                    return sum;
                }
            }
        }
        return sum4MinGap;
    }
}

 

最接近的两数之和

先排序。使用对向双指针的方式,两个指针分别从头尾开始向中间走,若指针指向的两个值的和大于目标值,则将右指针往左一步;若两个值的和小于或等于目标值,则右指针往左一步,在循环过程中进行打擂台,比较当前的差值和记录的最小值,直到数组遍历结束。

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

 

class Solution {
    public int[] twoSum(int[] numbers, int target) {

        int l=0;
        int r=numbers.length-1;
        int[] result = new int[2];
        while (l<r) {
            int sum = numbers[l] + numbers[r];
            // l 向右移动使自己变大使和变大
            if (sum < target) {
                l++;
            }
            // r 向左移动使自己变小使和变小
            else if (sum > target) {
                r--;
            }
            // sum == target
            else {
                // +1 是因为题目里规定下标从1开始
                result[0]=l+1;
                result[1]=r+1;
                while (l<r && numbers[l+1]==numbers[l]) {
                    l++;
                }
                while (l<r && numbers[r-1]==numbers[r]) {
                    r--;
                }
                // 规定只有一个答案,那先 Break。如果有多个答案,那还可以继续找
                break;
            }
        }
        return result;
    }
}

 

 

18. 四数之和

确定一个数后,剩下的用三数之和的解法即可。

外层循环 i=0;i<len-3;i++ , 内层循环 j=i+1;j<len-2;j++, 然后左指针: leftIndex=j+1, rightIndex=len-1 ,后面就是常规的 while(leftIndex<rightIndex)......

注意这样是 i < j < leftIndex < rightIndex 的

为什么 i<len-3 ,因为要留三个位置给后面的 j、leftIndex、rightIndex

为什么 j<len-2,因为要留两个位置给后面的 leftIndex、rightIndex

注意求四数之和与 target 比较的时候,sum 的类型最好为 long ,因为有用例可能取值非常大,四数加起来会超过整数范围

 

【附加剪枝】,减少循环次数【紧急情况下不是必须的】

1、i>0 && nums[i]==nums[i-1] 前面那个i已经算过了,这一次可以 continue

2、j>i+1 && nums[j]==nums[j-1]  j是从 j+1 开始的,即 i 的后半段。在这一段里如果前面那个 j 已经算过了,这一次可以 continue

3、(long) nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target  break

第一个数确定后,与后面最小的三个数加起来还大于 target

【为什么这里 break 而不 continue 】

因为 对于同样的 i , 后续的循环继续对其它三个下标右移的话, 只可能比target更大, 所以可以跳过这个 i

因为 对于同样的 i+1 i+2 i+3 组合,i 没有左移变小的空间了(前面的 i 已经看过), 所以可以跳出整个循环

4、(long) nums[i] + nums[len-1] + nums[len-2] + nums[len-3] < target  continue 

  第一个数确定后,与后面最大的三个数加起来还小于 target

【为什么这里 continue 而不 break】

因为 对于同样的 i , 后续的循环继续对其它三个下标左移的话, 只可能比target更小,所以要跳过这个 i

因为 对于同样的 len-1 len-2 len-3 组合,i 继续右移增大的话还可能反超 target, 所以不能跳出整个循环

。。。。。。

j 的内层循环与 3、4 同理

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> res = new ArrayList();
        Arrays.sort(nums);
        int len = nums.length;
        // 要留有三个位置给后面待确定的三个元素,因此 i<len-3
        for (int i=0;i<len-3;i++) {
            // 确定的第一个数是重复元素,不重复算,跳过
            if (i>0 && nums[i] == nums[i-1]) {
                continue;
            }
            // 第一个数确定后,与后面最小的三个数加起来还大于 target
            // 【有可能是非常大的数字,超过 int 范畴,因此要转为 (long)】
            // 【为什么这里 break 而不 continue】
            // 因为 对于同样的 i , 后续的循环继续对其它三个下标右移的话, 只可能比target更大,所以可以跳过这个 i
            // 因为 对于同样的 i+1 i+2 i+3 组合,i 没有左移变小的空间了(前面的 i 已经看过), 所以可以跳出整个循环
            if ((long) nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target) {
                break;
            }
            // 第一个数确定后,与后面最大的三个数加起来还小于 target(这三个数加起来可能会超过 int 的范畴)
            // 【为什么这里 continue 而不 break】
            // 因为 对于同样的 i , 后续的循环继续对其它三个下标左移的话, 只可能比target更小,所以要跳过这个 i
            // 因为 对于同样的 len-1 len-2 len-3 组合,i 继续右移增大的话还可能反超 target, 所以不能跳出整个循环
            if ((long) nums[i] + nums[len-1] + nums[len-2] + nums[len-3] < target) {
                continue;
            }

            
            // 【j=i+1】开始 下面就是熟悉通用的三数之和
            // 要留有两个位置给后面待确定的两个元素,因此 j<len-2
            for (int j=i+1;j<len-2;j++) {
                // 确定了 i 后,让后面的 j 不重复
                if (j>i+1 && nums[j] == nums[j-1]) {
                    continue;
                }
                if ((long) nums[i] + nums[j] + nums[j+1] + nums[j+2] > target) {
                    break;
                }
                if ((long) nums[i] + nums[j] + nums[len-1] + nums[len-2] < target) {
                    continue;
                }
                
                
                // 下面就是熟悉通用的三数之和
                int leftIndex = j+1;
                int rightIndex = len-1;
                while (leftIndex < rightIndex) {
                    long sum = nums[i] + nums[j] + nums[leftIndex] + nums[rightIndex];
                    if (sum < target) {
                        leftIndex++;
                    }
                    else if (sum > target) {
                        rightIndex--;
                    }
                    else if (sum == target) {

                        res.add(Arrays.asList(nums[i], nums[j], nums[leftIndex], nums[rightIndex]));

                        // 因为是排序过的。左指针去重【这里就保证了四元组不重复】
                        // 注意条件限制 leftIndex<rightIndex
                        while(leftIndex<rightIndex && nums[leftIndex] == nums[leftIndex+1]) leftIndex++;

                        // 因为是排序过的。右指针去重【这里就保证了四元组不重复】
                        // 注意条件限制 leftIndex<rightIndex
                        while(leftIndex<rightIndex && nums[rightIndex] == nums[rightIndex-1]) rightIndex--;

                        // 【去重之后还要继续往下走】
                        leftIndex ++;
                        rightIndex --;
                    }
                }
            }
        }
        return res;
    }


}

 

283. 移动零

我的解法:

  • 左指针一直指的是最后结果的右边界,也就是说最后一定是 左指针以左的元素里没有 0 值,左指针以右的元素都是0值
  • 如果左指针位置为 0,那么右指针一直向右直到找到一个不为 0 的值与左指针交换。left++ right++
  • 如果左指针位置不为0,那么 left++ right++

更好的解法:右指针去寻找所有不为0的,左边视原数组为无物,从头挨个开始放

  • 每当右指针处不为0 nums[right] != 0,就将它放到左指针的位置 nums[left] = nums[right]  left++ right++
  • 每当右指针为 0,就将只有 right++
  • 最后 0~左指针 就是结果,把 左指针~末尾 全部置为0 即可
class Solution {
    public void moveZeroes(int[] nums) {
        int left = 0;
        int right = 0;
        while(left<nums.length && right<nums.length) {
            if (nums[right] != 0) {
                nums[left] = nums[right];
      
    left++;
right
++; } else { right++; } } for (int i=left;i<nums.length;i++) { nums[i] = 0; } } public void moveZeroes2(int[] nums) { int left = 0; int right = 0; while(left<nums.length && right<nums.length) { if (nums[left] == 0) { // 如果后面都是 0,那么 right 一直右移也找不到非 0 的就会移出边界 // 所以要在前面加上判断条件 right<nums.length while(right<nums.length && nums[right] == 0) { right++; } // 提前 break,不再 swap if (right == nums.length) { break; } swap(nums, left, right); left++; right++; } else { left++; right++; } } } private void swap(int[] nums, int index1, int index2) { int tmp = nums[index1]; nums[index1] = nums[index2]; nums[index2] = tmp; } }

 

 

26. 删除有序数组中的重复项

就是将不重复的元素复制到数组的左边

双指针:l r

如果 nums[l] == nums[r] 那么 r 继续向右寻找不与 nums[l] 相等 的元素

如果 nums[l] != nums[r] 那么 把这个不相等的 nums[r] 复制到 l 的右边。同时 l 和 r 同时向右 移动

class Solution {
    public int removeDuplicates(int[] nums) {
        // 左指针
        int left=0;
        // 右指针负责找不重复的,不重复的话把不重复的依次复制到左指针的右边(占据重复元素的位置)
        int right=0;
        while(right < nums.length) {
            // index2 右指针找到的都是重复的,那么继续向右找
            if (nums[right] == nums[left]) {
                right++;
            }
            // 找到不重复的复制到左指针的右边
            else {
                // 考虑 1,2,3,4 如果左右指针挨着,说明它们之间本来就没有重复的,不用多赋值一遍
                if (right - left>1) {
                    nums[left+1] = nums[right];
                }
                left++;
                right++;
            }
        }
        return left + 1;
    }
}

 

56. 合并区间

输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]

按左边界排序,然后左右指针遍历新数组。左指针:上一个区间; 右指针:当前区间

  • 如果当前区间左边界小于等于上一个区间的右边界,则说明是重叠边界更新当前区间的左右边界,将当前区间的左右边界扩展为重叠区间的最大
  • 如果当前区间的左边界大于上一个区间的右边界,将上一个区间放入结果数组。

遍历完毕,再将数组最后一个区间放入结果数组。

class Solution {
    public int[][] merge(int[][] intervals) {
        // 注意这里怎么根据起始元素排序
        Arrays.sort(intervals, (e1, e2)->e1[0]-e2[0]);
        // 结果数组长度先设为和 intervels 一样
        int[][] res = new int[intervals.length][2];
        // 左指针
        int l = 0;
        // 右指针
        int r = 1;
        // 结果数组现在复制到哪儿了
        int resNowIndex = 0;
        // l<r 为了使得最后一个元素能被复制,判断条件是 l
        while(l < intervals.length) {
            // 重叠区间:右指针区间的起始小于等于左指针区间的结束  [1,"3"]["2",6][8,10]
            if (r < intervals.length && intervals[r][0] <= intervals[l][1]) {
                // 将右指针的区间更新为融合了左边界之后的最大 [1,3][2,6][8,10]-->[1,3][1,6][8,10]
                if (intervals[r][1] >= intervals[l][1]) {
                    intervals[r][0] = intervals[l][0];
                }
                // 注意 [1,4][2,3] 要合并成 [1,4]
                else if (intervals[r][1] < intervals[l][1]) {
                    intervals[r][0] = intervals[l][0];
                    intervals[r][1] = intervals[l][1];
                }
            }
            else {
                // 不是重叠区间,就将上一个结果放到答案中,将 [1,6] 放到答案中
                res[resNowIndex][0] = intervals[l][0];
                res[resNowIndex][1] = intervals[l][1];
                resNowIndex++;
            }
            r++;
            l++;
        }
        // 注意怎么去掉数组多出来的0
        return Arrays.copyOf(res, resNowIndex);

    }
}

 

31. 下一个排列

来自题解:https://leetcode.cn/problems/next-permutation/solution/xia-yi-ge-pai-lie-suan-fa-xiang-jie-si-lu-tui-dao-/

以求 12385764 的下一个排列为例:

首先从后向前查找第一个相邻升序的元素对 (i,j)。这里 i=4j=5,对应的值为 57

 然后在 [j,end) 从后向前查找第一个大于 A[i] 的值 A[k]。这里 A[i] 是 5,故 A[k] 是 6

 将 A[i] 与 A[k] 交换。这里交换 56

 这时 [j,end) 必然是降序,逆置 [j,end),使其升序。这里逆置 [7,5,4]

 

因此,12385764 的下一个排列就是 12386457

最后再可视化地对比一下这两个相邻的排列(橙色是蓝色的下一个排列):

class Solution {
    public void nextPermutation(int[] nums) {
        int len = nums.length;


        int i=len-2;
        int j=len-1;
        // i,j 是从后往前查找的第一个升序对
        while(i>=0) {
            if (nums[i]<nums[j]) {
                break;
            }
            i--;
            j--;
        }

        // 全部降序 是最大的情况 654321 的下一个重新变成 123456 
        if (i == -1) {
            Arrays.sort(nums);
            return;
        }

    
        // nums[i] nums[j] 第一个升序对。现在找 k,是从后往前 [j,end) 中最后一个大于 nums[i] 的
        int k=len-1;
       while (k>j) {
            if (nums[k] > nums[i]) {
                break;
            }
            k--;
        }
        //查找区间包括 j,  k 有可能等于j


        // 交换 nums[i] 与 nums[k]
        swap(nums, i, k);

        // 此时 [j,end) 【必然为降序,反转使其升序】。以后面尽可能大,靠近前面nums[i] 抬上去的数
        for (int l=j,r=len-1;l<r;l++,r--) {
            // 逆序即 [j,end) :首尾两两交换,直到中间
            swap(nums, l, r);
        }

        return;

    }

    private void swap(int[] nums, int a, int b) {
        int temp = nums[a];
        nums[a] = nums[b];
        nums[b] = temp;
    }
}

 

27. 移除元素

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

 

这个题解里面有动画说明

https://leetcode.cn/problems/remove-element/solution/27-yi-chu-yuan-su-bao-li-jie-fa-shuang-zhi-zhen-ji/

class Solution {
    public int removeElement(int[] nums, int val) {
        // 慢指针用来指向当前结果集的最右端
        int slowIndex = 0;
        // 快指针用来逐个遍历
        int fastIndex = 0;
        // 快指针每一步都走
        for (fastIndex = 0; fastIndex < nums.length; fastIndex++) {
            // 如果 nums[fastIndex] == val ,那么这个位置是要被移除的,不能放进结果集里,slowIndex 不动
            if(nums[fastIndex] == val) {
            }
            // 如果 nums[fastIndex] != val ,那么这个位置可以被放进结果集里
            else {
                nums[slowIndex] = nums[fastIndex];
                slowIndex++;
            }
        }
        return slowIndex;
    }
}

 

88. 合并两个有序数组

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

nums1 的长度为 m+n,有效长度只有m,后面的 n 是填充的0

不用开辟一个新数组来存放合并结果,只需 从后往前 遍历nums1 和 nums2,选出大的放在 nums1 的末尾 (官方题解有证明这样永远不会占掉 nums1 前面的)

注意写法(对于一个遍历完了,一个遍历没完的情况,不用写那么多判断分支):

  • 循环条件是 p1>-1 || p2>-1 ,是 nums1 和 nums2 都没遍历完
  • 如果 nums1 遍历完了 nums2 没遍历完,就是在循环里判断 p1==-1
  • 如果 nums2 遍历完了 nums2 没遍历完,就是在循环里判断 p2==-1
class Solution {

    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int p1 = m-1;
        int p2 = n-1;
        // 双指针,从尾到头遍历,将nums1和nums2中较大的放到 nums1 后面空出来的位置0
        int tail = m+n-1;
        while(p1>-1 || p2>-1) {
            // nums1 已经遍历完了(p1==-1),nums2 还没完(p2>0)
            if (p1 == -1) {
                nums1[tail--] = nums2[p2--];
            }
            // nums2 已经遍历完了(p2==-1),nums1 还没完(p1>0)
            else if (p2 == -1) {
                nums1[tail--] = nums1[p1--];
            }
            // 下面三种都是 nums1 和 nums2 都没完的情况
            else if (nums1[p1]>nums2[p2]) {
                nums1[tail--] = nums1[p1--];
            }
            else if (nums1[p1]<nums2[p2]) {
                nums1[tail--] = nums2[p2--];
            }
            else if (nums1[p1]==nums2[p2]) {
                nums1[tail--] = nums1[p1--];
                nums1[tail--] = nums2[p2--];
            }
        }
    }

 

75. 颜色分类

官方题解 解法三

class Solution {
    public void sortColors(int[] nums) {
        // 0 元素的下一个位置。整个数组的前面,向后增长
        int p0=0;
        // 2 元素的下一个位置。整个数组的后面,向前增长
        int p2=nums.length-1;
        
        // i 走到 p2 即 2 元素的起始位置,应该终止
        for (int i=0;i<=p2;i++) {
            // 因为 p2 位置可能也是2,因此被错误地换到 i 之后。还要再放到末尾去
            // 0 2 1 2 如 i是第一个2 p2是第二个2 i和p2交换后 p2--在1地位置,现在还要把 i 位置的2再放到末尾去
            while(i<=p2 && nums[i] == 2) {
                swap(nums, i, p2);
                // 从后往前
                p2--;
            }
            if (nums[i]==0) {
                swap(nums, i, p0);
                p0++;
            }
        }
    }

   

    public void swap(int[] nums, int a, int b) {
        int tmp = nums[a];
        nums[a] = nums[b];
        nums[b] = tmp;
    }

    
}

 

 

11. 盛最多水的容器

 官方题解:https://leetcode.cn/problems/container-with-most-water/solution/sheng-zui-duo-shui-de-rong-qi-by-leetcode-solution/

在初始时,左右指针分别指向数组的左右两端

此时我们需要移动一个指针。移动哪一个呢?直觉告诉我们,应该移动对应数字较小的那个指针。这是因为,由于容纳的水量是由

两个指针指向的数字中较小值 指针之间的距离

决定的。如果我们移动数字较大的那个指针,那么前者「两个指针指向的数字中较小值」不会增加,后者「指针之间的距离」会减小,那么这个乘积会减小。因此,我们移动数字较大的那个指针是不合理的。因此,我们移动数字较小的那个指针。

while (leftIndex < rightIndex) 就一直移动,每移动一次就计算一次容量,然后在这个过程中比较得出最大值
class Solution {
    public int maxArea(int[] height) {
        int left=0;
        int right=height.length-1;
        int max = 0;
        while (left<right) {
            int curCap = Math.min(height[left], height[right]) * (right - left);
            if (curCap>max) {
                max = curCap;
            }
            if (height[right] < height[left]) {
                right--;
            }
            else {
                left++;
            }
        }
        return max;
    }
}

 

 125. 验证回文串

 

class Solution {
    public boolean isPalindrome(String s) {
        int l=0;
        int r=s.length()-1;
        while (l<r) {
            // 右移到第一个有效的
            while (l<r && !isValid(s.charAt(l))) {
                l++;
            }
            // 左移到第一个有效的
            while (l<r && !isValid(s.charAt(r))) {
                r--;
            }
            // l==r 例如 aca。后面的例如 aa 
            if (l == r || (l+1 == r && isEquals(s.charAt(l), s.charAt(r)))) {
                return true;
            }
            if (l<r && isEquals(s.charAt(l), s.charAt(r))) {
                l++;
                r--;
            }
            else {
                return false;
            }
        }
        if (l==r) {
            return true;
        }
        else {
            return false;
        }
    }

    // 判断是否为字母或数字 注意是 >= <= 而不是 > <
    private boolean isValid(char c) {
        if ((c>='A' && c<='Z') || (c>='a' && c<='z') || (c>='0' && c<='9')) {
            return true;
        }
        else {
            return false;
        }
    }

    // 判断大小写字母是否相等
    private boolean isEquals(char a, char b) {
        if (a>='A' && a<='Z') {
            a = (char) (a - ('A'-'a'));
        }
        if (b>='A' && b<='Z') {
            b = (char) (b - ('A'-'a'));
        }
        return a==b;
    }
}

 

 392. 判断子序列

为短串中的每一个字符,依次在长串中寻找。i=0 j=0

如果相等,两个都移动;如果不相等,只长串中的那个移动,去为短串当前字符继续寻找相等的

class Solution {
    public boolean isSubsequence(String s, String t) {
        int i=0;
        int j=0;
        while (i<s.length() && j<t.length()) {
            // 如果相等,都移动一步
            if (s.charAt(i) == t.charAt(j)) {
                i++;
                j++;
            }
            // 如果不相等,长的那个移动一步去为"子序列"寻找下一个数字
            else {
                j++;
            }
        }
        // 如果 "子序列" 走完了,说明是子序列
        return i==s.length();
    }
}