4.2 双指针刷题记录

Leetcode #167 两数之和
https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted/
给定一个有序数组,判断是否存在数组内的两个数相加等于目标值,双指针遍历数组,一个指针从最小值开始,另一指针从最大值开始。
左指针往右移动则使得和增加,右指针往左移则使得和减小

    public static int[] twoSum(int[] numbers, int target) {
        int l = 0;
        int r = numbers.length - 1;
        while(l < r){
            if(numbers[l] + numbers[r] == target){
                return new int[]{l+1,r+1};
            }else if(numbers[l] + numbers[r] > target){
                r--;
            }else if (numbers[l] + numbers[r] < target){
                l++;
            }
        }
        return null;
    }

Leetcode #15 三数之和
https://leetcode-cn.com/problems/3sum/
先把数组进行排序,从最左边元素开始定位,如果该元素大于0,则说明所有元素都大于0,不存在相加等于0的情况
然后从左边第二个元素和最末尾的元素开始相加,如果该3个元素相加等于0,则进入下一轮,如果大于0则说明需要一个更小的元素,右边的指针往左移,如果小于0则说明需要一个更大的元素,左边的指针往右移

public static List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> ans = new ArrayList();
        int len = nums.length;
        if(nums == null || len < 3) return ans;
        Arrays.sort(nums); // 排序
        for (int i = 0; i < len ; i++) {
            if(nums[i] > 0) break; // 如果当前数字大于0,则三数之和一定大于0,所以结束循环
            if(i > 0 && nums[i] == nums[i-1]) continue; // 去重
            int L = i+1;
            int R = len-1;
            while(L < R){
                int sum = nums[i] + nums[L] + nums[R];
                if(sum == 0){
                    ans.add(Arrays.asList(nums[i],nums[L],nums[R]));
                    while (L<R && nums[L] == nums[L+1]) L++; // 去重
                    while (L<R && nums[R] == nums[R-1]) R--; // 去重
                    L++;
                    R--;
                }
                else if (sum < 0) L++;
                else if (sum > 0) R--;
            }
        }
        return ans;
    }

Leetcode #19 删除链表的到数第n个节点
https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
dummy头节点的作用是方便访问head链表,和处理链表只有一个元素的状况

    public static ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode nodeR = dummy;
        ListNode nodeL = dummy;
        for(int i = 1; i <= n+1; i++){
            nodeR = nodeR.next;
        }
        while(nodeR != null){
            nodeR = nodeR.next;
            nodeL = nodeL.next;
        }
        //此时nodeL的指针指向的是被删除节点的前一位,直接返回nodeL是错的
        nodeL.next = nodeL.next.next;
        return dummy.next;
    }

Leetcode #141 判断链表是否存在环
https://leetcode-cn.com/problems/linked-list-cycle
设定2个步长不一样的指针,如果链表中存在环,那么两个指针会相遇(一个指针追上另一个指针)。

public boolean hasCycle(ListNode head) {
         ListNode l1 = head;
         ListNode 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;
     }

Leetcode #345 反转字符串中元音字符
https://leetcode-cn.com/problems/linked-list-cycle
反转字符串中的元音字符(交换位置)
从字符串数组的两头开始往中间遍历,如果是元音,交换位置

public static String reverseVowels(String s) {
        int l = 0;
        int r = s.length() - 1;
        char[] sArr = s.toCharArray();
        while (l < r){
            if(!isVowel(sArr[l])){
                l++;
            }else if(!isVowel(sArr[r])){
                r--;
            }else if(isVowel(sArr[l]) && isVowel(sArr[r])){
                char temp = sArr[l];
                sArr[l] = sArr[r];
                sArr[r] = temp;
                l++;
                r--;
            }
        }
        return new String(sArr);
    }

private static boolean isVowel(char c){
        if(c == 'a' || c =='e' || c == 'i' || c == 'o' || c =='u' ||
                c == 'A' || c == 'E' || c =='I' || c == 'O' || c == 'U'){
            return true;
        }
        return false;
    }

