【丁】数组,序列,指针

文章目录

  • 1. 把数组中的 0 移到末尾
  • 2. 改变矩阵维度
  • 3. 找出数组中最长的连续 1
  • 4. 有序矩阵查找
  • 5. 有序矩阵的 Kth Element
  • 6. 一个数组元素在 [1, n] 之间,其中一个数被替换为另一个数,找出重复的数和丢失的数
  • 7. 找出数组中重复的数,数组值在 [1, n] 之间
  • 8. 数组相邻差值的个数
  • 9. 数组的度
  • 10. 对角元素相等的矩阵
  • 12. 分隔数组
    • 13.最长连续数列
    • 旋转矩阵
    • [228. 汇总区间](https://leetcode-cn.com/problems/summary-ranges/)
    • [238. 除自身以外数组的乘积](https://leetcode-cn.com/problems/product-of-array-except-self/)
    • [239. 滑动窗口最大值](https://leetcode-cn.com/problems/sliding-window-maximum/)
    • [240. 搜索二维矩阵 II](https://leetcode-cn.com/problems/search-a-2d-matrix-ii/)
    • [242. 有效的字母异位词](https://leetcode-cn.com/problems/valid-anagram/)
    • [287. 寻找重复数](https://leetcode-cn.com/problems/find-the-duplicate-number/)
    • [31. 下一个排列](https://leetcode-cn.com/problems/next-permutation/)
    • 最佳观光组合
  • 用到排序的算法
  • 子数组
    • 乘积最大子数组
    • 连续子数组的最大和
    • 环形子数组的最大和
    • 和为K的子数组
  • 类似链表
    • 跳跃游戏 II
    • 跳跃索引
    • 魔术索引(跳跃查找)
  • 二分查找
  • 二维数组问题
  • 区间问题
    • 合并区间
  • 双指针
    • 1. 有序数组的 Two Sum
    • 2. 两数平方和
    • 3. 反转字符串中的元音字符
    • 4. 回文字符串
    • 5. 归并两个有序数组
    • 6. 判断链表是否存在环
    • 7. 最长子序列
    • 8. 长度最小的子数组
    • 颜色分类
    • 判断子序列
  • 滑动窗口
    • 找到字符串中所有字母异位词

部分转自:

1. 把数组中的 0 移到末尾

283. Move Zeroes (Easy)

Leetcode / 力扣

For example, given nums = [0, 1, 0, 3, 12], after calling your function, nums should be [1, 3, 12, 0, 0].
public void moveZeroes(int[] nums) {
    int idx = 0;
    for (int num : nums) {
        if (num != 0) {
            nums[idx++] = num;
        }
    }
    while (idx < nums.length) {
        nums[idx++] = 0;
    }
}

2. 改变矩阵维度

566. Reshape the Matrix (Easy)

Leetcode / 力扣

Input:
nums =
[[1,2],
 [3,4]]
r = 1, c = 4

Output:
[[1,2,3,4]]

Explanation:
The row-traversing of nums is [1,2,3,4]. The new reshaped matrix is a 1 * 4 matrix, fill it row by row by using the previous list.
//遍历一个矩阵的新方式

public int[][] matrixReshape(int[][] nums, int r, int c) {
    int m = nums.length, n = nums[0].length;
    if (m * n != r * c) {
        return nums;
    }
    int[][] reshapedNums = new int[r][c];
    int index = 0;
    for (int i = 0; i < r; i++) {
        for (int j = 0; j < c; j++) {
            reshapedNums[i][j] = nums[index / n][index % n];
            index++;
        }
    }
    return reshapedNums;
}

3. 找出数组中最长的连续 1

485. Max Consecutive Ones (Easy)

Leetcode / 力扣

public int findMaxConsecutiveOnes(int[] nums) {
    int max = 0, cur = 0;
    for (int x : nums) {
        cur = x == 0 ? 0 : cur + 1;
        max = Math.max(max, cur);
    }
    return max;
}


//我的,复杂度小一点
class Solution {
    int max = 0;
    public int findMaxConsecutiveOnes(int[] nums) {
        int res = 0;
        int cur = 0;
        for(int i = 0; i < nums.length; i++){
            if(nums[i]==1){
                cur++;
                
            }else{
                res = Math.max(res,cur);
                cur = 0;
            }
        }
        res = Math.max(res,cur);
    return res;     
    }
}

4. 有序矩阵查找

240. Search a 2D Matrix II (Medium)

Leetcode / 力扣

[
   [ 1,  5,  9],
   [10, 11, 13],
   [12, 13, 15]
]
public boolean searchMatrix(int[][] matrix, int target) {
    if (matrix == null || matrix.length == 0 || matrix[0].length == 0) return false;
    int m = matrix.length, n = matrix[0].length;
    int row = 0, col = n - 1;
    while (row < m && col >= 0) {
        if (target == matrix[row][col]) return true;
        else if (target < matrix[row][col]) col--;
        else row++;
    }
    return false;
}

5. 有序矩阵的 Kth Element

378. Kth Smallest Element in a Sorted Matrix ((Medium))

Leetcode / 力扣

matrix = [
  [ 1,  5,  9],
  [10, 11, 13],
  [12, 13, 15]
],
k = 8,

return 13.

��题参考:[Share my thoughts and Clean Java Code

Leetcode / 力扣

二分查找解法:

public int kthSmallest(int[][] matrix, int k) {
    int m = matrix.length, n = matrix[0].length;
    int lo = matrix[0][0], hi = matrix[m - 1][n - 1];
    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;
        int cnt = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n && matrix[i][j] <= mid; j++) {
                cnt++;
            }
        }
        if (cnt < k) lo = mid + 1;
        else hi = mid - 1;
    }
    return lo;
}

堆解法:

public int kthSmallest(int[][] matrix, int k) {
    int m = matrix.length, n = matrix[0].length;
    PriorityQueue<Tuple> pq = new PriorityQueue<Tuple>();
    //先填充第0行的
    for(int j = 0; j < n; j++) pq.offer(new Tuple(0, j, matrix[0][j]));
    for(int i = 0; i < k - 1; i++) { // 小根堆,去掉 k - 1 个堆顶元素,此时堆顶元素就是第 k 的数
        Tuple t = pq.poll();
        if(t.x == m - 1) continue;
        pq.offer(new Tuple(t.x + 1, t.y, matrix[t.x + 1][t.y]));
    }
    return pq.poll().val;
}

class Tuple implements Comparable<Tuple> {
    int x, y, val;
    public Tuple(int x, int y, int val) {
        this.x = x; this.y = y; this.val = val;
    }

    @Override
    public int compareTo(Tuple that) {
        return this.val - that.val;
    }
}

详细点

1.为什么要将寻找值的中间值而不是索引的中间值,因为矩阵的特殊性,

    public int kthSmallest(int[][] matrix, int k) {
        int row = matrix.length;
        int col = matrix[0].length;
        int left = matrix[0][0];
        int right = matrix[row - 1][col - 1];
        while (left < right) {
            // 每次循环都保证第K小的数在start~end之间,当start==end,第k小的数就是start
            int mid = (left + right) / 2;
            // 找二维矩阵中<=mid的元素总个数
            int count = findNotBiggerThanMid(matrix, mid, row, col);
            if (count < k) {
                // 第k小的数在右半部分,且不包含mid
                left = mid + 1;
            } else {
                // 第k小的数在左半部分,可能包含mid
                right = mid;
            }
        }
        return right;
    }

    private int findNotBiggerThanMid(int[][] matrix, int mid, int row, int col) {
        // 以列为单位找,找到每一列最后一个<=mid的数即知道每一列有多少个数<=mid
        int i = row - 1;
        int j = 0;
        int count = 0;
        while (i >= 0 && j < col) {
            if (matrix[i][j] <= mid) {
                // 第j列有i+1个元素<=mid
                count += i + 1;
                j++;
            } else {
                // 第j列目前的数大于mid,需要继续在当前列往上找
                i--;
            }
        }
        return count;
    }

作者:jacksu1024
链接:https://leetcode-cn.com/problems/kth-smallest-element-in-a-sorted-matrix/solution/er-fen-chao-ji-jian-dan-by-jacksu1024/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

6. 一个数组元素在 [1, n] 之间,其中一个数被替换为另一个数,找出重复的数和丢失的数

645. Set Mismatch (Easy)

Leetcode / 力扣

Input: nums = [1,2,2,4]
Output: [2,3]

Input: nums = [1,2,2,4]
Output: [2,3]

最直接的方法是先对数组进行排序,这种方法时间复杂度为 O(NlogN)。本题可以以 O(N) 的时间复杂度、O(1) 空间复杂度来求解。

主要思想是通过交换数组元素,使得数组上的元素在正确的位置上。

public int[] findErrorNums(int[] nums) {
    for (int i = 0; i < nums.length; i++) {
        while (nums[i] != i + 1 && nums[nums[i] - 1] != nums[i]) {
            swap(nums, i, nums[i] - 1);
        }
    }
    for (int i = 0; i < nums.length; i++) {
        if (nums[i] != i + 1) {
            return new int[]{nums[i], i + 1};
        }
    }
    return null;
}

private void swap(int[] nums, int i, int j) {
    int tmp = nums[i];
    nums[i] = nums[j];
    nums[j] = tmp;
}

使用数组代替hash表。循环两次

public int[] findErrorNums2(int[] nums) {
        Arrays.sort(nums);
        int[] result = new int[2];
        for (int i = 0; i < nums.length - 1; i++) {
            if (nums[i + 1] - nums[i] == 0)
                result[0] = nums[i];
            if (nums[i + 1] - nums[i] == 2)
                result[1] = nums[i] + 1;
        }
        if (result[1] == 0) { // 要特别注意缺失第一个元素或者最后一个元素的情况, 这两种情况都会使得result[1]为初始值0
            if (nums[0] != 1)
                result[1] = 1;
            else result[1] = nums.length;
        }
        return result;
    }

作者:ustcyyw
链接:https://leetcode-cn.com/problems/set-mismatch/solution/645java-shu-zu-dai-ti-ha-xi-biao-pai-xu-xiang-jie-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

类似题目:

[448. Find All Numbers Disappeared in an Array (Easy)

Leetcode,寻找所有丢失的元�) / 力扣,寻找所有丢失的元�)
[442. Find All Duplicates in an Array (Medium)

Leetcode,寻找所有重复的元素�) / 力扣,寻找所有重复的元素�)

7. 找出数组中重复的数,数组值在 [1, n] 之间

287. Find the Duplicate Number (Medium)

Leetcode / 力扣

要求不能修改数组,也不能使用额外的空间。

二分查找解法:

public int findDuplicate(int[] nums) {
     int l = 1, h = nums.length - 1;
     while (l <= h) {
         int mid = l + (h - l) / 2;
         int cnt = 0;
         for (int i = 0; i < nums.length; i++) {
             if (nums[i] <= mid) cnt++;
         }
         if (cnt > mid) h = mid - 1;
         else l = mid + 1;
     }
     return l;
}

class Solution {
    public int findDuplicate(int[] nums) {
    //不是索引二分,是值二分
     int l = 1, h = nums.length-1;
     while (l < h) {
        
         //当限制空间或时间的题目都会有特点:本题特点是如果不重复那么小于等于一个数的个数等于这个数本身
        int mid = (l+h)/2;
        int cnt = 0;
        for(int i  = 0; i < nums.length; i++){
            if (nums[i] <= mid)
                cnt+=1;
        }
		//有可能mid是这个重复的数,此时将它看成右边界。
        if(cnt > mid){
            h = mid;
        }else{
            l = mid+1;
        }
     }
     return l;
}
}

双指针解法,类似于有环链表中找出环的入口:

//非常好的方法

public int findDuplicate(int[] nums) {
    int slow = nums[0], fast = nums[nums[0]];
    while (slow != fast) {
        slow = nums[slow];
        fast = nums[nums[fast]];
    }
    fast = 0;
    while (slow != fast) {
        slow = nums[slow];
        fast = nums[fast];
    }
    return slow;
}

8. 数组相邻差值的个数

667. Beautiful Arrangement II (Medium)

Leetcode / 力扣

Input: n = 3, k = 2
Output: [1, 3, 2]
Explanation: The [1, 3, 2] has three different positive integers ranging from 1 to 3, and the [2, 1] has exactly 2 distinct integers: 1 and 2.

题目描述:数组元素为 1~n 的整数,要求构建数组,使得相邻元素的差值不相同的个数为 k。

让前 k+1 个元素构建出 k 个不相同的差值,序列为:1 k+1 2 k 3 k-1 … k/2 k/2+1.

方法二:构造
当 \text{k=n-1}k=n-1 时,有效的构造是 \text{[1, n, 2, n-1, 3, n-2, …]}[1, n, 2, n-1, 3, n-2, …]。我们需要 \text{n-1}n-1 个的不同的相邻整数差,这意味着我们需要 \text{1}1 和 \text{n}n 相邻;然后,我们需要 \text{n-2}n-2 的差异以此类推。
另外,当 \text{k=1}k=1 时,有效的构造是 \text{[1, 2, 3, …, n]}[1, 2, 3, …, n]。所以我们有一个构造,我们可以将 \text{k=n-1}k=n-1 和 \text{k=1}k=1 的构造方法结合起来。我们可以先构造 \text{k=1}k=1 的序列 \text{[1,2,…,n-k-1]}[1,2,…,n-k-1],这样剩下需要构造的序列的长度 \text{n}n 实际上就是 \text{k+1}k+1,然后用 $\text{k=n-1} $方法完成构造。
例如,当 \text{n=6}n=6 和 \text{k=3}k=3 时,我们将数组构造为 \text{[1, 2, 3, 6, 4, 5]}[1, 2, 3, 6, 4, 5]。这包括两部分:构造 \text{[1,2]}[1,2] 和构造 \text{[1,4,2,3]}[1,4,2,3],其中每个元素都添加了 \text{2}2(即 \text{[3,6,4,5]}[3,6,4,5])。
算法:

和上面说的一样,先构造 \text{[1,2,…,n-k-1]}[1,2,…,n-k-1]。剩下的 \text{k+1}k+1 个元素是 \text{[n-k,n-k+1,…,n]}[n-k,n-k+1,…,n],我们将按头尾顺序交替写入。
复杂度分析

时间复杂度:O(n)O(n)。
空间复杂度:O(n)O(n),用来存放答案的数组长度等于 \text{n}n。

作者:LeetCode
链接:https://leetcode-cn.com/problems/beautiful-arrangement-ii/solution/you-mei-de-pai-lie-ii-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

public int[] constructArray(int n, int k) {
    int[] ret = new int[n];
    ret[0] = 1;
    for (int i = 1, interval = k; i <= k; i++, interval--) {
        ret[i] = i % 2 == 1 ? ret[i - 1] + interval : ret[i - 1] - interval;
    }
    for (int i = k + 1; i < n; i++) {
        ret[i] = i + 1;
    }
    return ret;
}

9. 数组的度

697. Degree of an Array (Easy)

Leetcode / 力扣

Input: [1,2,2,3,1,4,2]
Output: 6

题目描述:数组的度定义为元素出现的最高频率,例如上面的数组度为 3。要求找到一个最小的子数组,这个子数组的度和原数组一样。

public int findShortestSubArray(int[] nums) {
    Map<Integer, Integer> numsCnt = new HashMap<>();
    Map<Integer, Integer> numsLastIndex = new HashMap<>();
    Map<Integer, Integer> numsFirstIndex = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int num = nums[i];
        numsCnt.put(num, numsCnt.getOrDefault(num, 0) + 1);
        numsLastIndex.put(num, i);
        if (!numsFirstIndex.containsKey(num)) {
            numsFirstIndex.put(num, i);
        }
    }
    int maxCnt = 0;
    for (int num : nums) {
        maxCnt = Math.max(maxCnt, numsCnt.get(num));
    }
    int ret = nums.length;
    for (int i = 0; i < nums.length; i++) {
        int num = nums[i];
        int cnt = numsCnt.get(num);
        if (cnt != maxCnt) continue;
        ret = Math.min(ret, numsLastIndex.get(num) - numsFirstIndex.get(num) + 1);
    }
    return ret;
}

官方的map的两种用法值得学习

	public int findShortestSubArray2(int[] nums) {
        Map left = new HashMap<>(), right = new HashMap<>(), count = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            int x = nums[i];
            left.putIfAbsent(x, i); // 如果x是第一次出现,放入Map中,从而left可以记录某个数字第一次出现的索引
            right.put(x, i);
            count.put(x, count.getOrDefault(x, 0) + 1); // 记录x出现的次数
        }
        int maxCount = Collections.max(count.values());
        int result = nums.length;
        for (int key : count.keySet()) {
            if (count.get(key) == maxCount)
                result = Math.min(result, right.get(key) - left.get(key) + 1);
        }
        return result;
    }

作者:ustcyyw
链接:https://leetcode-cn.com/problems/degree-of-an-array/solution/697java-liang-chong-fang-fa-xiang-jie-by-ustcyyw/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

10. 对角元素相等的矩阵

766. Toeplitz Matrix (Easy)

Leetcode / 力扣

1234
5123
9512

In the above grid, the diagonals are "[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]", and in each diagonal all elements are the same, so the answer is True.

public boolean isToeplitzMatrix(int[][] matrix) {
    for (int i = 0; i < matrix[0].length; i++) {
        if (!check(matrix, matrix[0][i], 0, i)) {
            return false;
        }
    }
    for (int i = 0; i < matrix.length; i++) {
        if (!check(matrix, matrix[i][0], i, 0)) {
            return false;
        }
    }
    return true;
}

private boolean check(int[][] matrix, int expectValue, int row, int col) {
    if (row >= matrix.length || col >= matrix[0].length) {
        return true;
    }
    if (matrix[row][col] != expectValue) {
        return false;
    }
    return check(matrix, expectValue, row + 1, col + 1);
}

只需与左上角相等就行

public boolean isToeplitzMatrix(int[][] matrix) {
        int column = matrix[0].length;
        for (int i = 1; i < matrix.length; i++) {
            for (int j = 1; j < column; j++) {
                if (matrix[i][j] != matrix[i - 1][j - 1])
                    return false;
            }
        }
        return true;
    }

作者:ustcyyw
链接:https://leetcode-cn.com/problems/toeplitz-matrix/solution/766java-mei-ci-jin-shi-yong-liang-ge-yuan-su-by-us/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

另一种方法,将对角线上的元素的特征找出来。

class Solution {
    public boolean isToeplitzMatrix(int[][] matrix) {
        Map groups = new HashMap();
        for (int r = 0; r < matrix.length; ++r) {
            for (int c = 0; c < matrix[0].length; ++c) {
                if (!groups.containsKey(r-c))
                    groups.put(r-c, matrix[r][c]);
                else if (groups.get(r-c) != matrix[r][c])
                    return False;
            }
        }
        return True;
    }
}

作者:LeetCode
链接:https://leetcode-cn.com/problems/toeplitz-matrix/solution/tuo-pu-li-ci-ju-zhen-by-leetcode/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

565. Array Nesting (Medium)

Leetcode / 力扣

Input: A = [5,4,0,3,1,6,2]
Output: 4
Explanation:
A[0] = 5, A[1] = 4, A[2] = 0, A[3] = 3, A[4] = 1, A[5] = 6, A[6] = 2.

One of the longest S[K]:
S[0] = {A[0], A[5], A[6], A[2]} = {5, 6, 2, 0}

题目描述:S[i] 表示一个集合,集合的第一个元素是 A[i],第二个元素是 A[A[i]],如此嵌套下去。求最大的 S[i]。

//这么做的依据:只需要访问一次数组元素 2.不需要记录最长的序列包含哪些元素,只需要个数。

public int arrayNesting(int[] nums) {
    int max = 0;
    for (int i = 0; i < nums.length; i++) {
        int cnt = 0;
        for (int j = i; nums[j] != -1; ) {
            cnt++;
            int t = nums[j];
            nums[j] = -1; // 标记该位置已经被访问
            j = t;

        }
        max = Math.max(max, cnt);
    }
    return max;
}

我的,使用map和set

class Solution {
    public int arrayNesting(int[] nums) {
        int cnt = 0;
        int max = 0;
        Set<Integer> set = new HashSet<>();
        Map<Integer,Integer> res = new HashMap<>();
        for(int i = 0; i < nums.length; i++){
            int t = i;
            set.clear();
            while(!set.contains(nums[t])){
                set.add(nums[t]);
                t = nums[t];
                cnt++; 
            }
            int siz = set.size();
            max = Math.max(max, siz);
            for(Integer r : set){
                res.put(r, siz);
            }
            if(cnt>=nums.length){
                break;
            }
        }
        return max;
    }
}

12. 分隔数组

769. Max Chunks To Make Sorted (Medium)

Leetcode / 力扣

Input: arr = [1,0,2,3,4]
Output: 4
Explanation:
We can split into two chunks, such as [1, 0], [2, 3, 4].
However, splitting into [1, 0], [2], [3], [4] is the highest number of chunks possible.

题目描述:分隔数组,使得对每部分排序后数组就为有序。

//和我的做法一样:当前值是历史最大值就可以。

public int maxChunksToSorted(int[] arr) {
    if (arr == null) return 0;
    int ret = 0;
    int right = arr[0];
    for (int i = 0; i < arr.length; i++) {
        right = Math.max(right, arr[i]);
        if (right == i) ret++;
    }
    return ret;
}

13.最长连续数列

128. 最长连续序列

如果用排序+并查集 复杂度太大

class Solution {
    public int longestConsecutive(int[] nums) {
        
        //int pre = 0;
        if(nums==null || nums.length==0){
            return 0;
        }
        int cur = 1;
        int res = 1;
        Arrays.sort(nums);
        for(int i = 1; i < nums.length; i++){
            if(nums[i] == nums[i-1]){
                continue;
            }
            else if(nums[i] == nums[i-1] + 1){
                cur++;
            }else{
                res = Math.max(res, cur);
                cur = 1;
            }            
        }
        res = Math.max(res, cur);
        return res;
    }
}

使用hashset

旋转矩阵

189. 旋转数组

下面两部分旋转法在遇到k > len的时候不可以

class Solution {
    public void rotate(int[] nums, int k) {
        //法1:两部分旋转        
        int len = nums.length;
        if(k > len) return;
        helper(nums, 0, len - k -1);
        helper(nums, len - k, len - 1);
        helper(nums, 0, len - 1);
        
    }

    private void helper(int[] nums, int i, int j) {
        while (i < j){
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
            i++;
            j--;
        }
    }
}

改进一下可以 加了一句 k = k % len;可以

class Solution {
    public void rotate(int[] nums, int k) {
        //法1:两部分旋转        
        int len = nums.length;
        k = k % len;
        helper(nums, 0, len - k -1);
        helper(nums, len - k, len - 1);
        helper(nums, 0, len - 1);

    }

    private void helper(int[] nums, int i, int j) {
        while (i < j){
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
            i++;
            j--;
        }
    }
}

下面的想法是 原地替换

每次顶替一个,然后再顶替刚才顶替的原位置的,不对,有可能出现比如3个数字换完后的位置均在这三个数字间

class Solution {
    public void rotate(int[] nums, int k) {
        int len = nums.length;
        int t = 0;
        int cnt = 0;
        int pre = nums[0];
        while(cnt < len){
            int temp = nums[(t+k) % len];
            nums[(t+k) % len] = pre;
            t = (t+k) % len;
            pre = temp;
            cnt++;
        }
    }
}

这个可以:确保了不会原地打转,另为什么要用 ? do while 因为循环体的起始条件是 i=t,结束条件t = i 必须执行一次才不会"一开始就结束了"

class Solution {
    public void rotate(int[] nums, int k) {
        int len = nums.length;
        int cnt = 0;
        for (int i = 0; cnt < len; i++) {
            int pre = nums[i];
            int t = i;
            do{
                int temp = nums[(t+k) % len];
                nums[(t+k) % len] = pre;
                t = (t+k) % len;
                pre = temp;
                cnt++;
            } while(t != i);
        }
    }
}

直接拼接也可以

class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        n = len(nums)
        k %= n
        nums[:] = nums[-k:] + nums[:-k]

228. 汇总区间

双指针法:
i与i+1比较,不满足则计算结果,start更新
注意最后一个元素一定要计算结果

class Solution {
    public List<String> summaryRanges(int[] nums) {
        List<String> res = new ArrayList<>();
        //记录区间起和cur
        int start  = 0;
        for (int i = 0; i < nums.length; i++) {
            if(i + 1 == nums.length || nums[i] != nums[i+1]-1){
                if(start == i) res.add(nums[i]+"");
                else res.add(nums[start] + "->" + nums[i]);
                start = i+1;
            }
        }
        return res;
    }
}

238. 除自身以外数组的乘积

有点动态规划的意思

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int len = nums.length;
        int[] left = new int[len];
        int[] right = new int[len];
        int[] res = new int[len];
        left[0] = 1;
        right[len-1] = 1;
        for (int i = 1; i < len; i++) {
            left[i] = left[i-1] * nums[i-1];
            right[len - i - 1] = right[len - i] * nums[len - i];
        }
        for (int i = 0; i < len; i++) {
            res[i] = left[i] * right[i];
            
        }
        return res;
    }
}

