从此开始顺着刷完
先给一个二分查找模板,以后都按照此模板来写。
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+")