数组双指针算法题

本文详细介绍了双指针技术,包括对撞指针、快慢指针和分离双指针的步骤,并提供了多个使用双指针解决的LeetCode题目,如最长连续递增序列、滑动窗口最大值等问题的解决方案。
摘要由CSDN通过智能技术生成

目录

什么是双指针?

双指针(Two Pointers):指的是在遍历元素的过程中,不是使用单个指针进行访问,而是使用两个指针进行访问,从而达到相应的目的。在数组的区间问题上,暴力算法的时间复杂度往往是 O(n^2)。而双指针利用了区间「单调性」的性质,可以将时间复杂度降到 O(n)

双指针分类

1、如果两个指针方向相反,则称为「对撞指针」;
2、如果两个指针方向相同,则称为「快慢指针」;
3、如果两个指针分别属于不同的数组 / 链表,则称为「分离双指针」;

对撞指针对撞指针求解步骤

1步:使用两个指针 left,right
left 指向序列第一个元素,即:left = 0;
right 指向序列最后一个元素,即:right = len(nums) - 1。

第2步:在循环体中将左右指针 相向移动
当满足一定条件时,将左指针右移,left += 1;
当满足另外一定条件时,将右指针左移,right -= 1。

第3步:跳出循环体
直到两指针相撞(即 left == right);
或者满足其他要求的特殊条件时。

快慢指针快慢指针求解步骤

1步:使用两个指针 slow、fast
slow 一般指向序列第一个元素,即:slow = 0;
fast 一般指向序列第二个元素,即:fast = 1。

第2步:在循环体中将两指针 向右移动 。
当满足一定条件时,将慢指针右移,即 slow += 1;
当满足另外一定条件时(也可能不需要满足条件),将快指针右移,即 fast += 1。