239. 滑动窗口最大值

https://leetcode-cn.com/problems/sliding-window-maximum/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-5-3/#comment

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int[] win = new int[k];
        int len = nums.length;
        int[] res = new int[len - k + 1];
        if(k * len == 0) return res;
        if(k == 1) return nums;
       //把整个数组分块,l[i]标识块边界到i的最大的数,r[j]标识j到块边界的最大的值

        int[] l = new int[len];
        int[] r = new int[len];
        l[0] = nums[0];
        r[len - 1] = nums[len - 1];

        for (int i = 1; i < len - 1; i++) {
            if(i % k == 0) l[i] = nums[i];
            else l[i] = Math.max(l[i - 1], nums[0]);
            //j代表
            int j = len - i - 1;
            if((j+1) % k == 0) r[j] = nums[j];
            else r[i] = Math.max(r[j + 1], nums[j]);
        }
        for (int i = 0; i < len - k + 1; i++)
            res[i] = Math.max(l[i + k - 1], r[i]);
        return res;
    }
}

240. 搜索二维矩阵 II

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        if(matrix==null || matrix.length==0) return false;        
        int row = matrix.length;
        int col = matrix[0].length;
        int i = 0;
        int j = col-1;
        while (i < row && j >= 0) {
                if(matrix[i][j] > target){
                    j--;
                }
                else if(matrix[i][j] < target){
                    i++;
                }
                else return true;
        }
        return false;
    }
}

