auto commit

This commit is contained in:
CyC2018
2018-04-24 16:38:06 +08:00
parent aa407d4e11
commit 6e40826c18

View File

@ -54,25 +54,39 @@
## 二分查找
```java
public int search(int key, int[] array) {
int l = 0, h = array.length - 1;
public int binarySearch(int key, int[] nums) {
int l = 0, h = nums.length - 1;
while (l <= h) {
int mid = l + (h - l) / 2;
if (key == array[mid]) return mid;
if (key < array[mid]) h = mid - 1;
if (key == nums[mid]) return mid;
if (key < nums[mid]) h = mid - 1;
else l = mid + 1;
}
return -1;
}
```
实现时需要注意以下细节:
**时间复杂度**
- 在计算 mid 时不能使用 mid = (l + h) / 2 这种方式,因为 l + h 可能会导致加法溢出,应该使用 mid = l + (h - l) / 2。
O(logN)
- 对 h 的赋值和循环条件有关,当循环条件为 l <= h 时h = mid - 1当循环条件为 l < h h = mid。解释如下在循环条件为 l <= h 如果 h = mid会出现循环无法退出的情况例如 l = 1h = 1此时 mid 也等于 1如果此时继续执行 h = mid那么就会无限循环在循环条件为 l < h如果 h = mid - 1会错误跳过查找的数例如对于数组 [1,2,3]要查找 1最开始 l = 0h = 2mid = 1判断 key < arr[mid] 执行 h = mid - 1 = 0此时循环退出直接把查找的数跳过了。
**计算 mid**
- l 的赋值一般都为 l = mid + 1
在计算 mid 时不能使用 mid = (l + h) / 2 这种方式,因为 l + h 可能会导致加法溢出,应该使用 mid = l + (h - l) / 2。
**计算 h**
当循环条件为 l <= h则 h = mid - 1。因为如果 h = mid会出现循环无法退出的情况例如 l = 1h = 1此时 mid 也等于 1如果此时继续执行 h = mid那么就会无限循环。
当循环条件为 l < h h = mid。因为如果 h = mid - 1会错误跳过查找的数例如对于数组 [1,2,3]要查找 1最开始 l = 0h = 2mid = 1判断 key < arr[mid] 执行 h = mid - 1 = 0此时循环退出直接把查找的数跳过了。
**返回值**
在循环条件为 l <= h 的情况下循环退出时 l 总是比 h 1并且 l 是将 key 插入 nums 中的正确位置例如对于 nums = {0,1,2,3}key = 4循环退出时 l = 4 key 插入到 nums 中的第 4 个位置就能保持 nums 有序的特点
在循环条件为 l < h 的情况下循环退出时 l h 相等
如果只是想知道 key 存不存在在循环退出之后可以直接返回 -1 表示 key 不存在于 nums
**求开方**
@ -87,17 +101,19 @@ Output: 2
Explanation: The square root of 8 is 2.82842..., and since we want to return an integer, the decimal part will be truncated.
```
一个数 x 的开方 sqrt 一定在 0 \~ x 之间并且满足 sqrt == x / sqrt 可以利用二分查找在 0 \~ x 之间查找 sqrt
一个数 x 的开方 sqrt 一定在 0 \~ x 之间并且满足 sqrt == x / sqrt可以利用二分查找在 0 \~ x 之间查找 sqrt
对于 x = 8它的开方是 2.82842...最后应该返回 2 而不是 3在循环条件为 l <= h 并且循环退出时h 总是比 l 1也就是说 h = 2l = 3因此最后的返回值应该为 h 而不是 l
```java
public int mySqrt(int x) {
if(x <= 1) return x;
if (x <= 1) return x;
int l = 1, h = x;
while(l <= h){
while (l <= h) {
int mid = l + (h - l) / 2;
int sqrt = x / mid;
if(sqrt == mid) return mid;
else if(sqrt < mid) h = mid - 1;
if (sqrt == mid) return mid;
if (sqrt < mid) h = mid - 1;
else l = mid + 1;
}
return h;
@ -120,23 +136,25 @@ Because the 4th row is incomplete, we return 3.
题目描述 i 行摆 i 统计能够摆的行数
返回 h 而不是 l为摆的硬币最后一行不能算进去
n 个硬币能够摆的行数 row 0 \~ n 之间并且满足 n == row * (row + 1) / 2此可以利用二分查找在 0 \~ n 之间查找 row
对于 n = 8它能摆的行数 row = 3这是因为最后没有摆满的那一行不能算进去因此在循环退出时应该返回 h
```java
public int arrangeCoins(int n) {
int l = 0, h = n;
while(l <= h){
int m = l + (h - l) / 2;
long x = m * (m + 1) / 2;
if(x == n) return m;
else if(x < n) l = m + 1;
else h = m - 1;
while (l <= h) {
int mid = l + (h - l) / 2;
long x = mid * (mid + 1) / 2;
if (x == n) return mid;
else if (x < n) l = mid + 1;
else h = mid - 1;
}
return h;
}
```
可以不用二分查找更直观的解法如下
本题可以不用二分查找更直观的解法如下
```java
public int arrangeCoins(int n) {
@ -149,6 +167,39 @@ public int arrangeCoins(int n) {
}
```
**大于给定元素的最小元素**
[Leetcode : 744. Find Smallest Letter Greater Than Target (Easy)](https://leetcode.com/problems/find-smallest-letter-greater-than-target/description/)
```html
Input:
letters = ["c", "f", "j"]
target = "d"
Output: "f"
Input:
letters = ["c", "f", "j"]
target = "k"
Output: "c"
```
题目描述给定一个有序的字符数组 letters 和一个字符 target要求找出 letters 中大于 target 的最小字符letters 字符数组是循环数组
应该注意最后返回的是 l 位置的字符
```java
public char nextGreatestLetter(char[] letters, char target) {
int n = letters.length;
int l = 0, h = n - 1;
while (l <= h) {
int m = l + (h - l) / 2;
if (letters[m] <= target) l = m + 1;
else h = m - 1;
}
return l < n ? letters[l] : letters[0];
}
```
**有序数组的 Single Element**
[Leetcode : 540. Single Element in a Sorted Array (Medium)](https://leetcode.com/problems/single-element-in-a-sorted-array/description/)
@ -158,21 +209,101 @@ Input: [1,1,2,3,3,4,4,8,8]
Output: 2
```
题目描述一个有序数组只有一个数不出现两次找出这个数
题目描述一个有序数组只有一个数不出现两次找出这个数要求以 O(logN) 时间复杂度进行求解
key Single Element 在数组中的位置如果 m 为偶数并且 m < key那么 nums[m] == nums[m + 1]m >= key那么 nums[m] != nums[m + 1]。
从上面的规律可以知道,如果 nums[m] == nums[m + 1],那么 key 所在的数组位置为 m + 2 \~ n - 1此时令 l = m + 2如果 nums[m] != nums[m + 1],那么 key 所在的数组位置为 0 \~ m此时令 h = m。
因为 h 的赋值表达式为 h = m那么循环条件也就只能使用 l < h 这种形式
```java
public int singleNonDuplicate(int[] nums) {
int l = 0, h = nums.length - 1;
while(l < h) {
while (l < h) {
int m = l + (h - l) / 2;
if(m % 2 == 1) m--; // 保证 l/h/m 都在偶数位,使得查找区间大小一直都是奇数
if(nums[m] == nums[m + 1]) l = m + 2;
if (m % 2 == 1) m--; // 保证 l/h/m 都在偶数位,使得查找区间大小一直都是奇数
if (nums[m] == nums[m + 1]) l = m + 2;
else h = m;
}
return nums[l];
}
```
**第一个错误的版本**
[Leetcode : 278. First Bad Version (Easy)](https://leetcode.com/problems/first-bad-version/description/)
题目描述给定一个元素 n 代表有 [1, 2, ..., n] 版本可以调用 isBadVersion(int x) 知道某个版本是否错误要求找到第一个错误的版本
如果第 m 个版本出错则表示第一个错误的版本在 1 \~ m 之前 h = m否则第一个错误的版本在 m + 1 \~ n 之间 l = m + 1
因为 h 的赋值表达式为 h = m因此循环条件为 l < h
```java
public int firstBadVersion(int n) {
int l = 1, h = n;
while (l < h) {
int m = l + (h - l) / 2;
if (isBadVersion(m)) h = m;
else l = m + 1;
}
return l;
}
```
**旋转数组的最小数字**
[Leetcode : 153. Find Minimum in Rotated Sorted Array (Medium)](https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/description/)
```html
Input: [3,4,5,1,2],
Output: 1
```
```java
public int findMin(int[] nums) {
int l = 0, h = nums.length;
while (l < h) {
int m = l + (h - l) / 2;
if (nums[m] <= nums[h]) h = m;
else l = m + 1;
}
return nums[l];
}
```
**查找区间**
[Leetcode : 34. Search for a Range (Medium)](https://leetcode.com/problems/search-for-a-range/description/)
```html
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]
```
```java
public int[] searchRange(int[] nums, int target) {
int first = binarySearch(nums, target);
int last = binarySearch(nums, target + 1) - 1;
if (first == nums.length || nums[first] != target) return new int[]{-1, -1};
return new int[]{first, Math.max(first, last)};
}
private int binarySearch(int[] nums, int target) {
int l = 0, h = nums.length; // 注意 h 的初始值
while (l < h) {
int m = l + (h - l) / 2;
if (nums[m] >= target) h = m;
else l = m + 1;
}
return l;
}
```
## 贪心思想
贪心思想保证每次操作都是局部最优的并且最后得到的结果是全局最优的