• Chapter two Binary Search(二分法)


    1.classical-binary-search(经典二分查找问题)【二分查找模板】

    在一个排序数组中找一个数,返回该数出现的任意位置,如果不存在,返回-1

    public class Solution {
        /**
         * @param nums: An integer array sorted in ascending order
         * @param target: An integer
         * @return an integer
         */
        public int findPosition(int[] nums, int target) {
            // Write your code here
            if (nums == null || nums.length == 0) {
                return -1;
            }
            int start = 0;
            int end = nums.length - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (nums[mid] == target) {
                    return 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;
        }
    }
    View Code

    2.first-position-of-target(二分查找)

    给定一个排序的整数数组(升序)和一个要查找的整数target,用O(logn)的时间查找到target第一次出现的下标(从0开始),如果target不存在于数组中,返回-1

    class Solution {
        /**
         * @param nums: The integer array.
         * @param target: Target to find.
         * @return: The first position of target. Position starts from 0.
         */
        public int binarySearch(int[] nums, int target) {
            //write your code here
            if (nums == null || nums.length == 0) {
                return -1;
            }
            int start = 0;
            int end = nums.length - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (nums[mid] < target) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
            if (nums[start] == target) {
                return start;
            }
            if (nums[end] == target) {
                return end;
            }
            return -1;
        }
    }
    View Code

    3.last-position-of-target(目标最后位置)

    给一个升序数组,找到target最后一次出现的位置,如果没出现过返回-1

    public class Solution {
        /**
         * @param nums: An integer array sorted in ascending order
         * @param target: An integer
         * @return an integer
         */
        public int lastPosition(int[] nums, int target) {
            // Write your code here
            if (nums == null || nums.length == 0) {
                return -1;
            }
            int start = 0;
            int end = nums.length - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (nums[mid] > target) {
                    end = mid;
                } else {
                    start = mid;
                }
            }
            if (nums[end] == target) {
                return end;
            }
            if (nums[start] == target) {
                return start;
            }
            return -1;
        }
    }
    View Code

    第一境界:
    二分位置 之 OOXX 一般会给一个数组 找数组中第一个/最后一个满足某个条件的位置OOOOOOO...OOXX….XXXXXX

    4.first-bad-version(第一个错误的代码版本)

    代码库的版本号是从 1 到 n 的整数。某一天,有人提交了错误版本的代码,因此造成自身及之后版本的代码在单元测试中均出错。请找出第一个错误的版本号。

    你可以通过 isBadVersion 的接口来判断版本号 version 是否在单元测试中出错,java的调用方式是SVNRepo.isBadVersion(v)

    /**
     * public class SVNRepo {
     *     public static boolean isBadVersion(int k);
     * }
     * you can use SVNRepo.isBadVersion(k) to judge whether 
     * the kth code version is bad or not.
    */
    class Solution {
        /**
         * @param n: An integers.
         * @return: An integer which is the first bad version.
         */
        public int findFirstBadVersion(int n) {
            // write your code here
            int start = 1;
            int end = n;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (SVNRepo.isBadVersion(mid)) {
                    end = mid;
                } else {
                    start = mid;
                }
            }
            if (SVNRepo.isBadVersion(start)) {
                return start;
            }
            return end;
        }
    }
    View Code

    注意:函数要有返回值,判断start和end时不能都使用if。

    5.search-in-a-big-sorted-array(在大数组中查找)

    给一个按照升序排序的正整数数组。这个数组很大以至于你只能通过固定的接口 ArrayReader.get(k) 来访问第k个数,并且你也没有办法得知这个数组有多大。找到给出的整数target第一次出现的位置。你的算法需要在O(logk)的时间复杂度内完成,k为target第一次出现的位置的下标。如果找不到target,返回-1。如果你访问了 ArrayReader 中一个不可访问的下标(比如越界),ArrayReader 会返回 MAXINT = 2,147,483,647

    /**
     * Definition of ArrayReader:
     * 
     * class ArrayReader {
     *      // get the number at index, return -1 if index is less than zero.
     *      public int get(int index);
     * }
     */
    public class Solution {
        /**
         * @param reader: An instance of ArrayReader.
         * @param target: An integer
         * @return : An integer which is the index of the target number
         */
        public int searchBigSortedArray(ArrayReader reader, int target) {
            // write your code here
            int index = 1;
            while (reader.get(index - 1) < target) {
                index = index * 2;
            }
            int start = 0;
            int end = index - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (reader.get(mid) < target) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
            if (reader.get(start) == target) {
                return start;
            }
            if (reader.get(end) == target) {
                return end;
            }
            return -1;
        }
    }
    View Code

    注意:使用倍增思想先找到右边界,先看第一个数是不是target,再看第二个数是不是target,再看第4个数是不是target,......肯定能在2*k内找到右边界。

    int index = 1;
    while (reader.get(index - 1) < target) {
        index = index * 2;
    }
    View Code

    6.find-minimum-in-rotated-sorted-array(寻找旋转排序数组中的最小值)【高频++】

    假设一个旋转排序数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2。你需要找到其中最小的元素。可以假设数组中不存在重复的元素。

    public class Solution {
        /**
         * @param nums: a rotated sorted array
         * @return: the minimum number in the array
         */
        public int findMin(int[] nums) {
            // write your code here
            int start = 0;
            int end = nums.length - 1;
            //首先选择一个target
            int target = nums[end];
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (nums[mid] <= target) {
                    end = mid;
                } else {
                    start = mid;
                }
            }
            if (nums[start] <= target) {
                return nums[start];
            } else {
                return nums[end];
            }
        }
    }
    View Code

    注意:首先要取一个target=last number,然后如果mid > target, start = mid;mid <= target, end = mid;(相等的情况由 0 1 2 3 4 5 6 7进行判断)

    第二境界:
    二分位置 之 Half half ,并无法找到一个条件,形成 OOXX 的模型, 但可以根据判断,保留下有解的那一半或者去掉无解的一半

    7.find-peak- element(寻找峰值)

    给出一个整数数组(size为n),其具有以下特点:相邻位置的数字是不同的,A[0] < A[1] 并且 A[n - 2] > A[n - 1]。假定P是峰值的位置则满足A[P] > A[P-1]A[P] > A[P+1],返回数组中任意一个峰值的位置。数组可能包含多个峰值,只需找到其中的任何一个即可。

    class Solution {
        /**
         * @param A: An integers array.
         * @return: return any of peek positions.
         */
        public int findPeak(int[] A) {
            // write your code here
            int start = 1;
            int end = A.length - 2;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (A[mid] < A[mid - 1]) {
                    end = mid;
                } else if (A[mid] > A[mid - 1]) {
                    start = mid;
                }
            }
            if (A[start] > A[end]) {
                return start;
            } else {
                return end;
            }
        }
    }
    View Code

    注意:只有两种情况:mid < mid - 1 (mid > mid + 1) end = mid或者mid > mid - 1(mid < mid + 1) start = mid。

    8.search-in-rotated-sorted-array(搜索旋转排序数组)【会了这道题,才敢说自己会二分法】

    假设有一个排序的按未知的旋转轴旋转的数组(比如,0 1 2 4 5 6 7 可能成为4 5 6 7 0 1 2)。给定一个目标值进行搜索,如果在数组中找到目标值返回数组中的索引位置,否则返回-1。可以假设数组中不存在重复的元素。

    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) {
            // write your code here
            if (A == null || A.length == 0) {
                return -1;
            }
            int start = 0;
            int 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]) {
                    if (A[start] <= target && target <= A[mid]) {
                        end = mid;
                    } else {
                        start = mid;
                    }
                } else {
                    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;
        }
    }
    View Code

    注意:两次二分法!第一次:start和mid比较,判断是在左上还是右下:A[start] < A[mid]在左上;A[start] > A[mid] 在右下

                             第二次:在有解的那一半中继续二分:左上start<=target<=mid end = mid;否则start = mid;

                                                                             右下mid<=target<=end start = mid;否则end = mid;

    第三境界:

    二分答案,Binary Search on Result,往往没有给你一个数组让你二分,而且题目压根看不出来是个二分法可以做的题,同样是找到满足某个条件的最大或者最小值。

    9.sqrtx(x的平方根)

    实现 int sqrt(int x) 函数,计算并返回 x 的平方根。

    class Solution {
        /**
         * @param x: An integer
         * @return: The sqrt of x
         */
        public int sqrt(int x) {
            // write your code here
            long start = 1;
            long end = x;
            while (start + 1 < end) {
                long mid = start + (end - start) / 2;
                if (mid * mid <= x) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
            if (end * end <= x) {
                return (int) end;
            } else {
                return (int) start;
            }
        }
    }
    View Code

    注意:首先确定答案范围(1~x),然后二分这个范围。start,mid和end声明为long类型,最后要先判断end的平方是否<=X,转为int类型输出。

    10.sqrtx-ii(x的平方根)

    实现double sqrt(double x) 并且x >= 0,计算并返回 x 的平方根。

    public class Solution {
        /**
         * @param x a double
         * @return the square root of x
         */
        public double sqrt(double x) {
            // Write your code here
            double start = 0.0;
            double end = x;
            double eps = 1e-12;
            if (end < 1.0) {
                end = 1.0;
            }
            while (end - start > eps) {
                double mid = start + (end - start) / 2;
                if (mid * mid <= x) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
            if (end * end <= x) {
                return end;
            }
            return start;
        }
    }
    View Code

    注意:答案范围为(0.0~x)。start,mid和end声明为double类型。

    double eps = 1e-12;
    //若end < 1.0,sqrt(end)>end,但右边界设定的是end,就求不出正确结果。所以要把end 设为1.0
    if (end < 1.0) {
        end = 1.0;
    }
    二分浮点数 和二分整数不同
     一般都有一个精度的要求 譬如这题就是要求小数点后八位, 也就是只要我们二分的结果达到了这个精度的要求就可以,所以 需要让 right 和 left 小于一个我们事先设定好的精度值 eps,一般eps的设定1e-8,因为这题的要求是到1e-8,所以我把精度调到了1e-12。最后 选择 left 或 right 作为一个结果即可 
    while (end - start > eps) {... }
    View Code

    11.search-a-2d-matrix(搜索二维矩阵)

    写出一个高效的算法来搜索 m × n矩阵中的值(target)。这个矩阵具有以下特性:每行中的整数从左到右是排序的。每行的第一个数大于上一行的最后一个整数。

    public class Solution {
        /**
         * @param 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) {
            // write your code here
            if (matrix == null || matrix.length == 0) {
                return false;
            }
            if (matrix[0] == null || matrix[0].length == 0) {
                return false;
            }
            int m = matrix.length;
            int n = matrix[0].length;
            int start = 0;
            int end = m * n - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                int number = matrix[mid / n][mid % n];
                if (number == target) {
                    return true;
                } else if (number < target) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
            if (matrix[start / n][start % n] == target) {
                return true;
            }
            if (matrix[end / n][end % n] == target) {
                return true;
            }
            return false;
        }
    }
    View Code

    注意:这个矩阵实质上是一个从小到大的排序数组。矩阵中每个数坐标的求法,m行n列。横坐标(start,mid,end)/n纵坐标(start,mid,end)%n

    由(0,0)0 (0,1)1 (0,2)2 (0,3)3

       (1,0)4 (1,1)5 (1,2)6 (1,3)7

       (2,0)8 (2,1)9  (2,2)10 (2,3)11 可得以上规律。

    12.search-a-2d-matrix-ii(搜索二维矩阵-II)【智力题】

    写出一个高效的算法来搜索m×n矩阵中的值(target),返回这个值出现的次数。这个矩.阵具有以下特性: 每行中的整数从左到右是排序的。每一列的整数从上到下是排序的。在每一行或每一列中没有重复的整数。

    public class Solution {
        /**
         * @param matrix: A list of lists of integers
         * @param: A number you want to search in the matrix
         * @return: An integer indicate the occurrence of target in the given matrix
         */
        public int searchMatrix(int[][] matrix, int target) {
            // write your code here
            if (matrix == null || matrix.length == 0) {
                return 0;
            }
            if (matrix[0] == null || matrix[0].length == 0) {
                return 0;
            }
            int m = matrix.length;
            int n = matrix[0].length;
            int x = m - 1;
            int y = 0;
            int count = 0;
            while (x >= 0 && y < n) {
                if (matrix[x][y] == target) {
                    count++;
                    x--;
                    y++;
                } else if (matrix[x][y] < target) {
                    y++;
                } else {
                    x--;
                }
            }
            return count;
        }
    }
    View Code

    注意:从左下角元素或右上角元素开始分析。以左下角元素为例,如果M[x][y]==target,x--,y++;如果M[x][y]<target,y++;如果M[x][y]>target,x--。

    13.search-for-a-range(搜索区间)

    给定一个包含 n 个整数的排序数组,找出给定目标值 target 的起始和结束位置。如果目标值不在数组中,则返回[-1, -1]

    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) {
            // write your code here
            int[] result = new int[2];
            result[0] = -1;
            result[1] = -1;
            if (A == null || A.length == 0 || target < A[0] || target > A[A.length - 1]) {
                return result;
            }
            int start = 0;
            int end = A.length - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (A[mid] >= target) {
                    end = mid;
                } else {
                    start = mid;
                }
            }
            if (A[start] == target) {
                result[0] = start;
            } else if (A[end] == target) {
                result[0] = end;
            } else {
                return result;
            }
            start = 0;
            end = A.length - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (A[mid] <= target) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
            if (A[end] == target) {
                result[1] = end;
            } else if (A[start] == target) {
                result[1] = start;
            } else {
                return result;
            }
            return result;
        }
    }
    View Code

    注意:两次二分法,第一次找first location,第二次找last location,第二次二分前start和end要赋初值。即使已经判断了target是否小于A[0],大于A[A.length-1]也要考虑target不在目标数组中的情况。例如:A=[1,2,3,5,6,7],target=4。

    14.total-occurrence-of-target(目标出现总和)

    给一个升序的数组,以及一个target,找到它在数组中出现的次数。

    public class Solution {
        /**
         * @param A an integer array sorted in ascending order
         * @param target an integer
         * @return an integer
         */
        public int totalOccurrence(int[] A, int target) {
            // Write your code here
            int[] result = new int[2];
            result[0] = 0;
            result[1] = 0;
            if (A == null || A.length == 0 || target < A[0] || target > A[A.length - 1]) {
                return 0;
            }
            int start = 0;
            int end = A.length - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (A[mid] >= target) {
                    end = mid;
                } else {
                    start = mid;
                }
            }
            if (A[start] == target) {
                result[0] = start;
            } else if (A[end] == target) {
                result[0] = end;
            } else {
                return 0;
            }
            start = 0;
            end = A.length - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (A[mid] <= target) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
            if (A[end] == target) {
                result[1] = end;
            } else if (A[start] == target) {
                result[1] = start;
            } else {
                return 0;
            }
            return result[1] - result[0] + 1;
        }
    }
    View Code

    注意:13题的变形。返回值为result[1] - result[0] + 1。若target不在目标数组中返回0。

    15.maximum-number-in-mountain-sequence(山序列中的最大数)

    给出一个先增加然后减少的n个整数的山序列,找到山顶。

    public class Solution {
        /**
         * @param nums a mountain sequence which increase firstly and then decrease
         * @return then mountain top
         */
        public int mountainSequence(int[] nums) {
            // Write your code here
            int start = 0;
            int end = nums.length - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (nums[mid] < nums[mid + 1]) {
                    start = mid;
                } else if (nums[mid] > nums[mid + 1]) {
                    end = mid;
                }
            }
            if (nums[start] < nums[end]) {
                return nums[end];
            } else {
                return nums[start];
            }
        }
    }
    View Code

    注意:考虑mid <mid + 1 和mid > mid + 1 两种情况,最后取start和end的较大值。

    16.search-insert-position(搜索插入位置)

    给定一个排序数组和一个目标值,如果在数组中找到目标值则返回索引。如果没有,返回到它将会被按顺序插入的位置。你可以假设在数组中无重复元素。

    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) {
            // write your code here
            if (A == null || A.length == 0 || target < A[0]) {
                return 0;
            }
            if (target > A[A.length - 1]) {
                return A.length;
            }
            int start = 0;
            int end = A.length - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (A[mid] == target) {
                    return mid;
                } else if (A[mid] < target) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
            if (A[end] == target) {
                return end;
            } else if (A[start] == target) {
                return start;
            } else {
                return end;
            }
        }
    }
    View Code

    注意:最后的判断条件,如果A[end]==target,返回end;A[start]==target.返回start;否则返回end.

    17.recover-rotated-sorted-array(恢复旋转排序数组)【三步翻转法】是指:[4,5,1,2,3] → [5,4,1,2,3] → [5,4,3,2,1] → [1,2,3,4,5]

    给定一个旋转排序数组,在原地恢复其排序。什么是旋转数组?比如,原始数组为[1,2,3,4], 则其旋转数组可以是[1,2,3,4], [2,3,4,1], [3,4,1,2], [4,1,2,3]

    public class Solution {
        /**
         * @param nums: The rotated sorted array
         * @return: void
         */
        public void recoverRotatedSortedArray(ArrayList<Integer> nums) {
            // write your code
            for (int i = 0; i < nums.size() - 1; i++) {
                if (nums.get(i) > nums.get(i + 1)) {
                    reverse(nums, 0, i);
                    reverse(nums, i + 1, nums.size() - 1);
                    reverse(nums, 0, nums.size() - 1);
                    break;
                }
            }
        }
        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);
            }
        }
    }
    View Code

    18.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;
            }
        }
    }
    View Code

    注意:偏移量offset要对字符串长度取余。依然是三步翻转法。

    19.wood-cut(木材加工)【hard】

    有一些原木,现在想把这些木头切割成一些长度相同的小段木头,需要得到的小段的数目至少为 k。当然,我们希望得到的小段越长越好,你需要计算能够得到的小段木头的最大长度。注意事项:木头长度的单位是厘米。原木的长度都是正整数,我们要求切割得到的小段木头的长度也要求是整数。无法切出要求至少 k 段的,则返回 0 即可。

    public class Solution {
        /**
         *@param L: Given n pieces of wood with length L[i]
         *@param k: An integer
         *return: The maximum length of the small pieces.
         */
        public int woodCut(int[] L, int k) {
            // write your code here
            if (L == null || L.length == 0) {
                return 0;
            }
            int max = Integer.MIN_VALUE;
            for (int i = 0; i < L.length; i++) {
                if (L[i] > max) {
                    max = L[i];
                }
            }
            int start = 1;
            int end = max;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (count(L, mid) < k) {
                    end = mid;
                } else {
                    start = mid;
                }
            }
            if (count(L, end) >= k) {
                return end;
            }
            if (count(L, start) >= k) {
                return start;
            }
            return 0;
        }
        private int count(int[] L, int len) {
            int num = 0;
            for (int i = 0; i < L.length; i++) {
                num += L[i] / len;
            }
            return num;
        }
    }
    View Code

    注意:木材长度的最小值是1(可以切总长度这么多段),最大值是这些木头中最长的那个(只切一段)。count()函数计算能够切割得到的木材段数。

    20.copy-books(书籍复印)【hard】

    给出一个数组A包含n个元素,表示n本书以及各自的页数。现在有个k个人复印书籍,每个人只能复印连续一段编号的书,比如A[1],A[2]由第一个人复印,但是不能A[1],A[3]由第一个人复印,求最少需要的时间复印所有书。返回值是复印的页数。

    public class Solution {
        /**
         * @param pages: an array of integers
         * @param k: an integer
         * @return: an integer
         */
        public int copyBooks(int[] pages, int k) {
            // write your code here
            if (pages == null || pages.length == 0) {
                return 0;
            }
            int max = Integer.MIN_VALUE;
            int sum = 0;
            for (int i = 0; i < pages.length; i++) {
                max = Math.max(max, pages[i]);
                sum += pages[i];
            }
            int start = max;
            int end = sum;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (countCopyiers(pages, mid) > k) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
            if (countCopyiers(pages, start) <= k) {
                return start;
            }
            return end;
        }
        private int countCopyiers(int[] pages, int limit) {
            int copyiers = 1;
            int sum = pages[0];
            for (int i = 1; i < pages.length; i++) {
                if (sum + pages[i] > limit) {
                    copyiers++;
                    sum = 0;
                }
                sum +=page[i];
            }
            return copyiers;
        }
    }
    View Code

    注意:复印页数的最小值是数组中元素的最大值(n个人一起,一人一本),最大值是数组中所有元素和(一个人复印完全部)。countCopiers()函数计算需要多少人能复印完书。

           最后要先判断countCopyiers(pages, start)是否<=k,再判断end。考虑pages={1,2},k=5。start=2,end=3。如果先判断end会得到结果3,而实际上结果2用时更少。

     private int countCopyiers(int[] pages, int limit) {
            int copyiers = 1;
            int sum = pages[0];
            for (int i = 1; i < pages.length; i++) {
                if (sum + pages[i] > limit) {
                    copyiers++;
                    sum = 0;
                }
                sum +=page[i];
            }
            return copyiers;
    }
     pages[0]+pages[1]>limit时只有pages[0]是这个人的工作,pages[1]就是下个人的工作了,所以copyiers++,sum清零。
    View Code

    21.closest-number-in-sorted-array(排序数组中最接近元素)【思路有障碍】

    在一个排好序的数组 A 中找到 i 使得 A[i] 最接近 target。数组中可以有重复的元素,我们可以返回任何具有相同值的索引。

    public class Solution {
        /**
         * @param A an integer array sorted in ascending order
         * @param target an integer
         * @return an integer
         */
        private int firstIndex(int[] A, int target) {
            int start = 0;
            int end = A.length - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (A[mid] == target) {
                   //return mid; 
                   end = mid;
                } else if (A[mid] < target) {
                    start = mid;
                } else {
                    end = mid;
                }
            }
            if (A[start] >= target) {
                return start;
            }
            if (A[end] >= target) {
                return end;
            }
            return A.length;
        }
        public int closestNumber(int[] A, int target) {
            // Write your code here
            if (A == null || A.length == 0) {
                return -1;
            }
            int index = firstIndex(A, target);
            if (index == 0) {
                return 0;
            }
            if (index == A.length) {
                return A.length - 1;
            }
            if (target - A[index - 1] < A[index] - target) {
                return index - 1;
            }
            return index;
        }
    }
    View Code

    注意:先使用二分法找到>=target的第一个位置index, 如果index等于0,返回0;如果index=A.length返回A.length-1;如果target-A[index-1]<A[index]-target返回index-1;否则返回index。

    public class Solution {
        /**
         * @param A an integer array sorted in ascending order
         * @param target an integer
         * @return an integer
         */
        public int closestNumber(int[] A, int target) {
            // Write your code here
            if (A == null || A.length == 0) {
                return -1;
            }
            if (target < A[0]) {
                return 0;
            }
            if (target > A[A.length - 1]) {
                return A.length - 1;
            }
            int start = 0;
            int end = A.length - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (A[mid] == target) {
                    return mid;
                } else if (A[mid] > target) {
                    end = mid;
                } else {
                    start = mid;
                }
            }
            if (target - A[start] < A[end] - target) {
                return start;
            }
            return end;
        }
    }
    View Code

    上述为个人思路,先判断target是否<A[0]或>A[A.length-1],然后二分法找到start和end,如果target-A[start]<A[end]- target,返回start,否则返回end。

    22.k-closest-numbers-in-sorted-array(在排序数组中找最接近的k个数)【思路有障碍】

    给一个目标数 target, 一个非负整数 k, 一个按照升序排列的数组 A。在A中找与target最接近的k个整数。返回这k个数并按照与target的接近程度从小到大排序,如果接近程度相当,那么小的数排在前面。

    public class Solution {
        /**
         * @param A an integer array
         * @param target an integer
         * @param k a non-negative integer
         * @return an integer array
         */
        public int[] kClosestNumbers(int[] A, int target, int k) {
            // Write your code here
            int[] result = new int[k];
            if (A == null || A.length == 0 || k > A.length) {
                return A;
            }
            int index = firstIndex(A, target);
            int left = index - 1;
            int right = index;
            for (int i = 0; i < k; i++) {
                if (left < 0) {
                    result[i] = A[right++];
                } else if (right >= A.length) {
                    result[i] = A[left--];
                } else {
                    if (target - A[left] <= A[right] - target) {
                        result[i] = A[left--];
                    } else {
                        result[i] = A[right++];
                    }
                }
            }
            return result;
        }
        private int firstIndex(int[] A, int target) {
            int start = 0;
            int end = A.length - 1;
            while (start + 1 < end) {
                int mid = start + (end - start) / 2;
                if (A[mid] == target) {
                    end = mid;
                } else if (A[mid] > target) {
                    end = mid;
                } else {
                    start = mid;
                }
            }
            if (A[start] >= target) {
                return start;
            }
            if (A[end] >= target) {
                return end;
            }
            return A.length;
        }
    }
    View Code

    注意:先使用二分法找到>=target的第一个位置index, 然后left=index-1,right=index。在k个循环的过程中,如果left<0,result[i]=A[right++];如果right>=A.length,result[i]=A[left--];如果target-A[left]<=A[right]-target result[i]=A[left--];否则result[i]=A[right++]。

    23.find-minimum-in-rotated-sorted-array-ii(寻找旋转排序数组中的最小值-II)

    假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2)。你需要找到其中最小的元素。数组中可能存在重复的元素

    public class Solution {
        /**
         * @param num: a rotated sorted array
         * @return: the minimum number in the array
         */
        public int findMin(int[] num) {
            // write your code here
            int min = Integer.MAX_VALUE;
            for (int i = 0; i < num.length; i++) {
                min = Math.min(min, num[i]);
            }
            return min;
        }
    }
    View Code

    注意:考点是能不能想得到最坏情况[1,1,1,...,1]里有一个0。时间复杂度o(n)是必须的。一个for循环即可。二分法也可以做。

    24.search-in-rotated-sorted-array-ii(搜索旋转排序数组-II)

    跟进“搜索旋转排序数组”,假如有重复元素。写出一个函数判断给定的目标值是否出现在数组中。

    public class Solution {
        /** 
         * param A : an integer ratated sorted array and duplicates are allowed
         * param target :  an integer to be search
         * return : a boolean 
         */
        public boolean search(int[] A, int target) {
            // write your code here
            for (int i = 0; i < A.length; i++) {
                if (A[i] == target) {
                    return true;
                }
            }
            return false;
        }
    }
    View Code

    注意:考点是能不能想得到最坏情况[1,1,1,...,1]里有一个0。时间复杂度o(n)是必须的。

    25.divide-two-integers(两个整数相除)

    将两个整数相除,要求不使用乘法、除法和 mod 运算符。如果溢出,返回 2147483647 。

    public class Solution {
        /**
         * @param dividend the dividend
         * @param divisor the divisor
         * @return the result
         */
        public int divide(int dividend, int divisor) {
            // Write your code here
            //处理特殊情况
            if (divisor == 0) {
                return dividend >= 0 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
            }
            if (dividend == 0) {
                return 0;
            }
            if (dividend == Integer.MIN_VALUE && divisor == -1) {
                return Integer.MAX_VALUE;
            }
            //判断最终结果是否为负数
            boolean isNegative = (dividend < 0 && divisor > 0)
                               ||(dividend > 0 && divisor < 0);
            //将a与b都转变成正数运算
            long a = Math.abs((long) dividend);
            long b = Math.abs((long) divisor);
            int result = 0;
            while (a >= b){
                int shift = 0;
                while (a >= (b << shift)){
                    shift++;
                }
                //a -= b << (shift - 1);
                //result += 1 << (shift - 1);
                a = a - (b << (shift - 1));
                result = result + (1 << (shift - 1));
            }
            return isNegative ? -result : result;
        }
    }
    View Code

    注意:先处理各类特殊情况,然后判断结果的正负性,最后使用位运算求出结果。具体分析过程见笔记。

    26.maximum-average-subarray(最大平均值子数组)

    给出一个整数数组,有正有负。找到这样一个子数组,他的长度大于等于 k,且平均值最大。保证数组的大小 >= k。

    public class Solution {
        /**
         * @param nums an array with positive and negative numbers
         * @param k an integer
         * @return the maximum average
         */
        public double maxAverage(int[] nums, int k) {
            // Write your code here
            double l = Integer.MAX_VALUE;
            double r = Integer.MIN_VALUE;
            for (int i = 0; i < nums.length; i++) {
                if (nums[i] < l) {
                    l = nums[i];
                }
                if (nums[i] > r) {
                    r = nums[i];
                }
            }
            while (r - l >= 1e-6) {
                double mid = l + (r - l) / 2;
                if (check_valid(nums, mid, k)) {
                    l = mid;
                } else {
                    r = mid;
                }
            }
            return l;
        }
        private boolean check_valid(int[] nums, double mid, int k) {
            int n = nums.length;
            double min_pre = 0;
            double[] sum = new double[n + 1];
            sum[0] = 0;
            for (int i = 1; i <= n; i++) {
                sum[i] = sum[i - 1] + nums[i - 1] - mid;
                if (i >= k && sum[i] - min_pre >= 0) {
                    return true;
                }
                if (i >= k) {
                    min_pre = Math.min(min_pre, sum[i + 1 - k]);
                }
            }
            return false;
        }
    }
    View Code

    思路:

    1、一个数组的子数组的最大平均数一定在数组的最大值和最小值之间,所以二分法的第一步限定average位于[min,max]之中。
    2、接下去要做的就是不断的缩小范围,直至max-min足够小(如1e-6),那我们就得到了想要的结果。

    缩小范围的思想如下:

    每一轮设置mid=(min+max)/2,然后将原数组中的每一个数减去这个mid,如果能找到k个相邻数的总和大于0的情况,
    那么说明最终结果一定比这个mid要更大,限定下一轮寻找范围在[mid,max]之中。反之在限定在[min,mid]之中。

    那么在实际算法中我们需要解决的关键一步就是,如何判断“有k个相邻数的总和大于0的情况存在”。

    首先,我们可以用sum数组来存储减掉mid值的原数组的各总和(sum[i]存储num[0]-mid到num[i-1]-mid的总和),
    当sum[i]存储的总和个数超过k时(即i>k),也就是说我们保证了这个子数组的长度达到k后,可以砍掉之前一些拖后腿的数。
    这些拖后腿的数用min_pre来实现的。当之前拖后腿的数值小于min_pre时,更新min_pre=sum[i - k + 1]。
    sum[i]存储的是num[0]~num[i-1]减去mid的总和,而min_pre存储的是num[0]~num[k]减掉mid的总和,这样sum[i]-min_pre得到的是sum[k+1]~sum[i-1],
    它所记录的总和个数也就是到num[i]为止能够找到的最大平均数子数组的长度。

  • 相关阅读:
    ecmall挂件开发实例二(转)
    ecmall 挂件开发实例一
    ecmall公告挂件分析(转)--此挂件写法已有更新的写法。
    centos下svn的主要常用命令(解决商城系统添加的文件无法自动更新至svn服务器)
    lbypmall虚拟主机的设置
    解决EditPlus的默认编码方式有关问题(转)
    php mysql 字符集(三) (转)
    php mysql apache字符集(二) (转)
    php mysql apache 的字符集
    Android学习笔记——文件路径(/mnt/sdcard/...)、Uri(content://media/external/...)学习
  • 原文地址:https://www.cnblogs.com/struggleli/p/6756510.html
Copyright © 2020-2023  润新知