数组起到hash表的作用

242. 有效的字母异位词

很巧妙,一个一维数组就可以

  • 索引代表字母,值代表出现的个数
  • s出现某个字符就加,t出现就减,如果都为0肯定是异位词
public boolean isAnagram(String s, String t) {
    if (s.length() != t.length()) {
        return false;
    }
    int[] counter = new int[26];
    for (int i = 0; i < s.length(); i++) {
        counter[s.charAt(i) - 'a']++;
        counter[t.charAt(i) - 'a']--;
    }
    for (int count : counter) {
        if (count != 0) {
            return false;
        }
    }
    return true;
}

287. 寻找重复数

方法太多了,很好的一道题
1.双指针法:因为两个数可能存在一个元素,如果将数组转换为指向数组中元素的所以位置的链表,就变成了一个求双指针的问题。

class Solution {
    public int findDuplicate(int[] nums) {
        int slow = 0;
        int fast = 0;
        slow = nums[slow];
        fast = nums[nums[fast]];
        while(slow != fast){
            slow = nums[slow];
            fast = nums[nums[fast]];
        }
        int pre1 = 0;
        int pre2 = slow;
        while(pre1 != pre2){
            pre1 = nums[pre1];
            pre2 = nums[pre2];
        }
        return pre1;
    }
}

