LeetCode复习 双指针系列

本文详细介绍了多种算法的实现,包括移除重复项、删除指定元素、KMP字符串搜索、数组操作如移动零和颜色分类,以及解决实际问题如接雨水和盛水容器问题。此外,还探讨了如何找到字符串中的下一个更大排序和如何反转字符串中的单词。这些算法在优化数据处理和解决复杂问题方面具有重要意义。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


移除重复项

public int removeDuplicates(int[] nums) { 
        int left = 0, right = 0;
        while (right < nums.length) {
            if (nums[left] == nums[right]) {
                right++;
             //找到不重复的元素
            } else {
                nums[left+1] = nums[right];
                left++;
            }
        }
        return  left + 1;
    }



删除指定元素

 public int removeElement(int[] nums, int val) {
        int right = nums.length - 1, left = 0;
        while (left <= right) {
            if (nums[left] != val) {
                left++;
            //找到相等的就和不相等的交换
            } else {
                nums[left] = nums[right];
                right--;
            }
        }
        return right + 1;
    }


KMP

//这种写法next数组表示的当前子串的最长公共前后缀长度,字符串下标从0开始
public int strStr(String haystack, String needle) {
        char[] chars = haystack.toCharArray();
        char[] chars1 = needle.toCharArray();
        int[] next = new int[chars1.length];
        //计算next数组
        //前缀终止位置-1
        int prefix = 0;
        next[0] = prefix;
        //后缀终止位置-1
        for (int suffix = 1; suffix < next.length ; suffix++) {
            //前后缀末尾不相同的情况,就要向前回溯,直到前后缀结尾一样或prefix为0
            //回溯的位置是前缀结尾的前一位的next数组值
            //例如字符串aaabbab prefix = 2  suffix = 3时不相等时就看prefix的前一位的next数组值,
            //prefix = 1时aa的最长前后缀长度是2,不过前缀aa结尾的a和后缀ab结尾的b不相等还要继续回退
            //prefix = 0可以结束回退,next[3]为0
            //因为prefix的前一位的next数组值记录着prefix的前一位的相同前后缀的长度
            while (prefix > 0 && chars1[prefix] != chars1[suffix]) {
                prefix = next[prefix-1];
            }
            //前后缀末尾相同就继续往后找直到前后缀结尾不一样
            if (chars1[prefix] == chars1[suffix]) {
                prefix++;
            }
            next[suffix] = prefix;
        }
        int b = 0;
        for (int a = 0; a < haystack.length(); a++) {
            //不匹配就回退
            while (b > 0 && chars[a] != chars1[b]) {
                //因为next存的是数组长度,所以这里刚好是回退到已匹配的后一位
                b = next[b-1];
            }
            if (chars[a] == chars1[b]) {
                b++;
            }
            if (b == chars1.length) {
                return a - needle.length() + 1;
            }
        }
        return -1;
    }

//这种写法next数组的意思当前字符的前面字符的最长公共前后缀长度+1,字符串下标从1开始,下标0为空字符
 public int strStr(String haystack, String needle) {
        if (needle.length() == 0) {
            return 0;
        }
        haystack = " " + haystack;
        needle = " " + needle;
        char[] pattern = needle.toCharArray();
        char[] source = haystack.toCharArray();
        int[] next = new int[pattern.length];
        Arrays.fill(next,1);
        int pre = 1;
        int suffix = 2;
        while (suffix < next.length - 1) {
            while (pre > 1 && pattern[pre] != pattern[suffix]) {
                pre = next[pre];
            }
            if (pattern[pre] == pattern[suffix]) {
                next[++suffix] = ++pre;
                continue;
            }
            suffix++;
        }
        int a = 1, b = 1;
        while (a < source.length) {
            if (source[a] == pattern[b]) {
                a++;
                b++;
                if (b == pattern.length) {
                    return a - pattern.length;
                }
            }
            while (a < source.length && source[a] != pattern[b]) {
                if (b > 1) {
                    b = next[b];
                } else {
                    a++;
                }
            }
        }
        return -1;
    }


移动零

 public void moveZeroes(int[] nums) {
        if (nums.length < 2) {
            return;
        }
        int zero = 0, notZero = 0;
        while (notZero < nums.length) {
           if (nums[notZero] != 0) {
               int temp = nums[zero];
               nums[zero] = nums[notZero];
               nums[notZero] = temp;
               notZero++;
               zero++;
           } else {
               notZero++;
           }
        }
    }


反转字符串中的单词 III

public String reverseWords(String s) {
        String trim = s.trim();
        String[] s1 = trim.split(" ");
        StringBuilder sb = new StringBuilder();
        for (String s2 : s1) {
            char[] chars = s2.toCharArray();
            int left = 0, right = s2.length() - 1;
            while (left < right) {
                char temp = chars[left];
                chars[left] = chars[right];
                chars[right] = temp;
                left++;
                right--;
            }
            sb.append(chars);
            sb.append(" ");
        }
        sb.deleteCharAt(sb.length()-1);
        return sb.toString();
    }


验证回文串||

public boolean validPalindrome(String s) {
        char[] chars = s.toCharArray();
        int left = 0, right = chars.length - 1;
        while (left < right) {
            if (chars[left] != chars[right]) {
                return isPalindrome(chars, left + 1, right) || isPalindrome(chars, left, right - 1);
            }
            left++;
            right--;
        }
        return true;
    }

    public boolean isPalindrome(char[] s, int left, int right) {
        while (left < right) {
            if (s[left] != s[right]) {
                return false;
            }
            left++;
            right--;
        }
        return true;
    }


