[7/9]双指针Leetcode和LintCode习题[to be finished]

  • 1. Window Sum
  • 2. Remove Duplicates
  • 3. Remove Zeroes
  • 4. Valid Palindrome - too easy
  • 5. Rotate String
  • 6. Recover Rotated Sorted Array
  • 7. Two Sum - Less Than Or Equal To Target
  • 8. Two Sum - Greater Than Target
  • 9. Two Sum - Closest To Target
  • 10. 3Sum
  • 11. 4Sum
  • 12. Two Sum - Diff Equals To Target
  • 13. 3 Sum Closest
  • 14. Parittion Array
  • 15. Quick Select算法
  • 16. Partition Array By Odd and Even
  • 17. Interleaving positive and negative numbers
  • 18. Sort Letters By Case
  • 19. Sort Colors
  • 20. Sort Colors II
  • 21. Array Partition I on LeetCode
  • 22. Longest Substring Without Repeating Characters
Sorting Algorithms会有另一篇blog来讨论,这里先focus在习题上面。

1. Window Sum - LintCode

Description
Given an array of n integer, and a moving window(size k), move the window at each iteration from the start of the array, find the sum of the element inside the window at each moving.
Example
For array [1,2,7,8,5], moving window size k = 3.
1 + 2 + 7 = 10
2 + 7 + 8 = 17
7 + 8 + 5 = 20
return [10,17,20]
思路总结:再找一个“平行数组“:根据题目意思可以推断出来return的数组的size应该是原数组的length - 1 + k的长度。这里我们无需一个个地加每个数字,可以利用“新sum是旧sum抛弃第一个数字再加上原数组第i-1+k数字”这个规律。

public class Solution {
    /**
     * @param nums a list of integers.
     * @return the sum of the element inside the window at each moving.
     */
    public int[] winSum(int[] nums, int k) {
        if (nums == null || nums.length == 0){
			return new int [0];
		}
		int [] res = new int [nums.length - k + 1];
		for (int i = 0; i < k; i++){
			res[0] += nums[i];
		}
		for (int i = 1; i < res.length; i++){
			res[i] = res[i - 1] - nums[i - 1] + nums[i - 1 + k];
		}
    }
}

8. Rotate String LintCode

套路:把整个string先全部翻转过来,然后分两个部分再次翻转(分割线就在k那个地方)。很多题目都是相似操作。比如Leetcode的Rotate List

public class Solution {
    /**
     * @param str: An array of char
     * @param offset: An integer
     * @return: nothing
     */
    public void rotateString(char[] str, int offset) {
        // write your code here
        if (str == null || str.length == 0){
            return;
        }
        int k = offset % str.length;
        // Reverse the entire String
        reverse(str, 0, str.length - 1);
        // Reverse the first half part: 0 to k -1
        reverse(str, 0, k-1);
        // Reverse the second half of the string: from k to the end
        reverse(str, k, str.length - 1);
    }
    private void reverse (char[] s, int i, int j){
        while (i <= j){
            char temp = s[i];
            s[i] = s[j];
            s[j] = temp;
            i++;
            j--;
        }
    }
}

9. [LintCode] 533 Two Sum - Closest to target

Description
Given an array nums of n integers, find two integers in nums such that the sum is closest to a given number, target.
Return the difference between the sum of the two integers and the target.
Example
Given array nums = [-1, 2, 1, -4], and target = 4.
The minimum difference is 1. (4 - (2 + 1) = 1).
Challenge
Do it in O(nlogn) time complexity. -> 可以排序Arrays.sort()
这个题很简单,在two sum双指针的基础上再对现在的两个数之差进行比较,打擂台找到最小的difference。

//9. Two Sum - Closest to target
// Given an array of n integers, find two integers in nums such that the sum is closest //to a given number, target. Return the difference between the sum of the two integers //and the target. 

