二分查找
二分查找 - LeetBook - 力扣(LeetCode)全球极客挚爱的技术成长平台
二分查找
解法1
二分查找
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
}
x的平方根
解法1
呃呃呃
class Solution {
public int mySqrt(int x) {
return (int)Math.sqrt(x);
}
}
解法2
二分查找,很简单就不写了
class Solution {
public int mySqrt(int x) {
int left = 0;
int right = x;
while (left <= right) {
int mid = left + (right - left) / 2;
long pow = (long) mid * mid;
if (pow == x) {
return mid;
} else if (pow > x) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return right;
}
}
猜数字大小
解法1
二分查找
/**
* Forward declaration of guess API.
* @param num your guess
* @return -1 if num is higher than the picked number
* 1 if num is lower than the picked number
* otherwise return 0
* int guess(int num);
*/
public class Solution extends GuessGame {
public int guessNumber(int n) {
int left = 0;
int right = n;
while (left <= right) {
int mid = left + (right - left) / 2;
if (guess(mid) == 0) {
return mid;
} else if (guess(mid) == 1) {
left = mid + 1;
} else {
right = mid -1;
}
}
return 1;
}
}
搜索旋转排序数组
解法1
找断点+二分查找
class Solution {
public int search(int[] nums, int target) {
if (nums.length == 1) {
if (nums[0] == target) {
return 0;
} else {
return -1;
}
}
int left = 0;
int right = nums.length - 1;
if (nums[left] == target) {
return left;
}
if (nums[right] == target) {
return right;
}
if (nums[right] < nums[left]){
for (int i = 0; i < nums.length; i++) {
if (nums[i + 1] < nums[i]) {
if (target > nums[0]) {
right = i;
break;
} else {
left = i + 1;
break;
}
}
}
}
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
return mid;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return -1;
}
}
第一个错误版本
解法1
二分查找
/* The isBadVersion API is defined in the parent class VersionControl.
boolean isBadVersion(int version); */
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left = 1;
int right = n;
while (left <= right) {
int mid = left + (right - left) / 2;
if (!isBadVersion(mid - 1) && isBadVersion(mid)) {
return mid;
} else if (isBadVersion(mid)) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return 0;
}
}
解法2
解法1的基础优化
- 如果求第一个错误版本,那么取到正确版本就给left赋值mid + 1,循环结束后left即为第一个错误版本
/* The isBadVersion API is defined in the parent class VersionControl.
boolean isBadVersion(int version); */
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left = 1;
int right = n;
int mid = 0;
while (left < right) {
mid = left + (right - left) / 2;
if (isBadVersion(mid)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
寻找峰值
解法1
暴力循环
class Solution {
public int findPeakElement(int[] nums) {
for (int i = 1; i < nums.length - 1 ;i++) {
if (nums[i - 1] < nums[i] && nums[i + 1] < nums[i]) {
return i;
}
}
if (nums[0] > nums[nums.length - 1]) {
return 0;
}
return nums.length - 1;
}
}
解法2
二分查找
class Solution {
public int findPeakElement(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid + 1] < nums[mid]) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
寻找旋转排序数组中的最小值
解法1
- 假设数组第一个值为最小值
- 进行一次循环,如果碰到某个值k小于第一个值的,直接返回k
- 否则返回数组第一个值
class Solution {
public int findMin(int[] nums) {
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[0]) {
return nums[i];
}
}
return nums[0];
}
}
在排序数组中查找元素的第一个和最后一个位置
解法1
二分查找
- 碰到相等的元素,把其确定为中点,向两侧遍历寻找临界点并返回
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] result = {-1, -1};
if (nums.length == 0) {
return result;
}
if (nums.length == 1 && nums[0] == target) {
result[0] = 0;
result[1] = 0;
return result;
}
int left = 0;
int right = nums.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) {
left = mid;
right = mid;
while (left != 0 && nums[left - 1] == target) {
left--;
}
while (right != nums.length - 1 && nums[right + 1] == target) {
right++;
}
result[0] = left;
result[1] = right;
break;
} else if (nums[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return result;
}
}
找到K个最接近的元素
解法1
官解思路
找到 K 个最接近的元素 - 找到 K 个最接近的元素 - 力扣(LeetCode)
class Solution {
public List<Integer> findClosestElements(int[] arr, int k, int x) {
int left = 0;
int right = arr.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (arr[mid] >= x) {
right = mid;
} else {
left = mid + 1;
}
}
right = left;
left = right - 1;
while (k > 0) {
if (left < 0) {
right++;
} else if (right >= arr.length) {
left--;
} else if (x - arr[left] <= arr[right] - x) {
left--;
} else {
right++;
}
k--;
}
List<Integer> result = new ArrayList<>();
for (int i = left + 1; i < right; i++) {
result.add(arr[i]);
}
return result;
}
}
寻找峰值
解法1
暴力循环
class Solution {
public int findPeakElement(int[] nums) {
for (int i = 1; i < nums.length - 1 ;i++) {
if (nums[i - 1] < nums[i] && nums[i + 1] < nums[i]) {
return i;
}
}
if (nums[0] > nums[nums.length - 1]) {
return 0;
}
return nums.length - 1;
}
}
解法2
二分查找
class Solution {
public int findPeakElement(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid + 1] < nums[mid]) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
}
Pow(x, n)
解法1
hhhhhh,写着玩
class Solution {
public double myPow(double x, int n) {
return Math.pow(x, n);
}
}
解法2
递归乘法
class Solution {
public double myPow(double x, int n) {
if (n == 0) {
return 1;
}
int N = n > 0 ? n : 0 - n;
double value = dododo(x, N);
return n > 0 ? value : 1 / value;
}
public double dododo(double x, int n) {
if (n == 0) {
return 1;
}
double value = dododo(x, n / 2);
if (n % 2 == 0) {
return value * value;
}
return value * value * x;
}
}
解法3
- 折半计算,每次把n缩小一半,奇数少乘一次x
class Solution {
public double myPow(double x, int n) {
if (n == 0 || x == 1) {
return 1;
}
if (n < 0) {
x = 1 / x;
n = -1 * n;
}
double result = 1;
for (int i = n; i != 0; i /= 2) {
if (i % 2 != 0) {
result *= x;
}
x *= x;
}
return result;
}
}
有效的完全平方数
解法1
写着玩,hhhh
class Solution {
public boolean isPerfectSquare(int num) {
int result = (int)Math.sqrt(num) * (int)Math.sqrt(num);
return num == result;
}
}
解法2
二分查找
class Solution {
public boolean isPerfectSquare(int num) {
int left = 1;
int right = num;
while (left < right) {
long mid = left + (right - left + 1) / 2;
if (mid * mid == num) {
return true;
}
if (mid * mid <= num) {
left = (int) mid;
} else {
right = (int) mid - 1;
}
}
return left * left == num;
}
}
寻找比目标字母大的最小字母
解法1
暴力循环
class Solution {
public char nextGreatestLetter(char[] letters, char target) {
for (int i = 0; i < letters.length; i++) {
if (letters[i] > target) {
return letters[i];
}
}
return letters[0];
}
}
解法2
二分查找
class Solution {
public char nextGreatestLetter(char[] letters, char target) {
if (target >= letters[letters.length - 1]) {
return letters[0];
}
int left = 0;
int right = letters.length - 1;
int mid = 0;
char result = ' ';
while (left <= right) {
mid = left + (right - left) / 2;
if (letters[mid] > target) {
result = letters[mid];
right = mid - 1;
} else {
left = mid + 1;
}
}
return result;
}
}
寻找旋转排序数组中的最小值
解法1
- 假设数组第一个值为最小值
- 进行一次循环,如果碰到某个值k小于第一个值的,直接返回k
- 否则返回数组第一个值
class Solution {
public int findMin(int[] nums) {
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[0]) {
return nums[i];
}
}
return nums[0];
}
}
寻找旋转排序数组中的最小值Ⅱ
解法1
- sort排序
- 返回0号元素
class Solution {
public int findMin(int[] nums) {
Arrays.sort(nums);
return nums[0];
}
}
解法2
二分查找
class Solution {
public int findMin(int[] nums) {
int left = 0;
int right = nums.length - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] == nums[right]) {
right--;
} else if (nums[mid] < nums[right]) {
right = mid;
} else {
left = mid + 1;
}
}
return nums[left];
}
}
解法3
复用寻找旋转排序数组中的最小值的解法(上一题)
class Solution {
public int findMin(int[] nums) {
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[0]) {
return nums[i];
}
}
return nums[0];
}
}
两个数组的交集
解法1
- new两个hashset,第一个hashset存放第一个数组
- 第二个数组存放进第二个hashset,放入之前判断是否在第一个hashset中存在(hashset不可重复)
- 将第二个hashset放入数组返回
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> set1 = new HashSet<>();
Set<Integer> set2 = new HashSet<>();
for (Integer number : nums1) {
set1.add(number);
}
for (Integer number : nums2) {
if (set1.contains(number)) {
set2.add(number);
}
}
int[] result = new int[set2.size()];
int count = 0;
for (Integer number : set2) {
result[count] = number;
count++;
}
return result;
}
}
两个数组的交集Ⅱ
解法1
暴力循环
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
int index = 0;
for (int i = 0; i < nums1.length; i++) {
for (int j = 0; j < nums2.length; j++){
if (nums1[i] == nums2[j]) {
nums1[index] = nums1[i];
nums2[j] = -1;
index++;
break;
}
}
}
int[] result = new int[index];
for (int i = 0; i < index; i++) {
result[i] = nums1[i];
}
return result;
}
}
解法2
- 新建一个数组
- 遍历list1,对于list1中的每个元素,都在新数组中对应下标自增1
- 新建一个result数组用于存放结果
- 遍历list2,对于list2中每个元素,对应新数组中的下标不为0的元素存放入result数组,并把新数组中元素自减1
- 用Arrays的copyOfRange方法返回结果效率较高
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
int[] account = new int[1001];
for (int i = 0; i < nums1.length; i++) {
account[nums1[i]]++;
}
int[] result = new int[Math.min(nums1.length, nums2.length)];
int index = 0;
for (int i = 0; i < nums2.length; i++) {
int val = nums2[i];
if(account[val] > 0) {
account[val]--;
result[index] = val;
index++;
}
}
return Arrays.copyOfRange(result, 0, index);
}
}
两数之和Ⅱ-输入有序数组
解法1
暴力循环
- 遍历整个数组
- 如果两个数相加等于target,返回下标
class Solution {
public int[] twoSum(int[] numbers, int target) {
int[] result = new int[2];
for (int i = 0; i < numbers.length; i++) {
for (int j = i + 1; j < numbers.length; j++) {
if (numbers[i] + numbers[j] == target) {
result[0] = i + 1;
result[1] = j + 1;
return result;
}
}
}
return result;
}
}
解法2
双指针
- 由于数组是从小到大排列的
- 一个指针指向数组头,零一个指针指向数组末尾,进行循环判断,两个指针指向的数的和是否等于target
- 如果相等,则返回下标
- 如果大于target,则后面的指针-1
- 如果小于target,则前面的指针+1
class Solution {
public int[] twoSum(int[] numbers, int target) {
int i = 0;
int j = numbers.length - 1;
int[] result = new int[2];
while (i < j) {
if (numbers[i] + numbers[j] == target) {
result[0] = i + 1;
result[1] = j + 1;
return result;
} else if (numbers[i] + numbers[j] > target) {
j--;
} else {
i++;
}
}
return result;
}
}
寻找重复数
解法1
- HashSet
- 遍历数组,将所有数放入HashSet
- 如果add返回为false,return这个数
class Solution {
public int findDuplicate(int[] nums) {
Set<Integer> set = new HashSet<>();
for (int i = 0; i < nums.length; i++) {
if (!set.add(nums[i])) {
return nums[i];
}
}
return 0;
}
}
解法2
- 思路同解法1
- 用int数组替换set提高速度
class Solution {
public int findDuplicate(int[] nums) {
int[] acc = new int[nums.length];
for (int i = 0; i < nums.length; i++) {
if (acc[nums[i]] == 1) {
return nums[i];
}
acc[nums[i]]++;
}
return 0;
}
}
解法3
- 环形链表的解法
- 把数组当作链表,找环
- 思路见环形链表Ⅱ(链表)
class Solution {
public int findDuplicate(int[] nums) {
int slow = 0;
int fast = 0;
do {
slow = nums[slow];
fast = nums[nums[fast]];
} while(slow != fast);
slow = 0;
while (slow != fast) {
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
}
寻找两个正序数组的中位数
解法1
- 暴力合并
- 排序
- 取中位数
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int[] num = new int[nums1.length + nums2.length];
for (int i = 0; i < nums1.length; i++) {
num[i] = nums1[i];
}
for (int i = nums1.length; i < nums1.length + nums2.length; i++) {
num[i] = nums2[i - nums1.length];
}
Arrays.sort(num);
if (num.length % 2 == 1) {
return num[num.length / 2];
}
return ((double)num[num.length / 2] + num[num.length / 2 - 1]) / 2;
}
}
解法2
官解
寻找两个有序数组的中位数 - 寻找两个正序数组的中位数 - 力扣(LeetCode)
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
return findMedianSortedArrays(nums2, nums1);
}
int m = nums1.length;
int n = nums2.length;
int left = 0;
int right = nums1.length;
int mid1 = 0;
int mid2 = 0;
while (left <= right) {
int i = (left + right) / 2;
int j = (m + n + 1) / 2 - i;
int nums_i_ = (i == 0 ? Integer.MIN_VALUE : nums1[i - 1]);
int nums_i = (i == m ? Integer.MAX_VALUE : nums1[i]);
int nums_j_ = (j == 0 ? Integer.MIN_VALUE : nums2[j - 1]);
int nums_j = (j == n ? Integer.MAX_VALUE : nums2[j]);
if (nums_i_ <= nums_j) {
mid1 = Math.max(nums_i_, nums_j_);
mid2 = Math.min(nums_i, nums_j);
left = i + 1;
} else {
right = i - 1;
}
}
return (m + n) % 2 == 0 ? (mid1 + mid2) / 2.0 : mid1;
}
}
找出第k小的距离对
解法1
宫水三叶大佬的解答
【宫水三叶の特别篇】二分答案 + 双指针 check 运用题 - 找出第 K 小的数对距离 - 力扣(LeetCode)
class Solution {
public int smallestDistancePair(int[] nums, int k) {
Arrays.sort(nums);
int left = 0;
int right = 1000000;
while (left < right) {
int mid = left + (right - left) / 2;
if (check(nums, mid) >= k) {
right = mid;
} else {
left = mid + 1;
}
}
return right;
}
public int check (int[] nums, int x) {
int n = nums.length;
int result = 0;
for (int i = 0, j = 1; i < n; i++) {
while (j < n && nums[j] - nums[i] <= x) {
j++;
}
result += j - i - 1;
}
return result;
}
}
分割数组的最大值
解法1
官解
分割数组的最大值 - 分割数组的最大值 - 力扣(LeetCode)
class Solution {
public int splitArray(int[] nums, int k) {
int left = 0;
int right = 0;
for (int i = 0; i < nums.length; i++) {
right += nums[i];
if (left < nums[i]) {
left = nums[i];
}
}
while (left < right) {
int mid = left + (right - left) / 2;
if (check(nums, mid, k)) {
right = mid;
} else {
left = mid + 1;
}
}
return left;
}
public boolean check (int[] nums, int x, int m) {
int sum = 0;
int cnt = 1;
for (int i = 0; i < nums.length; i++) {
if (sum + nums[i] > x) {
cnt++;
sum = nums[i];
} else {
sum += nums[i];
}
}
return cnt <= m;
}
}