盛最多水的容器

  /**
     * 双指针的遍历方法,相当于找的是以每个点为端点的图形的最大面积,那么就一定包含了整个问题的最大面积
     * 假设状态 S(i, j)S(i,j) 下 h[i] < h[j]h[i]<h[j] ,在向内移动短板至 S(i + 1, j)S(i+1,j) ,
     * 则相当于消去了 {S(i, j - 1), S(i, j - 2), ... , S(i, i + 1)}S(i,j−1),S(i,j−2),...,S(i,i+1) 状态集合。
     * 而所有消去状态的面积一定都小于当前面积(即 < S(i, j)<S(i,j)),因为这些状态:
     * 短板高度:相比 S(i, j)S(i,j) 相同或更短(即h[i]≤h[i] );不可能比短板的长,因为最短的板在左边
     * 底边宽度:相比 S(i, j)S(i,j) 更短;
     */
    public int maxArea(int[] height) {
        int left = 0, right = height.length - 1;
        int res = 0;
        while (left < right) {
            int h = Math.min(height[left], height[right]);
            res = Math.max((right - left) * h,res);
            if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }
        return res;
    }


下一个更大排序
在这里插入图片描述

/**
     * 我们希望下一个数比当前数大,这样才满足“下一个排列”的定义。因此只需要将后面的「大数」与前面的「小数」交换,
     * 就能得到一个更大的数。比如 123456,将 5 和 6 交换就能得到一个更大的数 123465。
     * 我们还希望下一个数增加的幅度尽可能的小,这样才满足“下一个排列与当前排列紧邻“的要求。为了满足这个要求,我们需要:
     * 在尽可能靠右的低位进行交换,需要从后向前查找
     * 将一个 尽可能小的「大数」 与前面的「小数」交换。比如 123465,下一个排列应该把 5 和 4 交换而不是把 6 和 4 交换
     * 将「大数」换到前面后,需要将「大数」后面的所有数重置为升序,升序排列就是最小的排列。
     * 以 123465 为例:首先按照上一步,交换 5 和 4,得到 123564;然后需要将 5 之后的数重置为升序,
     * 得到 123546。显然 123546 比 123564 更小,123546 就是 123465 的下一个排列
     */
    public void nextPermutation(int[] nums) {
        //例如12385764
        for (int i = nums.length - 1; i > 0; i--) {
            //找到【5,7】是升序的
            if (nums[i-1] < nums[i]) {
                int temp = nums[i-1];
                for (int j = nums.length - 1; j >= i; j--) {
                    //找到6比5大,交换5和6变成12386754
                    if (nums[j] > temp) {
                        nums[i-1] = nums[j];
                        nums[j] = temp;
                        //对754进行升序排序变成12386457
                        Arrays.sort(nums,i,nums.length);
                        return;
                    }
                }
            }
        }
        Arrays.sort(nums);
    }


颜色分类

public void sortColors(int[] nums) {
        int lt = 0, gt = nums.length - 1, equal = 0;
        int base = 1;
        while (equal <= gt) {
        //这步执行equal位置肯定是1,lt位置肯定是0,equal位置不可能是2因为是2的话会被移到最右边
            if (nums[equal] < base) {
                swap(nums,lt,equal);
                lt++;
                equal++;
            } else if (nums[equal] == base) {
                equal++;
            //不确定交换后equal位置的是不是等于1所以equal不加1,equal位置有可能是0,1,2
            } else  {
                swap(nums,gt,equal);
                gt--;
            }
        }
    }


反转字符串里的单词

public String reverseWords(String s) {
        Deque<String> stack = new LinkedList<>();
        String trim = s.trim();
        char[] chars = s.toCharArray();
        int i = 0;
        while (i < chars.length) {
            //找到单词首字符
            if (chars[i] != ' ') {
                int start = i;
                //直到单词尾字符的后一位
                while (i < chars.length && chars[i] != ' ') {
                    i++;
                }
                int end = i;
                stack.push(s.substring(start,end));
            }
            i++;
        }
        StringBuffer sb = new StringBuffer();
        while (!stack.isEmpty()) {
            sb.append(stack.pop());
            sb.append(" ");
        }
        sb.deleteCharAt(sb.length() - 1);
        return sb.toString();
    }


轮转数组

ublic void rotate(int[] nums, int k) {
        int length = nums.length;
        k = k % length;
       reverse(nums,0,length - 1);
       reverse(nums,0,k - 1);
       reverse(nums,k,length - 1);
    }
    
    public void reverse(int[] nums, int left, int right) {
        while (left < right) {
            swap(nums,left,right);
            left++;
            right--;
        }
    }

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


接雨水

 public int trap(int[] height) {
        int sum = 0;
        int[] maxLeft = new int[height.length];
        int[] maxRight = new int[height.length];
        for (int i = 1; i < height.length; i++) {
            maxLeft[i] = Math.max(maxLeft[i-1],height[i-1]);
        }
        for (int i = height.length-2; i >= 0; i--) {
            maxRight[i] = Math.max(maxRight[i+1],height[i+1]);
        }
        for (int i = 1; i < height.length-1; i++) {
            int min = Math.min(maxLeft[i],maxRight[i]);
            if (min > height[i]) {
                sum += min - height[i];
            }
        }
        return sum;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值