public int twoSumClosest (int[] nums, int target){
	if (nums == null || nums.length == 0){
		return -1;
	}
	Arrays.sort(nums);
	int i = 0;
	int j = nums.length - 1;
	int best = Integer.MAX_VALUE;
	while (i < j){
		int diff = Math.abs((nums[i] + nums[j]) - target);
		int temp = nums[i] + nums[j];
		best = Math.min(best, diff);
		if (temp > target){
			j--;
		} else {
			i++;
		}
	}
	return best; 
}

14. Partition Array by LintCode

不需要排序(排序了就没意义了),把数组以k为界限,分成左右两部分。要求左边比k小,右边大于等于k。return第一个大于等于K的数字的idnex。同样,用双指针的办法, 先找出左边第一个大于等于K的数字,与右边小于K的数字【置换】,直到两个指针相邻。跳出循环后判断return的条件即可。

// 14. Partition Array - LintCode
// Given an array nums of integers and an int k, partition the array (i.e move th
// elements in "nums") such that:
// All elements < k are moved to the left
// All elements >= k are moved to the right
// Return the partitioning index, i.e the first index i nums[i] >= k.
public class Solution {
    /**
     * @param nums: The integer array you should partition
     * @param k: An integer
     * @return: The index after partition
     */
    public int partitionArray(int[] nums, int k) {
        // write your code here
        if (nums == null || nums.length == 0) {
            return 0;
        }
        // Two Pointers
        int i = 0;
        int j = nums.length - 1;
        while ( i < j ) {
            // Fing the first value in the left side that 
            // is bigger than k 
            while ( i < j && nums[i] < k){
                i++;
            }
            // Find the first value in the right side that is
            // smaller than k 
            while ( i < j && nums[j] >= k){
                j--;
            }
            // Swap two values
            if ( i < j ){
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
                i++;
                j--;
            }
        }
        if (nums[i] < k){
            return i + 1;
        } else {
            return i;
        }
    }
}

17. LintCode: Interleaving Positive and Negative Numbers

给定一个数组,要求你in-place的条件下把这个数组partition成正负交叉的情况。这个题并没有说清楚正负数的数量是不是最大相差为1,但是Looks like it is。所以我们只需要判断究竟是正数更多还是负数更多即可。哪种数字多就以哪种数字开头。

public class Solution {
    /*
     * @param A: An integer array.
     * @return: nothing
     */
    public void rerange(int[] A) {
        // write your code here
        // Edge Case
        if (A == null || A.length < 3){
            return;
        }
        int length = A.length;
        int postiveCount = 0;
        //先假设负数的个数更多,以负数开头
        int postiveIndex = 0;
        int pos = 1;
        int neg = 0;
        
        // Shifting all the postive numbers to the left
        // 用一个循环,但凡我们找到了正数,
        // 就swap到下个正数应该在的位置(positiveIndex)。
        
        for (int i = 0; i < A.length; i++){
            if(A[i] > 0){
                swap(A, postiveIndex++, i);
                postiveCount++;
            }
        }
        
        //[1,2,3,4,-1,-2,-3]
        
        // More positives 
        if (postiveCount > length / 2){
            pos = 0;
            neg = 1;
            int left = 0;
            int right = length - 1;
            while (left < right) {
                swap(A, left, right);
                left++;
                right--;
            }
        }
        
        //[-1,-2,-3,1,2,3,4]
        //[1,-2,-3,-1,2,3,4]
        //[1,-2,3,-1,2,-3,4]
        //done
        while (pos < length && neg < length){
            while(pos < length && A[pos] > 0){
                pos+=2;
            }
            while(neg<length && A[neg] <= 0){
                neg+=2;
            }
            if(neg >= length && pos>= length){
                break;
            }
            swap(A, pos, neg);
        }
        
    }
    private void swap (int[]A, int i, int j){
        int temp = A[i];
        A[i] = A[j];
        A[j] = temp;
    }
}

LeetCode 561. Array Partition I

我的solution:
Runtime: 16 ms, faster than 11.37% of Java online submissions for Array Partition I.
Memory Usage: 41.6 MB, less than 96.43% of Java online submissions for Array Partition I.

总结概括

自己对于这道题难度打分:2/10,题目陈述不够清晰
这道题花费的时间:15min
本题提交了几次:1
一句话评价本题:easy,但是我的解法没有必要。只需要return一个value,我却习惯性地用双指针把array都sort好了。anyway,not a bad practice。