法2
二分查找

每次统计小于mid的个数与mid的大小,大于num说明重复数在l和mid之间。

class Solution {
    public int findDuplicate(int[] nums) {
        int n = nums.length;
        int l = 1, r = n - 1, ans = -1;
        while (l <= r) {
            int mid = (l + r) >> 1;
            int cnt = 0;
            for (int i = 0; i < n; ++i) {
                if (nums[i] <= mid) {
                    cnt++;
                }
            }
            if (cnt <= mid) {
                l = mid + 1;
            } else {
                r = mid - 1;
                ans = mid;
            }
        }
        return ans;

    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/find-the-duplicate-number/solution/xun-zhao-zhong-fu-shu-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

31. 下一个排列

方法1:写出所有的全排列,然后排序就是下一个排列
方法2:一遍扫描法.
从后向前扫描一遍,遇到i比i+1大的直接停止扫描,将后面的比i大的最小值k处的元素与i替换,然后翻转k后面的所有的元素.

public class Solution {
    public void nextPermutation(int[] nums) {
        int i = nums.length - 2;
        //不知道什么时候结束的遍历最好用while,效果等于break
        while (i >= 0 && nums[i + 1] <= nums[i]) {
            i--;
        }
        if (i >= 0) {
            int j = nums.length - 1;
            while (j >= 0 && nums[j] <= nums[i]) {
                j--;
            }
            swap(nums, i, j);
        }
        reverse(nums, i + 1);
    }

    private void reverse(int[] nums, int start) {
        int i = start, j = nums.length - 1;
        while (i < j) {
            swap(nums, i, j);
            i++;
            j--;
        }
    }

    private void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

我的思路一样但非常丑陋的实现
说明while和for还是

    public void nextPermutation(int[] nums) {
        for (int i = nums.length - 2; i >= 0; i--) {
            if (nums[i] < nums[i + 1]){
                int k = nums.length - 1;
                for (; k > i; k--){
                    if (nums[k] > nums[i]){
                        break;
                    }
                }
                swap(nums,i,k);
                reverse(nums,i + 1);
                break;
                return;
            }
        }
        reverse(nums,0);        
    }

最佳观光组合

1014. 最佳观光组合

显然是个两层循环的思路.但是使用一层循环就可以做了.

  • 遍历当前的j,记录历史的a[i] + i 的最大值.这是个动态规划类的问题,只不过使用一个值记录
class Solution {
    public int maxScoreSightseeingPair(int[] A) {
        int ans = 0, mx = A[0] + 0;
        for (int j = 1; j < A.length; ++j) {
            ans = Math.max(ans, mx + A[j] - j);
            // 边遍历边维护
            mx = Math.max(mx, A[j] + j);
        }
        return ans;
    }
}

用到排序的算法

h指数

275. H指数 II

class Solution {
    public int hIndex(int[] citations) {
        Arrays.sort(citations);
        int res = 0;
        int len = citations.length;
        for (int i = 0; i < len; i++) {
            int temp = Math.min(citations[i],len - i);
            res = Math.max(res, temp);
        }
        return res;
    }
}

子数组

乘积最大子数组

152. 乘积最大子数组

最关键的一点在于:

当乘完一个数之后,最大的值可能变为最小的,最小的他也可能最大的。

class Solution {
    public int maxProduct(int[] nums) {
        int max = Integer.MIN_VALUE, imax = 1, imin = 1;
        for(int i=0; i<nums.length; i++){
            if(nums[i] < 0){ 
              int tmp = imax;
              imax = imin;
              imin = tmp;
            }
            imax = Math.max(imax*nums[i], nums[i]);
            imin = Math.min(imin*nums[i], nums[i]);
            
            max = Math.max(max, imax);
        }
        return max;
    }
}

连续子数组的最大和

剑指 Offer 42. 连续子数组的最大和

kanba

class Solution {
    public int maxSubArray(int[] nums) {
        int res = nums[0];
        int pre = nums[0];
        for(int i = 1; i < nums.length; i++) {
            pre = nums[i] + Math.max(pre, 0);
            res = Math.max(res, pre);
        }
        return res;
    }
}

环形子数组的最大和

918. 环形子数组的最大和

  • 思路是求数组的最大子数组和为ans1,求最小子数组和为ans2,关键在于求最小子数组时候不能出现全加起来的情况(如全负)。于是求两次最小子数组和,区间分别为[1 : ]和[ : -1]
class Solution {
    public int maxSubarraySumCircular(int[] A) {
        if(A.length == 1) return A[0];
        // S: sum of A
        int S = 0;
        for (int x: A)
            S += x;

        // ans1: answer for one-interval subarray
        int ans1 = Integer.MIN_VALUE;
        int cur = Integer.MIN_VALUE;
        for (int x: A) {
            cur = x + Math.max(cur, 0);
            ans1 = Math.max(ans1, cur);
        }

        // ans2: answer for two-interval subarray, interior in A[1:],从1开始,规避全减去的情况
        int ans2 = Integer.MAX_VALUE;
        cur = Integer.MAX_VALUE;
        for (int i = 1; i < A.length; ++i) {
            cur = A[i] + Math.min(cur, 0);
            ans2 = Math.min(ans2, cur);
        }
        ans2 = S - ans2;

        return Math.max(ans1, ans2);
    }
}

和为K的子数组

560. 和为K的子数组

给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。

示例 1 :

输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。

方法:

  • 前缀和加哈希表
  • 使用哈希的依据就是每一个前缀和都是唯一的
public class Solution {
    public int subarraySum(int[] nums, int k) {
        int count = 0, pre = 0;
        HashMap < Integer, Integer > mp = new HashMap < > ();
        mp.put(0, 1);
        for (int i = 0; i < nums.length; i++) {
            pre += nums[i];
            if (mp.containsKey(pre - k))
                count += mp.get(pre - k);
            mp.put(pre, mp.getOrDefault(pre, 0) + 1);
        }
        return count;
    }
}

类似链表

41. 缺失的第一个正数
我们可以把每个元素存放到对应的位置,比如1存放到数组的第一个位置,3存放到数组的第3个位置,
如果是非正数或者大于数组的长度的值,我们不做处理,最后在遍历一遍数组,如果位置不正确,说明这个位置没有这个数,我们就直接返回

非常巧妙的方法,可以拆分成许多模板比如:原地使用hash;如何使数组产生类似链表的效果(待处理的元素与将要处理的换位置后,待处理元素又更新了,虽然原地踏步但其实在前进。)

    public int firstMissingPositive(int[] nums) {
        for (int i = 0; i < nums.length; i++) {
            //如果在指定的位置就不需要修改
            if (i + 1 == nums[i])
                continue;
            int x = nums[i];
            if (x >= 1 && x <= nums.length && x != nums[x - 1]) {
                swap(nums, i, x - 1);
                i--;//抵消上面的i++,如果交换之后就不++;
            }
        }
        //最后在执行一遍循环,查看对应位置的元素是否正确,如果不正确直接返回
        for (int i = 0; i < nums.length; i++) {
            if (i + 1 != nums[i])
                return i + 1;
        }
        return nums.length + 1;
    }

    //交换两个数的值
    public void swap(int[] A, int i, int j) {
        if (i != j) {
            A[i] ^= A[j];
            A[j] ^= A[i];
            A[i] ^= A[j];
        }
    }

作者:sdwwld
链接:https://leetcode-cn.com/problems/first-missing-positive/solution/javade-6chong-jie-fa-de-tu-wen-xiang-jie-wei-yun-s/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

287. 寻找重复数
题解在上面

跳跃游戏 II

45. 跳跃游戏 II


//顺腾摸瓜法:每个索引的位置都要去寻找。
class Solution {
    public int jump(int[] nums) {
        //上一轮的最大的位置
        int cur = 0;
        //当前这一轮所能跳到的最大的位置
        int maxPos = 0;
        int res = 0;
        for(int i = 0; i < nums.length-1; i++){
            //每次处于
            maxPos = Math.max(nums[i]+i, maxPos);            
            if(i==cur){
                res++;
                cur = maxPos;
            }
        }
        return res;
    }
}

跳跃索引

55. 跳跃游戏

class Solution {
    public boolean canJump(int[] nums) {
        if(nums.length==1) return true;
        int len = nums.length;
       	int maxPos = 0;
	    for (int i = 0; i < nums.length; i++)
	    {
		    if (i > maxPos) return false;
		    maxPos = Math.max(maxPos, i + nums[i]);
	    }
	    return true;
    }
}

魔术索引(跳跃查找)

面试题 08.03. 魔术索引

  • 因为是有序的,所以更新下一个的时候可以跳跃多个。
class Solution {
    public int findMagicIndex(int[] nums) {
        for(int i=0;i

二分查找

287. 寻找重复数
二分法还可以用于确定一个有范围的整数(这个思路很常见);

二维数组问题

总结:类似的矩阵问题有很多,但不管什么问题,有两个技巧是一定用的上的,一定要记住!!!

1、矩阵中某个位置的状态如果发生改变,那么这种题的解法一般是两次遍历整个矩阵。第一遍遍历时,用一个不可能出现在原矩阵中的中间值来保存状态的变化(这样在此次遍历时,不影响其他的位置的判断,比如我们可以用“$”这种没人用的字符);第二遍遍历时,把中间值刷新成为变化后应该变成的值。

2、如果遍历到某个位置时,需要查看它周边的位置,此时如果每一个周围的位置都手写,然后再判断是否越界,就很麻烦。可以先用一个数组保存向周边位置变化的坐标偏移值,一次性通过一个循环,来遍历完周边的位置,并且方便进行越界判断。

分析本题:
本题就是上面两个技巧的典型用例,因为所有的格子同时刷新变化,所以,你需要两次遍历,第一次遍历时,判断下一次刷新后的状态应该是啥,你要记下变化,但你又不能影响本次正在进行的遍历,所以你可以用一个中间值来记录。第二次遍历时,刷新中间值。

class Solution {
    public void gameOfLife(int[][] board) {
        // 对应技巧2,用一个长度为8的数组来保存遍历每一个位置时,其周边位置的相对坐标偏移
        int[] x = {0, 0, 1, 1, 1, -1, -1, -1};
        int[] y = {1, -1, 1, -1, 0, 1, -1, 0};
        // 对应技巧1,第一次遍历,把需要变化状态的位置保存为中间值
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                int curX, curY;
                int live = 0;
                // 对应技巧2,通过之前保存的相对坐标偏移的数组,方便的遍历所有的周边位置。
                for (int k = 0; k < 8; k++) {
                    curX = i + x[k];
                    curY = j + y[k];
                    if (curX < 0 || curX >= board.length || curY < 0 || curY >= board[0].length) {
                        continue;
                    }
                    // 对应技巧1,这里的0,1是题目里合理的值,然后如果0要变1,我们用中间值-1记录,如果1要变0
                    // ,我们用中间值2来记录。
                    if (board[curX][curY] == 1 || board[curX][curY] == 2) {
                        live++;
                    }
                }
                if (board[i][j] == 0) {
                    if (live == 3) {
                        board[i][j] = -1;
                    }
                } else {
                    if (live < 2 || live > 3) {
                        board[i][j] = 2;
                    }
                }
            }
        }
        // 对应技巧1,第二次遍历,把中间值-1和2刷新为1和0
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (board[i][j] == 2) {
                    board[i][j] = 0;
                } else if (board[i][j] == -1) {
                    board[i][j] = 1;
                }
            }
        }
    }
}

作者:freshrookie
链接:https://leetcode-cn.com/problems/game-of-life/solution/ju-zhen-wen-ti-tong-yong-jie-fa-by-freshrookie/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

区间问题

参考b站宝藏up主Ranan0

  1. 最小区间
class Solution {
    public int[] smallestRange(List<List<Integer>> nums) {
        TreeSet<Elem> candi = new TreeSet<Elem>((a, b) -> a.val == b.val ?
                                                a.list - b.list : a.val - b.val);
        int range = Integer.MAX_VALUE;
        int[] res = new int[2];
        int len = nums.size();
        for (int i = 0; i < len; i++) {
            Elem elem = new Elem(0, i, nums.get(i).get(0));
            candi.add(elem);
        }
        while(candi.size() == len){
            int min = candi.first().val;
            int max = candi.last().val;
            if(max - min < range){
                range = max - min;
                res[0] = min;
                res[1] = max;
            }
            Elem t = candi.pollFirst();
            if(t.pos < nums.get(t.list).size() - 1){
                candi.add(new Elem(t.pos + 1, t.list, nums.get(t.list).get(t.pos + 1)));
            }
        }
        return res;

    }
    class Elem{
        int pos;
        int list;
        int val;
        public Elem(int pos, int list, int val) {
            this.pos = pos;
            this.list = list;
            this.val = val;
        }
    }
}

合并区间

class Solution {
    public int[][] merge(int[][] arr) {
        Arrays.parallelSort(arr, Comparator.comparingInt(x -> x[0]));
        LinkedList<int[]> list = new LinkedList<>();
        for (int i = 0; i < arr.length; i++) {
            if(i == 0 || arr[i][0] > list.getLast()[1]){
                list.add(arr[i]);
            }else{
                list.getLast()[1] = Math.max(list.getLast()[1], arr[i][1]);
            }
        }

        int[][] res = new int[list.size()][2];//生成结果数组
        int index = 0;
        while (!list.isEmpty()) {//遍历集合
            res[index++] = list.removeFirst();//删除集合首元素
        }
        return res;
    }
}

双指针

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

1. 有序数组的 Two Sum

167. Two Sum II - Input array is sorted (Easy)

Leetcode / 力扣

Input: numbers={2, 7, 11, 15}, target=9
Output: index1=1, index2=2

题目描述:在有序数组中找出两个数,使它们的和为 target。

使用双指针,一个指针指向值较小的元素,一个指针指向值较大的元素。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。

  • 如果两个指针指向元素的和 sum == target,那么得到要求的结果;
  • 如果 sum > target,移动较大的元素,使 sum 变小一些;
  • 如果 sum < target,移动较小的元素,使 sum 变大一些。

数组中的元素最多遍历一次,时间复杂度为 O(N)。只使用了两个额外变量,空间复杂度为 O(1)。


public int[] twoSum(int[] numbers, int target) {
    if (numbers == null) return null;
    int i = 0, j = numbers.length - 1;
    while (i < j) {
        int sum = numbers[i] + numbers[j];
        if (sum == target) {
            return new int[]{i + 1, j + 1};
        } else if (sum < target) {
            i++;
        } else {
            j--;
        }
    }
    return null;
}

2. 两数平方和

633. Sum of Square Numbers (Easy)

Leetcode / 力扣

Input: 5
Output: True
Explanation: 1 * 1 + 2 * 2 = 5

题目描述:判断一个非负整数是否为两个整数的平方和。

可以看成是在元素为 0~target 的有序数组中查找两个数,使得这两个数的平方和为 target,如果能找到,则返回 true,表示 target 是两个整数的平方和。

本题和 167. Two Sum II - Input array is sorted 类似,只有一个明显区别:一个是和为 target,一个是平方和为 target。本题同样可以使用双指针得到两个数,使其平方和为 target。

本题的关键是右指针的初始化,实现剪枝,从而降低时间复杂度。设右指针为 x,左指针固定为 0,为了使 02 + x2 的值尽可能接近 target,我们可以将 x 取为 sqrt(target)。

因为最多只需要遍历一次 0~sqrt(target),所以时间复杂度为 O(log2N)。又因为只使用了两个额外的变量,因此空间复杂度为 O(1)。

 public boolean judgeSquareSum(int target) {
     if (target < 0) return false;
     int i = 0, j = (int) Math.sqrt(target);
     while (i <= j) {
         int powSum = i * i + j * j;
         if (powSum == target) {
             return true;
         } else if (powSum > target) {
             j--;
         } else {
             i++;
         }
     }
     return false;
 }

3. 反转字符串中的元音字符

345. Reverse Vowels of a String (Easy)

Leetcode / 力扣

Given s = "leetcode", return "leotcede".

使用双指针,一个指针从头向尾遍历,一个指针从尾到头遍历,当两个指针都遍历到元音字符时,交换这两个元音字符。

为了快速判断一个字符是不是元音字符,我们将全部元音字符添加到集合 HashSet 中,从而以 O(1) 的时间复杂度进行该操作。

  • 时间复杂度为 O(N):只需要遍历所有元素一次
  • 空间复杂度 O(1):只需要使用两个额外变量

private final static HashSet<Character> vowels = new HashSet<>(
        Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));

public String reverseVowels(String s) {
    if (s == null) return null;
    int i = 0, j = s.length() - 1;
    char[] result = new char[s.length()];
    while (i <= j) {
        char ci = s.charAt(i);
        char cj = s.charAt(j);
        if (!vowels.contains(ci)) {
            result[i++] = ci;
        } else if (!vowels.contains(cj)) {
            result[j--] = cj;
        } else {
            result[i++] = cj;
            result[j--] = ci;
        }
    }
    return new String(result);
}

4. 回文字符串

680. Valid Palindrome II (Easy)

Leetcode / 力扣

Input: "abca"
Output: True
Explanation: You could delete the character 'c'.

题目描述:可以删除一个字符,判断是否能构成回文字符串。

所谓的回文字符串,是指具有左右对称特点的字符串,例如 “abcba” 就是一个回文字符串。

使用双指针可以很容易判断一个字符串是否是回文字符串:令一个指针从左到右遍历,一个指针从右到左遍历,这两个指针同时移动一个位置,每次都判断两个指针指向的字符是否相同,如果都相同,字符串才是具有左右对称性质的回文字符串。


本题的关键是处理删除一个字符。在使用双指针遍历字符串时,如果出现两个指针指向的字符不相等的情况,我们就试着删除一个字符,再判断删除完之后的字符串是否是回文字符串。

在判断是否为回文字符串时,我们不需要判断整个字符串,因为左指针左边和右指针右边的字符之前已经判断过具有对称性质,所以只需要判断中间的子字符串即可。

在试着删除字符时,我们既可以删除左指针指向的字符,也可以删除右指针指向的字符。


public boolean validPalindrome(String s) {
    for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
        if (s.charAt(i) != s.charAt(j)) {
            return isPalindrome(s, i, j - 1) || isPalindrome(s, i + 1, j);
        }
    }
    return true;
}