第3步:跳出循环体
快指针移动到数组尾端(即 fast == len(nums) - 1;
或者两指针相交,
或者满足其他特殊条件时。

分离双指针分离双指针求解步骤

1步: 使用两个指针 left_1、left_2
left_1 指向第一个数组 / 链表的第一个元素,即:left_1 = 0,
left_2 指向第二个数组 / 链表的第一个元素,即:left_2 = 0。

第2步:在循环体中将两指针 向右移动。
当满足一定条件时,两个指针同时右移,即 left_1 += 1、left_2 += 1。
当满足另外一定条件时,将 left_1 指针右移,即 left_1 += 1。
当满足其他一定条件时,将 left_2 指针右移,即 left_2 += 1。

第3步:跳出循环体
当其中一个数组 / 链表遍历完时;
或者满足其他特殊条件时跳出循环体。

1. i 和 i-1

1.1. 判断是否连续的迟到/早退 【找存在】

/**
 * 判断是否连续的迟到/早退
 */
private static boolean isBad(List<String> attendances) {
    if (attendances.size() <= 1) {
        return false;
    }
    
    List<String> absent = Arrays.asList("late", "leaveearly");
    for (int i = 1; i < attendances.size(); i++) {
        String current = attendances.get(i);
        String previous = attendances.get(i - 1);
        if (absent.contains(current) && absent.contains(previous)) {
            // 存在有问题数据就直接终止判断并返回
            return true;
        }
    }
    
    return false;
}

1.2. [简单] leetcode674. 未排序的整数数组,找最长连续递增的子序列的长度 [1,3,5,4,7]→[1,3,5]→3

/**
 * leetcode 674. 无序整数数组的最长且连续递增的子序列的长度
 */
public static int findLengthOfLCIS(int[] nums) {
    if (nums.length == 0) {
        return 0;
    }
    if (nums.length == 1) {
        return 1;
    }
    
    int max = 1;
    int start = 0;
    for (int i = 1; i < nums.length; i++) {
        if (nums[i - 1] < nums[i]) {
            // 5-3=2表示5比3大2个,3到5一共有三个,故再加1
            # 结束态 写法 故不需要收尾
            max = Math.max(i - start + 1, max);
        } else {
            start = i;
        }
    }
    
    return max;
}

/**
 * leetcode 674. 无序整数数组的最长且连续递增的子序列的长度
 */
public static int findLengthOfLCIS2(int[] nums) {
    if (nums.length == 0) {
        return 0;
    }
    if (nums.length == 1) {
        return 1;
    }
    
    int max = 1;
    
    int tempMax = 1;
    for (int i = 1; i < nums.length; i++) {
        if (nums[i - 1] < nums[i]) {
        	# 中间态 写法 故需要最后收尾
            tempMax++;
        } else {
            max = Math.max(tempMax, max);
            tempMax = 1;
        }
    }
    max = Math.max(tempMax, max);
    
    return max;
}

/**
 * leetcode 674. 无序整数数组的最长且连续递增的子序列
 */
public static int[] findLengthOfLCIS1(int[] nums) {
    if (nums.length < 2) { // 2个一下不涉及排序
        return nums;
    }

    int[] temp = {nums[0]};  // 下边从1开始和前面的值比较,起码有前面这个值,所以收集第一个元素应对[2,2,2,2,2]这种场景
    
    int max = 1;
    int start = 0;
    for (int i = 1; i < nums.length; i++) {
        if (nums[i - 1] < nums[i]) {
            if (i - start + 1 > max) {
                max = i - start + 1;
                // 方法1:直接截取数组
                temp = Arrays.copyOfRange(nums, start, i + 1);
                // 方法2:记录开始结束坐标最后再截取
            }
        } else {
            start = i;
        }
    }
    return temp;
}

1.3. [中等] leetcode128 未排序的整数数组,找出数字连续的最长序列的长度(不要求序列元素在原数组中连续) [100,4,200,1,3,2]→[1, 2, 3, 4]→4

/**
 * leetcode128 无序整数数组,找出数字连续的最长序列的长度(不要求序列元素在原数组中连续)
 */
public int longestConsecutive(int[] nums) {
    if (nums.length == 0) {
        return 0;
    }
    if (nums.length == 1) {
        return 1;
    }
    
    Arrays.sort(nums);
    
    int max = 1;
    
    int tempMax = 1; // 下边从1开始和前面的值比较,起码有前面这个值,所以tempMax从1开始
    for (int i = 1; i < nums.length; i++) {
        if ((nums[i - 1]) == nums[i]) {
            // 数字连续即要求严格递增,当前处理是针对如下场景
            // [100,4,200,1,3,3,3,3,2] → [1,2,3,4] → 4
            continue;
        }
        if ((nums[i - 1] + 1) == nums[i]) {
            // 符合连续条件
            tempMax++;
        } else {
            max = Math.max(tempMax, max); // 动态收集最大值
            tempMax = 1; // 当前值字符为新一轮的开始,故tempMax其实为1
        }
    }
    // 此处针对最后一位走的是(符合连续条件→然后循环完毕)这种场景
    max = Math.max(tempMax, max);
    
    return max;
}

1.4. [中等] leetcode1839. 所有元音按顺序排布的最长子字符串

这些元音字母的顺序都必须按照 字典序 升序排布
(也就是说所有的 'a' 都在 'e' 前面,所有的 'e' 都在 'i' 前面,以此类推)

/**
 * 1839. 所有元音按顺序排布的最长子字符串
 * 参考的题解→适合自己的题解
 */
public static int longestBeautifulSubstring(String word) {
    Map<Character, Integer> valueMap = new HashMap();
    valueMap.put('a', 0);
    valueMap.put('e', 1);
    valueMap.put('i', 2);
    valueMap.put('o', 3);
    valueMap.put('u', 4);
    
    char[] chars = word.toCharArray();
    
    int res = 0;
    
    int start = 0;
    for (int i = 1; i < word.length(); i++) {
        // 'a'开始'u'结束且已保证递增,故可以保证('a' ,'e' ,'i' ,'o' ,'u')都必须 至少 出现一次
        // 按照 字典序 升序排布
        if (chars[start] == 'a' && (valueMap.get(chars[i - 1]) + 1 == valueMap.get(chars[i]) || valueMap.get(chars[i - 1]) == valueMap.get(chars[i]))) {
            if (chars[i] == 'u') {
                res = Math.max(i - start + 1, res);
            }
        } else {
            start = i;
        }
    }
    
    return res;
}

1.5. NC37 合并区间

public static ArrayList<Interval> merge(ArrayList<Interval> intervals) {
    if (intervals.size() <= 1) {
        return intervals;
    }
    
    // 两个指标都排序排序
    List<Interval> orderedIntervals = intervals.stream().sorted((a, b) -> {
        if (a.start == b.start) {
            return a.end - b.end;
        } else {
            return a.start - b.start;
        }
    }).collect(Collectors.toList());
    
    // 基底
    ArrayList<Interval> res = new ArrayList<>();
    
    Interval base = orderedIntervals.get(0);
    for (int i = 1; i < orderedIntervals.size(); i++) {
        if (isNotUnit(base, orderedIntervals.get(i))) {
            res.add(base); // 不相交,收集基底
            base = orderedIntervals.get(i); // 变基
        } else {
            base = getNew(base, orderedIntervals.get(i)); // 当前区间和Base区间取并集
        }
        if (i == orderedIntervals.size() - 1) { // 当前项为最后一项的话、直接收集起来
            res.add(base);
        }
    }
    return res;
}

/**
 * 判断两个区间不相交
 */
public static boolean isNotUnit(Interval left, Interval right) {
    return left.end < right.start;
}

/**
 * 获取最大区间
 */
public static Interval getNew(Interval left, Interval right) {
    int min = Math.min(left.start, right.start);
    int max = Math.max(left.end, right.end);
    return new Interval(min, max);
}

class Interval {
    int start;
    int end;

    Interval() {
        start = 0;
        end = 0;
    }

    Interval(int s, int e) {
        start = s;
        end = e;
    }

    @Override
    public String toString() {
        return "[" + start + "," + end + ']';
    }
}

2. i 和 i+1

2.1. NC17 最长回文子串

Java 业务原子级常用方法 ☞ 判断子串是不是回文子串

/**
 * NC17 最长回文子串
 */
public static int getLongestPalindrome(String A) {
    int max = 0;
    
    int len = A.length();
    //暴力解法
    for (int i = 0; i < len; i++) {
        for (int j = i + 1; j <= len; j++) {
            String subStr = A.substring(i, j);//确定字符
            if (isPalindrome(subStr) && subStr.length() > max) {
                max = subStr.length();//最大长度
            }
        }
    }
    
    return max;
}

/**
 * 判断子串是不是回文子串
 */
public static boolean isPalindrome(String s) {
    // abcba  奇数时 5/2=2 范围for (int i = 0; i < 2; i++) [0 1] 2 3 4
    // abccba 偶数时 6/2=3 范围for (int i = 0; i < 3; i++) [0 1 2] 3 4 5
    int strLength = s.length();
    for (int i = 0; i < strLength / 2; i++) {
        // 数组折叠对应下标相加为strLength - 1
        if (s.charAt(i) != s.charAt(strLength - 1 - i))//不相等
            return false;
    }
    return true;
}

2.2. NC61 两数之和

private static int[] getIntegers(int[] numbers, int target) {
    int[] res = new int[2];
    for (int i = 0; i < numbers.length; i++) {
        if (numbers[i] > target) {
            continue;
        }
        // 不存在回头的场景,有回头也是前面收集过的,所以j=i+1
        for (int j = i + 1; j < numbers.length; j++) {
            if (numbers[i] + numbers[j] == target) {
                res[0] = i + 1;
                res[1] = j + 1;
                return res;
            }
        }
    }
    return res;
}

2.3. HJ65 查找两个字符串a,b中的最长公共子串

/**
 * HJ65 查找两个字符串a,b中的最长公共子串
 */
public static String getLongestCommonSubstring(String s1, String s2) {
    String shortStr = s1.length() < s2.length() ? s1 : s2;
    String longStr = s1.length() > s2.length() ? s1 : s2;
    
    int maxLen = 0;
    int start = 0;
    int end = 0;
    
    for (int l = 0; l < shortStr.length(); l++) {
    
        if (shortStr.length() - l <= maxLen) { // 剪枝,余下的子串长度已经不超过maxLen没必要计算,退出循环(性能优化)
            break;
        }
        
        for (int r = shortStr.length(); r > l; r--) { // 左指针i,右指针right, 右指针逐渐向左逼近
            String subStr = shortStr.substring(l, r);
            if (longStr.contains(subStr) && subStr.length() > maxLen) {
                maxLen = subStr.length();
                start = l; // start 记录一下此时的下标为了后续截取字符串
                end = r;
                break;
            }
        }
    }
    
    // 方法1
    // return shortStr.substring(start, start + maxLen);
    // 方法2
    return shortStr.substring(start, end);
}

3. for (int i = 0; i <= arr.length - 7; i++)

3.1. 判断是否任意连续7次考勤 缺勤/迟到/早退 超过3次

/**
 * 判断是否任意连续7次考勤 缺勤/迟到/早退 超过3次
 */
private static boolean isBad2(List<String> list) {
    if (list.isEmpty()) {
        return false;
    }
    
    int[] arr = new int[list.size()];
    for (int i = 0; i < list.size(); i++) {
        arr[i] = "present".equals(list.get(i)) ? 0 : 1;
    }
    
    if (arr.length <= 7) {
        return Arrays.stream(arr).sum() > 3;
    } else {
        for (int i = 0; i <= arr.length - 7; i++) {
            int[] ints = Arrays.copyOfRange(arr, i, i + 7); // 任意7天
            int sum = Arrays.stream(ints).sum();
            if (sum > 3) {
                return true; // 存在有问题数据就直接终止判断并返回
            }
        }
    }
    return false;
}

3.2. leetcode239. 滑动窗口最大值

/**
 * leetcode239. 滑动窗口最大值
 */
private static int[] getIntegers(int[] nums, int k) {
    // 收集结果容器
    List<Integer> res = new ArrayList<>();
    
    // 业务逻辑处理
    for (int i = 0; i <= nums.length - k; i++) {
        int[] ints = Arrays.copyOfRange(nums, i, i + k);
        Arrays.stream(ints).max().ifPresent(res::add);
    }
    
    return res.stream().mapToInt(Integer::intValue).toArray();
}

3.3. 字符串是否含有长度大于2的相同子串(注:其他符号不含空格或换行)

HJ20 密码验证合格程序

if (hasSameSubStr(str, 0, 3)) {
    return "NG";
}
 
# 递归遍历
/**
 * 是否有长度大于2的相同子串 (注:其他符号不含空格或换行)
 */
private static boolean hasSameSubStr(String str, int l, int r) {
    // 最优化应该是如果截取3位的话应该是r>=str.length()-2,n位应该是r>=str.length()-(n-1)
    if (r >= str.length()) {
        return false;
    }
    
    // 母串
    String sourceStr = str.substring(r);
    // 子串
    String targetStr = str.substring(l, r);
    // 母串是否包含子串
    if (sourceStr.contains(targetStr)) {
        return true;
    } else {
        return hasSameSubStr(str, l + 1, r + 1);
    }
}

---------------------------------------------------------------------------------
# 正常遍历
private static boolean hasSameSubStr2(String str) {
    for (int i = 0; i < str.length() - 3; i++) {
        String targetStr = str.substring(i, i + 3);
        String sourceStr = str.substring(i + 3);
        if (sourceStr.contains(targetStr)) {
            return true;
        }
    }
    return false;
}

4. for (int i = 0; i < str.length(); )

4.1. 字符串8位一截取,不够8位后面补0

/**
 * 字符串8位一截取,不够8位后面补0
 */
private static List<String> splitSubStrings(String str) {
    if (str.length() == 0) {
        return new ArrayList<>();
    }
    
    List<String> res = new ArrayList<>();
    for (int i = 0; i < str.length(); ) {
        if (i + 8 < str.length()) {
            String substring = str.substring(i, i + 8);
            res.add(substring);
            i = i + 8;
        } else {
            String substring = str.substring(i);
            res.add(fillStr(substring));
            i = i + 8;
        }
    }
    return res;
}

public static String fillStr(String str) {
    if (str.length() >= 8) {
        return str;
    }
    return fillStr(str + "0");
}

4.2. 截取字符串使得负数正数相加和最小

private static int getSum(String str) {
    // 结果收集容器
    List<Integer> res = new ArrayList<>();
    
    for (int i = 0; i < str.length(); ) {
        if (Character.isDigit(str.charAt(i))) {
            res.add(Character.getNumericValue(str.charAt(i))); // 数字直接收集,坐标跳到下一位
            i = i + 1;
        } else if ('-' == str.charAt(i)) {
            // 遇到负号开始拼接最大负数
            int start = i; // 记录开始位置
            i = i + 1; // 跳过当前负号的位置,到下一坐标
            while (i < str.length() && Character.isDigit(str.charAt(i))) { // 如果下一位在范围内,且是数字,继续蚕食
                i = i + 1;
            }
            if (i - start > 1) { // 如果只有一个负号,这种数据舍弃,负号和数字组合长度必然大于1才收集
                // i为下一个位置的坐标,也就可以视为当前子串的右边界
                res.add(Integer.parseInt(str.substring(start, i)));
            }
        } else {
            i = i + 1; // 非数字,非负号,皆跳过
        }
    }
    
    System.out.println(res);
    // 输出结果
    int sum = res.stream().mapToInt(Integer::intValue).sum();
    return sum;
}

4.3. [算法题] 连续字母长度

/**
 * [算法题]连续字母长度
 */
private static Map<Character, Integer> getCharAndSizeMap(String str) {
    Map<Character, Integer> map = new HashMap<>();
    
    Deque<Character> dynamicChars;
    for (int i = 0; i < str.length(); ) {
        dynamicChars = new LinkedList<>();
        while (i < str.length() && (dynamicChars.isEmpty() || dynamicChars.contains(str.charAt(i)))) { // ((有效范围内&&(第一字符时||还是这个字符时))收集此字符
            dynamicChars.addLast(str.charAt(i));
            i = i + 1; // 当前字符处理完毕,跳到下个字符坐标
        }
        
        Integer tempMax = map.getOrDefault(dynamicChars.getFirst(), 0); // 取出当前字符已知的最大长度
        map.put(dynamicChars.getFirst(), Math.max(tempMax, dynamicChars.size())); // 动态算取最大长度
    }
    
    return map;
}

6. 快慢指针

自我潜规则:i表示快指针,j表示慢指针

6.1. leetcode392 判断字符串 s 是否为 t 的子序列

/**
 * leetcode392 判断字符串 s 是否为 t 的子序列
 */
private static boolean isSubsequence(String s, String t) {
    if (s.length() == 0) {
        return true;
    }
    if (t.length() == 0) {
        return false;
    }
    
    // 快慢指针
    int j = 0;
    for (int i = 0; i < t.length(); i++) {
    	# 一个有序公布,一个去对比,体会彩票公布对号检查场景 
        if (s.charAt(j) == t.charAt(i)) {
            j++; // 相等的话指向慢指针的下个位置
            if (j >= s.length()) {
                return true;
            }
        }
    }
    return false;
}

6.2. NC28 最小覆盖子串

给你一个字符串 s 、一个字符串 t 。返回 s 中涵盖 t 所有字符的最小子串。

public static String minWindow(String s, String t) {
    // 没有用map统计每个字符的数量,而是用了int[](天然的Map)
    # 天然Map
    int[] tChars = new int[128];
    for (int i = 0; i < t.length(); i++) {
        tChars[t.charAt(i)] ++;
    }
    
    int l = -1;
    int r = -1;
    int minLen = Integer.MAX_VALUE;
    
    # 毛毛虫式遍历
    int j = 0;
    for (int i = 0; i < s.length(); i++) {
        tChars[s.charAt(i)]--; // >0表示t串此字符多,<0表示s串此字符多, =0表示字符数相等
        while (allItemLE0(tChars)) { // 都小于0说明s串覆盖t串了
            if (i - j + 1 < minLen) { // 动态求最小覆盖子串
                minLen = i - j + 1;
                l = j;
                r = i;
            }
            // j因为要右移动,所以把对应位置的字符收集起来,重新去排除
            tChars[s.charAt(j)]++;
            j++;
        }
    }
    
    if (l == -1) { //找不到的情况
        return "";
    }
    
    return s.substring(l, r + 1);
}
# 30ms,43.5M

-------------------------
# forEach 用时:51ms 43.9M
int[] cnt = new int[128];
for (char ch : t.toCharArray()) {
    cnt[ch]++;
}
-----------------------
public static boolean allItemLE0(int[] arr) {
    for (int i = 0; i < arr.length; i++) {
        if (arr[i] > 0) {
            return false;
        }
    }
    return true;
}

6.3. HJ70 矩阵乘法计算量估算

矩阵行列
50 10
10 20
20 5

计算顺序
(A(BC))

/**
 * HJ70 矩阵乘法计算量估算
 */
private static int matrixMultiplication(int[][] matrix, String actionStr) {
    int result = 0;
    
    int j = 0;
    # 唐歌舞式的遍历数组
    Deque<int[]> stack1 = new LinkedList<>(); // 存放矩阵行数和列数
    for (int i = 0; i < actionStr.length(); i++) {
        if (actionStr.charAt(i) == '(') {
            // 啥也不做
        } else if ('A' <= actionStr.charAt(i) && actionStr.charAt(i) <= 'Z') {
            stack1.push(matrix[j]);
            j++;
        } else if (actionStr.charAt(i) == ')') {
            int[] pop2 = stack1.pop();
            int[] pop1 = stack1.pop();
            
            result += pop1[0] * pop2[1] * pop1[1];
            
            int[] newMatrix = {pop1[0], pop2[1]};
            stack1.push(newMatrix);
        }
    }
    
    return result;
}

7. 逻辑

7.1. HJ41 称砝码

现有n种砝码,重量互不相等,分别为 m1,m2,m3…mn ;每种砝码对应的数量为 x1,x2,x3...xn 。
现在要用这些砝码去称物体的重量(放在同一侧),问能称出多少种不同的重量。注:称重重量包括 0

// n种砝码
int n = parseInt(bf.readLine());
// 每种砝码重量
Integer[] w = Arrays.stream(bf.readLine().split(" ")).limit(n).map(Integer::parseInt).toArray(Integer[]::new);
// 每种几个
Integer[] c = Arrays.stream(bf.readLine().split(" ")).limit(n).map(Integer::parseInt).toArray(Integer[]::new);

/**
 * @param n n种砝码
 * @param w 每种砝码重量
 * @param c 每种几个
 */
private static HashSet<Integer> getWeights(int n, Integer[] w, Integer[] c) {
    HashSet<Integer> res = new HashSet<>();
    res.add(0); // 由示例可知,0重量也属于一种情况
    
    for (int i = 0; i < n; i++) { // 遍历种数
    
        HashSet<Integer> resPer = new HashSet<>(); // 遍历res时再往里追加会有问题,所以引入一个中间变量
        for (int k = 1; k <= c[i]; k++) { // 遍历个数
            int stepWeight = w[i] * k;    // 遍历重量
            resPer.add(stepWeight);
            for (Integer weight : res) {
                resPer.add(weight + stepWeight);
            }
        }
        
        res.addAll(resPer);
    }
    return res;
}

7.2. HJ48 从单向链表中删除指定值的节点

6 2 1 2 3 2 5 1 4 5 7 2 2
第一个参数6表示输入总共6个节点
第二个参数2表示头节点值为2
剩下的2个一组表示第1个节点值插入到第2个节点值后面
最后一个参数为2,表示要删掉节点为2的值

// 6 2 1 2 3 2 5 1 4 5 7 2 2
// 6 {2 [1 2 3 2 5 1 4 5 7 2]} 2
String[] split = str.split(" ");

# 构建链表
ListNode head = buildListNode(arr);

// 最后一个参数为2,表示要删掉节点为2的值
int del_number = Integer.parseInt(arr[arr.length - 1]);

StringBuilder sb = new StringBuilder();
ListNode temp = head;
while (temp != null) {
    if (temp.val != del_number) { // 以跳过的方式模拟删除节点
        sb.append(temp.val).append(" ");
    }
    temp = temp.next;
}

System.out.println(sb.toString()); // 注意要求每个数后面都加空格

----------------------------------------------------------------------------------
/**
 * HJ48 从单向链表中删除指定值的节点
 * 官方题解的基础上结合了好理解的方式的实现
 */
public static ListNode buildListNode(String[] arr) {
    int cnt = Integer.parseInt(arr[0]); // 总共有多少个节点
    ListNode head = new ListNode(Integer.parseInt(arr[1])); // 头结点
    // 6 {2 [1 2 3 2 5 1 4 5 7 2]} 2
    String[] strings = Arrays.copyOfRange(arr, 2, arr.length - 1); // 成对的节点
    
    for (int i = 0; i < cnt - 1; i++) { // 头结点为2已确定,6组数据还剩5组,i表示组数
        // 2个一组,表示第1个节点值插入到第2个节点值后面
        int willInsert = Integer.parseInt(strings[2 * i]);
        int afterWho = Integer.parseInt(strings[2 * i + 1]);
        
        ListNode tmp = head;
        while (tmp.val != afterWho) { // 找到插入的位置
            tmp = tmp.next;
        }
        // 链表前后链接
        ListNode newNode = new ListNode(willInsert);
        newNode.next = tmp.next;
        tmp.next = newNode;
    }
    return head;
}

/**
 * 单向链表
 */
class ListNode {
    ListNode next;
    int val;

    ListNode(int val) {
        this.val = val;
        next = null;
    }
}

8. [0,i)回放

8.1. leetcode 300. 整数数组的最长严格递增子序列的长度

/**
 * leetcode  300. 整数数组的最长严格递增子序列的长度
 */
private static int lengthOfLIS(int[] nums) {
    if (nums.length == 0) {
        return 0;
    }
    
    // 参数初始化
    int[] dp = new int[nums.length];
    // 方法1
    for (int i = 0; i < dp.length; i++) {
        dp[i] = 1; // 每个序列起码有自己所以长度最小为1
    }
    // 方法2
    // Arrays.fill(dp, 1);
    
    int max = 1; // 每个序列起码有自己所以长度最小为1
    
    for (int i = 1; i < nums.length; i++) {
        for (int j = 0; j < i; j++) { // 通过回放计算出dp[i]的最优值
            if (nums[j] < nums[i]) {
                dp[i] = Math.max(dp[j] + 1, dp[i]); // 状态转移方程
            }
        }
        max = Math.max(dp[i], max); // dp[i]的最优值和max相比计算出max的最优值
    }
    
    return max;
}

参考

数组双指针知识

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值