题目分析

描述题意:看题
第一思路是什么:sort
思路过程:把array排序一遍,然后“跳着”取数。
正确思路以及花费时间:sort完跳着一个个把数字加起来,return

代码实现

花费时间:15min
静态查错花费多久时间:1min
代码的可读性:不好,一点也不好

最终评测

你自己设置的数据,以及你想要卡掉的点:ok
你的数据是否符合最终的期望:不好,白做很多功。这个题目很简单。

class Solution{
    public int arrayPairSum(int[] nums) {
        if (nums == null || nums.length == 0){
            return 0;
        }
        if(nums.length < 3){
            return Math.min(nums[0], nums[1]);
        }
        Arrays.sort(nums);
        int slow = 1;
        int fast = 2;
        while (slow < fast && fast < nums.length){
            swap(nums, slow, fast);
            slow++;
            fast+=2;
        }
        int end = nums.length - 1;
        while (slow<end){
            swap(nums, slow, end);
            slow++;
            end--;
        }
        int sum = 0;
        for (int i = 0; i < nums.length / 2; i++){
            sum += nums[i];
        }
        return sum;
    }
    private void swap (int[] nums, int i, int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] =temp;
    }
}

别人的:

public class Solution {
    public int arrayPairSum(int[] nums) {
        Arrays.sort(nums);
        int result = 0;
        for (int i = 0; i < nums.length; i += 2) {
            result += nums[i];
        }
        return result;
    }
}

75. Sort Colors

简单粗暴:给定一个由0,1,2组成的数组,in-place排序。更过分一点:双指针,one pass排序。
i相等于一个活动的指针,pl是为了指向左边起的1的位置,因为i遇到0的时候要和前面的1换位置。i遇到1的时候,啥也不做,继续往下走。i遇到2的时候,和pr换位置,因为pr是从右边开始的。

class Solution {
    public void sortColors(int[] nums) {
        if (nums == null || nums.length == 0){
            return;
        }
        int pl = 0;
        int pr = nums.length - 1;
        int i = 0;
        while (i <= pr){
            if (nums[i] == 0){
                swap(nums, i, pl);
                i++;
                pl++;
            } else if (nums[i] == 2){
                swap(nums, i, pr);
                pr--;
            } else {
                i++;
            }
        }
    }
    private void swap (int[] nums, int i, int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

3. Longest Substring Without Repeating Characters

最开始我是用brute force的,不出意料地超时。(其实写对了,但是oj无脑判超时,悲伤)。

class Solution {
    public int lengthOfLongestSubstring(String s) {
        if (s == null || s.length() == 0){
            return 0;
        }
        int longest = 0;
        for (int i = 0; i < s.length(); i++){
            for (int j = i + 1; j <= s.length(); j++){
                if (noRepeat(s.substring(i, j))){
                    longest = Math.max(longest, j - i);
                }
            }
        }
        return longest;
    }
    private boolean noRepeat (String s){
        Set <Character> set = new HashSet <>();
        for (int i = 0 ; i < s.length(); i++){
            if (set.contains(s.charAt(i))){
                return false;
            } else {
                set.add(s.charAt(i));
            }
        }
        return true;
    }
}

后来参考大神的想法:
用一个hashset和两个指针。Set用于记忆一段substring。
慢指针指向一段无重复substring(就是这个set)的头,快指针一直遍历这个string,直到找到和慢指针相同的字母。
找到和慢指针一样的字母之后,应该在set中删除慢指针元素,这个时候我们的set才会依然保持“无重复字母+substring”这个性质。更新慢指针,现在慢指针指向现在substring的第一个字母啦;)

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int i = 0;
        int j = 0;
        int max = 0;
        Set <Character> set = new HashSet<>();
        while (j < s.length()){
            if (!set.contains(s.charAt(j))){
                set.add(s.charAt(j));
                j++;
                max = Math.max(max, set.size());
            } else {
                set.remove(s.charAt(i));
                i++;
            }
        }
        return max;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值