九章算法题解记录【二】二分查找及逆序数组相关

从此开始顺着刷完
先给一个二分查找模板,以后都按照此模板来写。

public class 二分查找模板 {
    int binarySearch(int[] nums, int start, int end, int target){

        // 错误输入条件
        if (nums.length == 0 || start > end)
            return -1;
        // int start = 0, end = len - 1;   //当函数参数不包括start, end时
        while (start + 1 < end) { //注意这里是start+1,是<不是<=
            int mid = start + (end - start) / 2;
            // 注意: =, <, > 三种情况,mid 不+1也不-1
            if (nums[mid] == target) {
                // 寻找最后一个重复元素位置
                // start = mid;
                // 寻找第一个重复元素位置
                // end = mid;
                // 注释掉return  之后在while之后进行判断 if
                return mid;
            } else if (nums[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        // 注意,循环结束后,单独处理start和end
        if (nums[start] == target) {
            return start;
        }
        if (nums[end] == target) {
            return end;
        }
        return -1;
    }
}

简单的二分查找实际上并不困难,但是需要考虑到假如有多个元素都是target值,需要找个目标值所在的区间的开头与结尾。
1 Search for a range http://www.lintcode.com/problem/search-for-a-range/

public class Solution {
    /**
     * @param A: an integer sorted array
     * @param target: an integer to be inserted
     * @return: a list of length 2, [index1, index2]
     */
    public int[] searchRange(int[] A, int target) {
        if (A == null) return null;
        if (A.length == 0) return new int[]{-1, -1};
        int start = searchForStart(A, target);
        int end = searchForEnd(A, target);
        return new int[]{start, end};
    }

    private int searchForStart(int[] nums, int target){
        int start = 0, end = nums.length - 1;
        if (nums.length == 0 || start > end)
            return -1;
        // int start = 0, end = len - 1;   //当函数参数不包括start, end时
        while (start + 1 < end) { //注意这里是start+1,是<不是<=
            int mid = start + (end - start) / 2;
            // 注意: =, <, > 三种情况,mid 不+1也不-1
            if (nums[mid] == target) {
                end = mid;
            } else if (nums[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (nums[start] == target) {
            return start;
        }
        if (nums[end] == target) {
            return end;
        }
        return -1;
    }
    private int searchForEnd(int[] nums, int target){
        int start = 0, end = nums.length - 1;
        if (nums.length == 0 || start > end)
            return -1;
        // int start = 0, end = len - 1;   //当函数参数不包括start, end时
        while (start + 1 < end) { //注意这里是start+1,是<不是<=
            int mid = start + (end - start) / 2;
            // 注意: =, <, > 三种情况,mid 不+1也不-1
            if (nums[mid] == target) {
                start = mid;
            } else if (nums[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (nums[end] == target) {
            return end;
        }
        if (nums[start] == target) {
            return start;
        }
        return -1;
    }
}

解题思路:代码看着长,其实逻辑清晰且简单,可以用来做二分查找区间的所有模板。

2 Search Insert Position https://www.lintcode.com/problem/search-insert-position/ easy

public class Solution {
    /**
     * @param A: an integer sorted array
     * @param target: an integer to be inserted
     * @return: An integer
     */
    public int searchInsert(int[] A, int target) {
        int start = 0, end = A.length - 1;
        if (A.length == 0)
            return 0;
        //int start = 0, end = len - 1;   //当函数参数不包括start, end时
        while (start + 1 < end) { //注意这里是start+1,是<不是<=
            int mid = start + (end - start) / 2;
            // 注意: =, <, > 三种情况,mid 不+1也不-1
            if (A[mid] == target) {
                return mid;
            } else if (A[mid] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (A[start] >= target) {
            return start;
        }
        if (A[end] >= target) {
            return end;
        } else{
            return end + 1;
        }
    }
}

简单题,找到第一个大于等于它的数字,按照模板,就算没找到,start end肯定包含这个数的区间,所以说,判断start end是否满足, 考虑边界条件,都没找到,说明超过此数组,返回end + 1

3 Search in a 2D Matrix

public class Solution {
    /**
     * @param matrix: matrix, a list of lists of integers
     * @param target: An integer
     * @return: a boolean, indicate whether matrix contains target
     */
    public boolean searchMatrix(int[][] matrix, int target) {
        // 错误输入条件
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return false;
        }
        int rows = matrix.length;
        int cols = matrix[0].length;
        int start = 0, end = rows * cols - 1;   //当函数参数不包括start, end时
        while (start + 1 < end) { //注意这里是start+1,是<不是<=
            int mid = start + (end - start) / 2;
            // 注意: =, <, > 三种情况,mid 不+1也不-1
            if (matrix[mid / cols][mid % cols] == target) {
                return true;
            } else if (matrix[mid / cols][mid % cols] < target) {
                start = mid;
            } else {
                end = mid;
            }
        }
        // 注意,循环结束后,单独处理start和end ,第一个先看start 最后一个先看end
        if (matrix[start / cols][start % cols] == target) {
            return true;
        }
        if (matrix[end / cols][end % cols] == target) {
            return true;
        }
        return false;
    }
}

此种start + 1 < end的写法,最后一定要判断start与end处是否满足!!!!!!

4 Search a 2D Matrix II

public class Solution {
    /**
     * @param matrix: A list of lists of integers
     * @param target: An integer you want to search in matrix
     * @return: An integer indicate the total occurrence of target in the given matrix
     */
    public int searchMatrix(int[][] matrix, int target) {
        // 剑指offer原题哈,从右上开始找
        if (matrix == null || matrix.length == 0) {
            return 0;
        }
        if (matrix[0] == null || matrix[0].length == 0) {
            return 0;
        }
        int rows = matrix.length;
        int cols = matrix[0].length;
        int res = 0;
        int i = 0, j = cols - 1;
        while (i <= rows - 1 && j >= 0) {
            if (matrix[i][j] == target){
                res ++;
                i++;
            } else if (matrix[i][j] > target) {
                j--;
            } else {
                i++;
            }
        }
        return res;
    }
}

剑指offer原题,找到了也要i++。

5 Find Peak Element

public class Solution {
    /**
     * @param A: An integers array.
     * @return: return any of peek positions.
     */
    public int findPeak(int[] A) {
        if (A == null || A.length < 3) {
            return 0;
        }
        int start = 1, end = A.length - 2;
        while (start + 1 < end){
            int mid = start + (end - start) / 2;
            if (A[mid] > A[mid + 1] && A[mid] > A[mid - 1]){
                return mid;
            } else if (A[mid] > A[mid - 1] && A[mid] < A[mid] + 1) {
                start = mid;
            } else {
                end = mid;
            }
        }
        if (start >= 1 && A[start] > A[start - 1] && A[start] > A[start + 1]){
            return start;
        }
        if (end >= 1 && A[end] > A[end - 1] && A[end] > A[end + 1]){
            return end;
        }
        return -1;
    }
}

一遍AC,实际上内核还是二分。

下面开始Rotate Array型题目

1 Find Minimum in Rotated Sorted Array https://www.lintcode.com/problem/find-minimum-in-rotated-sorted-array/description

public class Solution {
    /**
     * @param nums: a rotated sorted array
     * @return: the minimum number in the array
     */
    public int findMin(int[] nums) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int start = 0, end = nums.length - 1;
        int target = nums[nums.length - 1];
        
        // find the first element <= target
        while (start + 1 < end) {    		//用来控制区间大小
            int mid = start + (end - start) / 2;
            if (nums[mid] <= target) { 		//如果mid位置上的数字小于等于最右端的数字时,区间向左移动
                end = mid;
            } else {
                start = mid;			 
            }
        }
        return Math.min(nums[start],nums[end]);  //最终返回start和end位置上较小的数字即可
        
    }
}

解题思路: 记录一下末端作为target。

2 Find Minimum in Rotated Sorted Array II https://www.lintcode.com/problem/find-minimum-in-rotated-sorted-array-ii/

public int findMin(int[] num) {
        //  这道题目在面试中不会让写完整的程序
        //  只需要知道最坏情况下 [1,1,1....,1] 里有一个0
        //  这种情况使得时间复杂度必须是 O(n)
        //  因此写一个for循环就好了。
        //  如果你觉得,不是每个情况都是最坏情况,你想用二分法解决不是最坏情况的情况,那你就写一个二分吧。
        //  反正面试考的不是你在这个题上会不会用二分法。这个题的考点是你想不想得到最坏情况。
        int min = num[0];
        for (int i = 1; i < num.length; i++) {
            if (num[i] < min)
                min = num[i];
        }
        return min;
    }

解题思路:如注释,当有重复元素,要考虑到111101, 101111这种极端情况,所以不能用二分,只能O(n)遍历一次.

3 Search in Rotated Sorted Array http://www.lintcode.com/problem/search-in-rotated-sorted-array/

public class Solution {
    /**
     * @param A: an integer rotated sorted array
     * @param target: an integer to be searched
     * @return: an integer
     */
    public int search(int[] A, int target) {
        if (A == null || A.length == 0) {
            return -1;
        }

        int start = 0, end = A.length - 1;

        while (start + 1 < end) {    		//用来控制区间大小
            int mid = start + (end - start) / 2;
            if (A[mid] == target) { 		
                return mid;
            }  
            if (A[start] < A[mid]) {
                // situation 1, red line
                if (A[start] <= target && target <= A[mid]) {
                    end = mid;
                } else {
                    start = mid;
                }
            } else {
                // situation 2, green line
                if (A[mid] <= target && target <= A[end]) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
        }

        if (A[start] == target) {
            return start;
        }
        if (A[end] == target) {
            return end;
        }
        return -1;

    }
}

解题思路:本题没A的原因在于用了第一题的思路,找末尾做哨兵,但是情况没有考虑全,应该是找顺序部分(start mid, mid end肯定有一部分是顺序),判断target是否在该部分里,不是就去找一个部分,不断缩小范围。 这才是二分查找嘛。

4 Search in Rotated Sorted Array II http://www.lintcode.com/problem/search-in-rotated-sorted-array-ii/

public class Solution {
    // 这个问题在面试中不会让实现完整程序
    // 只需要举出能够最坏情况的数据是 [1,1,1,1... 1] 里有一个0即可。
    // 在这种情况下是无法使用二分法的,复杂度是O(n)
    // 因此写个for循环最坏也是O(n),那就写个for循环就好了
    //  如果你觉得,不是每个情况都是最坏情况,你想用二分法解决不是最坏情况的情况,那你就写一个二分吧。
    //  反正面试考的不是你在这个题上会不会用二分法。这个题的考点是你想不想得到最坏情况。
    public boolean search(int[] A, int target) {
        for (int i = 0; i < A.length; i ++) {
            if (A[i] == target) {
                return true;
            }
        }
        return false;
    }
}

一样,有重复只能On 因为有极端

Median Kth 有序数组
5 Median of Two Sorted Arrays http://www.lintcode.com/problem/median-of-two-sorted-arrays/
要求能从中位数引申到Kth

public class MedianofTwoSortedArrays {
    /*
     * @param A: An integer array
     * @param B: An integer array
     * @return: a double whose format is *.5 or *.0
     */
    public double findMedianSortedArrays(int[] A, int[] B) {
        int n = A.length + B.length;

        if (n % 2 == 0) {
            return (findKth(A, B, n / 2) + findKth(A, B, n / 2 + 1)) / 2.0;
        }

        return findKth(A, B, n / 2 + 1);
    }

    // k is not zero-based, it starts from 1
    public int findKth(int[] A, int[] B, int k) {
        if (A.length == 0) {
            return B[k - 1];
        }
        if (B.length == 0) {
            return A[k - 1];
        }

        int start = Math.min(A[0], B[0]);
        int end = Math.max(A[A.length - 1], B[B.length - 1]);

        // find first x that >= k numbers is smaller or equal to x
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (countSmallerOrEqual(A, mid) + countSmallerOrEqual(B, mid) < k) {
                start = mid;
            } else {
                end = mid;
            }
        }

        if (countSmallerOrEqual(A, start) + countSmallerOrEqual(B, start) >= k) {
            return start;
        }

        return end;
    }

    private int countSmallerOrEqual(int[] arr, int number) {
        int start = 0, end = arr.length - 1;

        // find first index that arr[index] > number;
        while (start + 1 < end) {
            int mid = start + (end - start) / 2;
            if (arr[mid] <= number) {
                start = mid;
            } else {
                end = mid;
            }
        }

        if (arr[start] > number) {
            return start;
        }

        if (arr[end] > number) {
            return end;
        }

        return arr.length;
    }
}

二分写法 O(log(range) * (log(n) + log(m))的答案。还有分治写法 log(n + m)。

public class Solution {
    public double findMedianSortedArrays(int A[], int B[]) {
        int n = A.length + B.length;
        
        if (n % 2 == 0) {
            return (
                findKth(A, 0, B, 0, n / 2) + 
                findKth(A, 0, B, 0, n / 2 + 1)
            ) / 2.0;
        }
        
        return findKth(A, 0, B, 0, n / 2 + 1);
    }

    // find kth number of two sorted array
    public static int findKth(int[] A, int startOfA,
                              int[] B, int startOfB,
                              int k){       
        if (startOfA >= A.length) {
            return B[startOfB + k - 1];
        }
        if (startOfB >= B.length) {
            return A[startOfA + k - 1];
        }

        if (k == 1) {
            return Math.min(A[startOfA], B[startOfB]);
        }
        
        int halfKthOfA = startOfA + k / 2 - 1 < A.length
            ? A[startOfA + k / 2 - 1]
            : Integer.MAX_VALUE;
        int halfKthOfB = startOfB + k / 2 - 1 < B.length
            ? B[startOfB + k / 2 - 1]
            : Integer.MAX_VALUE; 
        
        if (halfKthOfA < halfKthOfB) {
            return findKth(A, startOfA + k / 2, B, startOfB, k - k / 2);
        } else {
            return findKth(A, startOfA, B, startOfB + k / 2, k - k / 2);
        }
    }
}

每次比较 A[start A + k/2 - 1] 与 B[startB + k/2 - 1] 假设A的小,那么说明假如把A那部分全部丢弃,也不会影响,之后将k = k - k/2 ,之后每次判断 startIndex += 当前的k/2 结束条件是k == 1或者其中某个数组为空(start >= length)

Rotated Recover类操作数组的问题,用三段reverse法!
6 Recover Rotated Sorted Array http://www.lintcode.com/problem/recover-rotated-sorted-array/
考虑到数组中可能有重复元素,只能On找开头,再用三段reverse法

public class Solution {
    /**
     * @param nums: The rotated sorted array
     * @return: The recovered sorted array
     */
    private void reverse(ArrayList<Integer> nums, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            int temp = nums.get(i);
            nums.set(i, nums.get(j));
            nums.set(j, temp);
        }
    }

    public void recoverRotatedSortedArray(ArrayList<Integer> nums) {
        for (int index = 0; index < nums.size() - 1; index++) {
            //找到第一个比后面的数大的数,以[4,5,1,2,3]为例,找到5,翻转[4,5]得到[5,4],翻转[1,2,3]得到[3,2,1]
            //最后翻转[5,4,3,2,1]得到[1,2,3,4,5]
            if (nums.get(index) > nums.get(index + 1)) {
                reverse(nums, 0, index);
                reverse(nums, index + 1, nums.size() - 1);
                reverse(nums, 0, nums.size() - 1);
                return;
            }
        }
    }
}

注意点:有序数组需要考虑是否有重复元素,有重复则不能用二分!

7 Rotate String http://www.lintcode.com/problem/rotate-string/

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;
            
        offset = offset % str.length;
        reverse(str, 0, str.length - offset - 1);
        reverse(str, str.length - offset, str.length - 1);
        reverse(str, 0, str.length - 1);
    }
    
    private void reverse(char[] str, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            char temp = str[i];
            str[i] = str[j];
            str[j] = temp;
        }
    }
}

三段反转法

8 Reverse Words in a String http://www.lintcode.com/problem/reverse-words-in-a-string/

public class Solution {
    /*
     * @param s: A string
     * @return: A string
     */
    public String reverseWords(String s) {
        if (s == null) {
            return null;
        }
        if (s.length() == 0) {
            return "";
        }
        String[] strarr = s.split("\\s+");
        reverse(strarr, 0, strarr.length - 1);
        return String.join(" ",strarr);
        
    }
    private void reverse(String[] strs, int start, int end){
        for (int i = start, j = end; i < j; i++, j--) {
            String temp = strs[i];
            strs[i] = strs[j];
            strs[j] = temp;
        }
    }
}

api调用,注意空格分割是 split("\\s+")

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值