Leetcode #88 合并两个有序数组
https://leetcode-cn.com/problems/merge-sorted-array
给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 num1 成为一个有序数组。nums1的容量远大于nums2.

  /**
     * nums1 = [1,2,3,0,0,0], m = 3
     * nums2 = [2,5,6],       n = 3
     * 输出: [1,2,2,3,5,6]
     * 注意判断边界当 一个数组的有效元素取完时,只复制另一数组的元素
     */
    public static void merge(int[] nums1, int m, int[] nums2, int n) {
        int [] a = new int[m];
        System.arraycopy(nums1, 0, a, 0, m);
        int i = 0;
        int i1 = 0;
        int i2 = 0;
        while((i1 < m) || (i2 < n)){
            if(i1 != m && i2 != n && a[i1] < nums2[i2]){
                nums1[i] = a[i1];
                i1++;
            }else if(i1 != m && i2 != n && a[i1] > nums2[i2]){
                nums1[i] = nums2[i2];
                i2++;
            }else if(i1 == m){//当nums1的有效元素取完
                nums1[i] = nums2[i2];
                i2++;
            }else{//当nums2的有效元素取完
                nums1[i] = a[i1];
                i1++;
            }
            i++;
        }
    }

Leetcode #633 平方数之和
https://leetcode-cn.com/problems/sum-of-square-numbers
根两数和、三数和一样的解体思路,使用相对双指针。

    /* *
     * 判断对于常数c 是否存在两个整数n,m 使得n^2 + m^2 = c
     * 假设 m 为 c的开根号,那么如果对于所有大于m的整数都有其平方大于c
     * 所以整数必须在0-m之间选取,则令一个指针从0开始往右,令一指针从m开始往左移
     */
public static boolean judgeSquareSum(int c) {
        int result;
        int n = (int) Math.sqrt(c);
        int m = 0;
        while(m <= n){
            result = m*m + n*n;
            if(result > c){
                n--;
            }else if(result < c){
                m++;
            }else if(result == c){
                return true;
            }
        }
        return false;
    }

Leetcode #42 接雨水
https://leetcode-cn.com/problems/trapping-rain-water

    /**
     * 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
     *
     * 思路:两个指针,对于某一列的储水量,根据左右最短的高度决定
     * 所以从左到右遍历整个数组,求出每一个元素左边最高和右边最高就可以了
     *
     * 外部需要遍历n次,内部又要遍历n次 时间复杂度为O(n^2)
     */
public int trap(int[] height) {
        int result = 0;
        if(height == null || height.length == 0){
            return 0;
        }
        //最左和最右的柱子不可能储水
        for(int i = 1; i < height.length - 1; i++){
            int maxL = 0;
            int maxR = 0;
            for(int pL = i - 1; pL >= 0; pL--){
                maxL = Math.max(height[pL],maxL);
            }
            for(int pR = i + 1; pR < height.length; pR++){
                maxR = Math.max(height[pR],maxR);
            }
            if(Math.min(maxL,maxR) > height[i])
                result = result + Math.min(maxL,maxR) - height[i];
        }
        return result;
    }

    /**
     * 优化
     * 使用动态规划思想,第n根柱子的储水量决定与其左边最高柱子和右边最高柱子的最小值,
     * 创建2个数组,一个储存元素左边最高柱子的高度,另一个储存右边最高柱子的高度
     */
    public int trap1(int[] height) {
        int[] lMax = new int[height.length];
        int[] rMax = new int[height.length];
        int result = 0;

        for(int i = 0; i < height.length; i++){
            if(i == 0){
                lMax[i] = 0;
            }else {
                lMax[i] = Math.max(lMax[i-1],height[i-1]);
            }
        }
        for(int i = height.length - 1; i >= 0; i--){
            if(i == height.length - 1){
                rMax[i] = 0;
            }else {
                rMax[i] = Math.max(rMax[i+1],height[i+1]);
            }
        }
        for(int i = 1; i < height.length - 1; i++){
            //一定要判断当前柱子是否比左右都要高!!!
            if(Math.min(rMax[i],lMax[i]) > height[i])
                result = result + Math.min(rMax[i],lMax[i]) - height[i];
        }
        return result;
    }

Leetcode #3 无重复字符的最长子串
https://leetcode-cn.com/problems/longest-substring-without-repeating-characters

    /**
     *
     * 给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
     * 输入: "abcabcbb"
     * 输出: 3
     * 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
     *
     * 创建哈希表作为key-index的映射
     */