private boolean isPalindrome(String s, int i, int j) {
    while (i < j) {
        if (s.charAt(i++) != s.charAt(j--)) {
            return false;
        }
    }
    return true;
}

5. 归并两个有序数组

88. Merge Sorted Array (Easy)

Leetcode / 力扣

Input:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

Output: [1,2,2,3,5,6]

题目描述:把归并结果存到第一个数组上。

需要从尾开始遍历,否则在 nums1 上归并得到的值会覆盖还未进行归并比较的值。

public void merge(int[] nums1, int m, int[] nums2, int n) {
    int index1 = m - 1, index2 = n - 1;
    int indexMerge = m + n - 1;
    while (index1 >= 0 || index2 >= 0) {
        if (index1 < 0) {
            nums1[indexMerge--] = nums2[index2--];
        } else if (index2 < 0) {
            nums1[indexMerge--] = nums1[index1--];
        } else if (nums1[index1] > nums2[index2]) {
            nums1[indexMerge--] = nums1[index1--];
        } else {
            nums1[indexMerge--] = nums2[index2--];
        }
    }
}

6. 判断链表是否存在环

141. Linked List Cycle (Easy)

Leetcode / 力扣

使用双指针,一个指针每次移动一个节点,一个指针每次移动两个节点,如果存在环,那么这两个指针一定会相遇。

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

