zoukankan      html  css  js  c++  java
  • 双指针算法

    一、相向双指针

    1、一个典型的相向双指针问题就是翻转字符串的问题。

    三步翻转法:

    public void reverse(char[] s) {
        int left = 0, right = s.length - 1;
        while (left < right) {
            char temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--; 
        }
    }

    2、 给一个字符串,判断这个字符串是不是回文串。我们可以使用双指针算法轻易解决。

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

    Follow Up 1 : 不区分大小写,忽略非英文字母

    http://www.lintcode.com/problem/valid-palindrome/

    这个问题本身没有太大难度,只是为了给过于简单的 isPalindrome 函数增加一些实现技巧罢了。

    代码上和上面的 isPalindrome 函数主要有2个区别:

      1. 在 i++ 和 j-- 的时候,要用 while 循环不断的跳过非英文字母。

      2. 比较的时候都要变成小写之后再比较。

      

    public class Solution {
        public boolean isPalindrome(String s) {
            if (s == null || s.length() == 0) {
                return true;
            }
    
            int front = 0;
            int end = s.length() - 1;
            while (front < end) {
                while (front < s.length() && !isvalid(s.charAt(front))) { // nead to check range of a/b
                    front++;
                }
    
                if (front == s.length()) { // for empty string “.,,,”     
                    return true; 
                }           
    
                while (end >= 0 && ! isvalid(s.charAt(end))) { // same here, need to check border of a,b
                    end--;
                }
    
                if (Character.toLowerCase(s.charAt(front)) != Character.toLowerCase(s.charAt(end))) {
                    break;
                } else {
                    front++;
                    end--;
                }
            }
    
            return end <= front; 
        }
    
        private boolean isvalid (char c) {
            return Character.isLetter(c) || Character.isDigit(c);
        }
    }

    Follow Up 2 :允许删掉一个字母(类似的,允许插入一个字母)

    http://www.lintcode.com/problem/valid-palindrome-ii/

      1、 依然用相向双指针的方式从两头出发,两根指针设为 L 和 R。

      2、如果 S[L] 和 S[R] 相同的话,L++, R--

      3、如果 S[L] 和 S[R] 不相同的话,停下来,此时可以证明,如果能够通过删除一个字符使得整个字符串变成回文串的话,那么一定要么是 S[L], 要么是 S[R]。

    class Solution {
        public boolean validPalindrome(String s) {
            int left = 0, right = s.length() - 1;
            
            while (left < right) {
                if (s.charAt(left) != s.charAt(right)) {
                    break;
                }
                left++;
                right--;
            }
            
            if (left >= right) {
                return true;
            }
            
            return isSubPalindrome(s, left + 1, right) || isSubPalindrome(s, left, right - 1);
        }
        
        private boolean isSubPalindrome(String s, int left, int right) {
            while (left < right) {
                if (s.charAt(left) != s.charAt(right)) {
                    return false;
                }
                left++;
                right--;
            }
            
            return true;
        }
    }

    3、双指针的鼻祖:两数之和 = target,给一个整数数组,找到两个数使得他们的和等于一个给定的数 target。返回这两个数。

      方法一:用哈希表来解决。时间复杂度是 O(n),空间复杂度是 O(n)

      方法二:先排序,再使用双指针算法来解决。时间复杂度是 O(nlogn),空间复杂度是 O(1)

    二、同向双指针

    1、 数组去重问题 Remove duplicates in an array

    https://www.lintcode.com/problem/remove-duplicate-numbers-in-array/description

    这个问题有两种做法,第一种做法比较容易想到的是,把所有的数扔到 hash 表里,然后就能找到不同的整数有哪些。但是这种做法会耗费额外空间 O(n)。面试官会追问,如何不耗费额外空间。

    public class Solution {
        /**
         * @param nums: an array of integers
         * @return: the number of unique integers
         */
        public int deduplication(int[] nums) {
            // write your code here
            if(nums == null || nums.length == 0) {
                return 0;
            }
            Set<Integer> set = new HashSet<>();
            for(int i = 0; i < nums.length; i++) {
                set.add(nums[i]);
            }
            int result = 0;
            for(Integer num: set) {
                nums[result++] = num;
            }
            return result;
        }
    }

    此时我们需要用到双指针算法,首先将数组排序,这样哪些重复的整数就会被挤在一起。然后用两根指针,一根指针走得快一些遍历整个数组,另外一根指针,一直指向当前不重复部分的最后一个数。快指针发现一个和慢指针指向的数不同的数之后,就可以把这个数丢到慢指针后面的一个位置,并把慢指针 ++

    public class Solution {
        /**
         * @param nums: an array of integers
         * @return: the number of unique integers
         */
        public int deduplication(int[] nums) {
            // write your code here
            if(nums == null || nums.length == 0) {
                return 0;
            }
            int result = 0;
            Arrays.sort(nums);
            for(int i = 0; i < nums.length; i++) {
                if(nums[i] != nums[result]) {
                    nums[++result] = nums[i];
                }
            }
            return result + 1;
        }
    }

    2、滑动窗口问题 Window Sum

    求出一个数组每 kk 个连续整数的和的数组。如 nums = [1,2,3,4], k = 2 的话,window sum 数组为 [3,5,7]
    http://www.lintcode.com/problem/window-sum/

    3、两数之差问题 Two Difference

    在一个数组中,求出满足两个数之差等于 target 的那一对数。返回他们的下标。

    LintCode 练习地址:
    http://www.lintcode.com/problem/two-sum-difference-equals-to-target/

    方法一:我自己的方法:(不好,时间复杂度有点高)

     public int[] twoSum7(int[] nums, int target) {
            // write your code here
            int len = nums.length;
            //Arrays.sort(nums);
            for(int left = 0; left < len; left++) {
                int right = left + 1;
                while(right < len) {
                    if(Math.abs(nums[right] - nums[left]) == Math.abs(target)) {
                        return new int[]{left + 1, right + 1};
                    }
                    right++;
                }
            }
            return new int[]{};
        }

    方法二:使用HashMap的方法,时间复杂度为 O(n), 空间复杂度也是O(n)

    public int[] twoSum7(int[] nums, int target) {
            // write your code here
            if(nums == null || nums.length < 2) {
                return new int[]{};
            }
            Map<Integer, Integer> map = new HashMap<>();
            for(int i = 0; i < nums.length; i++) {
                int sum = nums[i] + target;
                if(map.containsKey(sum)) {
                    int index = map.get(sum);
                    int[] pair = new int[2];
                    pair[0] = index + 1;
                    pair[1] = i + 1;
                    return pair;
                }
                
                int diff = nums[i] - target;
                if(map.containsKey(diff)) {
                    int index = map.get(diff);
                    int[] pair = new int[2];
                    pair[0] = index + 1;
                    pair[1] = i + 1;
                    return pair;
                }
                map.put(nums[i], i);
            }
            return new int[]{};
        }

    4、链表中点问题 Middle of Linked LIst

        public ListNode middleNode(ListNode head) {
            // write your code here
            if(head == null) return null;
            ListNode dummy = new ListNode(0);
            dummy.next = head;
            ListNode slow = dummy, fast = dummy;
            while(fast != null && fast.next != null) {
                fast = fast.next.next;
                slow = slow.next;
            }
            return slow;
        }

    5、带环链表问题 Linked List Cycle

  • 相关阅读:
    使用echarts插件做图表常见的几个问题(五)——图形的两种渲染方式
    数组对象如何根据对象中某个字段分组
    JS监听浏览器后退事件
    使用echarts插件做图表常见的几个问题(四)—— 柱状图中以虚线展示重合的柱子
    使用echarts插件做图表常见的几个问题(三)—— 图表标线的使用
    使用echarts插件做图表常见的几个问题(二)—— 实现多Y轴
    使用echarts插件做图表常见的几个问题(一)—— 折线图局部虚线
    如何判断touch事件滑动的方向?
    解决session共享方案
    设计模式总结
  • 原文地址:https://www.cnblogs.com/jenna/p/10867192.html
Copyright © 2011-2022 走看看