public static int lengthOfLongestSubstring(String s) {
        Map<Character,Integer> map = new HashMap<>();
        int result = 0;
        int i = 0;
        int j = 1;
        if(s.length() == 0 || s == null){
            return 0;
        }else if(s.length() == 1){
            return 1;
        }
        map.put(s.charAt(i),i);
        while(i < s.length() && j < s.length()){
            if(!map.containsKey(s.charAt(j))){
                map.put(s.charAt(j),j);
                result = Math.max(result,j-i+1);
            }else{
                i = Math.max(i,map.get(s.charAt(j))+1);
                map.put(s.charAt(j),j);
                result = Math.max(result,j-i+1);
            }
            j++;
        }
        return result;
    }

Leetcode #360 有序转化数组
https://leetcode-cn.com/problems/sort-transformed-array

    /**
     *
     * 给你一个已经 排好序 的整数数组 nums 和整数 a、b、c。对于数组中的每一个数 x,
     * 计算函数值 f(x) = ax2 + bx + c,请将函数值产生的数组返回。
     *
     * 要注意,返回的这个数组必须按照 升序排列,并且我们所期望的解法时间复杂度为 O(n)。
     *
     * 直接暴力API解决
     * 双指针考虑韦达定理求 极值点 和 对称线
     */
public static int[] sortTransformedArray(int[] nums, int a, int b, int c) {
        int[] result = new int[nums.length];
        for(int i = 0; i < nums.length; i++){
            result[i] = a*nums[i]*nums[i] + b*nums[i] + c;
        }
        Arrays.sort(result);
        return result;
    }

Leetcode #11 盛最多水的容器
https://leetcode-cn.com/problems/container-with-most-water/

question_11.jpg

    /**
     *
     * 给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。
     * 在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。
     * 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
     *
     * 思路:储水量又两块柱子的横坐标之差(j-1) 和 最短纵坐标 决定
     * area=(j-1)* min(height[i],height[j])
     * 两种移动方案,
     * 1.移动短板——面积可能增大可能减小,
     * 2.移动长板——面积一定减小
     * 每移动一次,横坐标之差(j-1)都会减少1,
     * 而面积由最短板决定,所以在横坐标之差减少时,如果移动长板,即最短板不变
     * 面积必然减小。所以只能移动长板
     */
    public int maxArea(int[] height) {
        int i = 0;
        int j = height.length - 1;
        int area = 0;
        while(i < j){
            area = Math.max(area,Math.min(height[j],height[i]));
            if(height[i] < height[j]){
                i++;
            }else {
                j--;
            }
        }
        return area;
    }

Leetcode #524 通过删除匹配字符串
https://leetcode-cn.com/problems/longest-word-in-dictionary-through-deleting

    /**
     * 给定一个字符串和一个字符串字典,找到字典里面最长的字符串,
     * 该字符串可以通过删除给定字符串的某些字符来得到。
     * 如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。
     *
     * 思路:先考虑一个字符串的情况,如果对于给定字符串先x,
     * 和字典字符串y,对于y中的每一个元素x都含有则说明
     * x可以经过删除得到y
     * 设定2个指针,一个指向x的头元素,一个指向y的头元素,
     * 如果相等则都往后移动一位,如果不相等则x的指针移动
     * 当y的指针移动到末尾时则说明元素全部可以匹配上
     */
public static String findLongestWord(String s, List<String> d) {
        String ans = "";
        for(String str : d){
            for(int i = 0, j = 0; i < s.length() && j < str.length(); i++){
                if(s.charAt(i) == str.charAt(j)){
                    j++;
                }
                if(j == str.length()){
                    if(ans.length() < str.length()
                            || (ans.length() == str.length() && ans.compareTo(str) > 0)){
                        ans = str;
                    }else if(ans.length() > str.length()
                            || (ans.length() == str.length() && ans.compareTo(str) <= 0)){
                        ans = ans;
                    }
                }
            }
        }
        return ans;
    }

Leetcode #680 验证是否为回文II
https://leetcode-cn.com/problems/valid-palindrome-ii
给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。

public static boolean validPalindrome(String s) {
        char[] sArr = s.toCharArray();
        int l = 0;
        int r = sArr.length - 1;
        while(l < r){
            if(sArr[l] != sArr[r]){ //不能用isPalindrome(sArr, l, r)判断,因为l,r没有自增或者自减
                return isPalindrome(sArr,l+1,r) || isPalindrome(sArr,l,r-1);
            }
            l++;
            r--;
        }
        return true;
    }

    private static boolean isPalindrome(char[] arr, int l, int r){
        while(l < r){
            if(arr[l] == arr[r]){
                l++;
                r--;
            }else {
                return false;
            }
        }
        return true;
    }
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容