7. 最长子序列

524. Longest Word in Dictionary through Deleting (Medium)

Leetcode / 力扣

Input:
s = "abpcplea", d = ["ale","apple","monkey","plea"]

Output:
"apple"

题目描述:删除 s 中的一些字符,使得它构成字符串列表 d 中的一个字符串,找出能构成的最长字符串。如果有多个相同长度的结果,返回字典序的最小字符串。

通过删除字符串 s 中的一个字符能得到字符串 t,可以认为 t 是 s 的子序列,我们可以使用双指针来判断一个字符串是否为另一个字符串的子序列。

public String findLongestWord(String s, List<String> d) {
    String longestWord = "";
    for (String target : d) {
        int l1 = longestWord.length(), l2 = target.length();
        if (l1 > l2 || (l1 == l2 && longestWord.compareTo(target) < 0)) {
            continue;
        }
        if (isSubstr(s, target)) {
            longestWord = target;
        }
    }
    return longestWord;
}

private boolean isSubstr(String s, String target) {
    int i = 0, j = 0;
    while (i < s.length() && j < target.length()) {
        if (s.charAt(i) == target.charAt(j)) {
            j++;
        }
        i++;
    }
    return j == target.length();
}

8. 长度最小的子数组

209. 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

  • 维护一个窗口,l记录左边的值。
class Solution {
    public int minSubArrayLen(int s, int[] nums) {
        //构造2个dp,存储以当前为终止的和,用一个his记录最小值
        //另一个记录数量
        if(nums.length < 1) return 0;
        int l = 0;
        int res = Integer.MAX_VALUE;
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            while(sum >= s){
                res = Math.min(res, i - l + 1);
                sum -= nums[l];
                l++;
            }
        }
        return res==Integer.MAX_VALUE ? 0 : res;        
    }
}

颜色分类

75. 颜色分类

注意一点就是在每轮判断的时候都要保证i的值要大于l的前面,所以i = Math.max(i, l);

class Solution {
    public void sortColors(int[] nums) {
        //使用两个指针l r
        int l = 0;
        int r = nums.length - 1;
        int i = 0;
        while(i <= r){
            if(nums[i] == 0){
                swap(nums, i, l++);
            }else if(nums[i] == 2) {
                swap(nums, i, r--);
            }else i++;
            i = Math.max(i, l);
        }
    }
    private void swap(int[] nums, int i, int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

判断子序列

393. 判断子序列

class Solution {
    public boolean isSubsequence(String s, String t) {
        int si = 0;
        int sl = s.length();
        for(int i = 0; i < t.length(); i++){
            if(si == sl) return true;
            if(t.charAt(i) == s.charAt(si)){
                si++;
            }
        } 
        return si == sl;
    }
}

滑动窗口

如果是固定大小的滑动窗口,其实就是队列的简化。

找到字符串中所有字母异位词

438. 找到字符串中所有字母异位词

输入:
s: “cbaebabacd” p: “abc”

输出:
[0, 6]

解释:
起始索引等于 0 的子串是 “cba”, 它是 “abc” 的字母异位词。
起始索引等于 6 的子串是 “bac”, 它是 “abc” 的字母异位词。

  • 使用cnt记录当前匹配的值的大小是最精妙的
  • 当长度超过滑动窗口后,没增加一个值都会自动去掉一个值,
class Solution {
    public List findAnagrams(String s, String p) {
        List res = new ArrayList<>();
        int plen = p.length();
        int[] pmap = new int[26];
        int cnt = plen;
        for (int i = 0; i < plen; i++) {
            pmap[p.charAt(i) - 'a']++;
        }
        int[] smap = new int[26];
        for (int i = 0; i < s.length(); i++) {
            int t = s.charAt(i) - 'a';
            smap[t]++;
            if(smap[t] <= pmap[t]){
                cnt--;
            }
            if(i >= plen){
                //大于起始位置,窗口开始滑动,tc是将要被移除的
                int tc = s.charAt(i - plen) - 'a';
                smap[tc]--;
                if(smap[tc] < pmap[tc]){
                    cnt++;
                }
            }
            if(cnt == 0){
                res.add(i - plen + 1);
            }
        }
        return res;
    }
}

你可能感兴趣的:(leetcode)