摘要
300. 最长递增子序列
287. 寻找重复数
class Solution {
public int findDuplicate(int[] nums) {
int slow = 0, fast = 0;
//第一次想遇的时候
do {
slow = nums[slow];
fast = nums[nums[fast]];
} while (slow != fast);
fast = 0;
while (slow != fast) {
slow = nums[slow];
fast = nums[fast];
}
// 快慢指针想遇
return fast;
}
}
240. 搜索二维矩阵 II
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int len=matrix.length;
int row=matrix[0].length;
int x=0,y=row-1;
while(x<len&&y>=0){
if(matrix[x][y]==target){
return true;
}
if(matrix[x][y]<target){
++x;
}else{
--y;
}
}
return false;
}
}
34. 在排序数组中查找元素的第一个
二分查找中,寻找 leftIdx即为在数组中寻找第一个大于等于target的下标,寻找 rightIdx}即为在数组中寻找第一个大于target的下标,然后将下标减一。
两者的判断条件不同,为了代码的复用,我们定义 binarySearch(nums, target, lower) 表示在 nums数组中二分查找 target的位置,如果 lower 为 true,则查找第一个大于等于target 的下标,否则查找第一个大于target的下标。
class Solution {
public int[] searchRange(int[] nums, int target) {
// 左边的最开始的位置
int leftIdx = binarySearch(nums, target, true);
// 右边最开始的位置
int rightIdx = binarySearch(nums, target, false) - 1;
if (leftIdx <= rightIdx && rightIdx < nums.length && nums[leftIdx] == target && nums[rightIdx] == target) {
return new int[]{leftIdx, rightIdx};
}
return new int[]{-1, -1};
}
private int binarySearch(int[] nums, int target, boolean lower) {
int left = 0, right = nums.length - 1, ans = nums.length;
while (left <= right) {
int mid = (left + right) >> 1;
// 通过的lower的真假来控制的 具体的nums[mid] > taget 还是的nums[mid]< target
if (nums[mid] > target || (lower && nums[mid] >= target)) {
right = mid - 1;
ans = mid;
} else {
left = mid + 1;
}
}
return ans;
}
}
33. 搜索旋转排序数组
对于有序数组,可以使用二分查找的方法查找元素。但是这道题中,数组本身不是有序的,进行旋转后只保证了数组的局部是有序的,这还能进行二分查找吗?答案是可以的。
可以发现的是,我们将数组从中间分开成左右两部分的时候,一定有一部分的数组是有序的。拿示例来看,我们从 6 这个位置分开以后数组变成了 [4, 5, 6] 和 [7, 0, 1, 2] 两个部分,其中左边 [4, 5, 6] 这个部分的数组是有序的,其他也是如此。
这启示我们可以在常规二分查找的时候查看当前 mid 为分割位置分割出来的两个部分 [l, mid] 和 [mid + 1, r] 哪个部分是有序的,并根据有序的那个部分确定我们该如何改变二分查找的上下界,因为我们能够根据有序的那部分判断出 target 在不在这个部分:
如果 [l, mid - 1] 是有序数组,且 target 的大小满足 [nums[l],nums[mid]),则我们应该将搜索范围缩小至 [l, mid - 1],否则在 [mid + 1, r] 中寻找。
如果 [mid, r] 是有序数组,且 target 的大小满足 (nums[mid+1],nums[r]],则我们应该将搜索范围缩小至 [mid + 1, r],否则在 [l, mid - 1] 中寻找。
package 二分法;
import org.junit.Test;
public class search33 {
/**
* 这也是一个变形的二分法
* 利用的暴力的算法是的遍历
* <p>
* 通过的判断 左边断电和右端点的来比较
*
* @param nums
* @param target
* @return
*/
public int search(int[] nums, int target) {
int n = nums.length;
// 当没有数据的时候
if (n == 0) {
return -1;
}
// 当只有一个数据的时候
if (n == 1) {
return nums[0] == target ? 0 : -1;
}
int l = 0, r = n - 1;
// 二分法
while (l <= r) {
int mid = (l + r) >> 1;
if (nums[mid] == target) {
return mid;
}
// 判断左边和中间的值
if (nums[0] <= nums[mid]) {
// 表示的是有序的
if (nums[0] <= target && target < nums[mid]) {
// 判断值时候在左边的数组里面就可以进行缩减
r = mid - 1;
} else {
l = mid + 1;
}
} else {
// 右边是有序的
if (nums[mid] < target && target <= nums[n - 1]) {
l = mid + 1;
} else {
r = mid - 1;
}
}
}
return -1;
}
@Test
public void test() {
int search = search(new int[]{4, 5, 6, 7, 0, 1, 2}, 5);
System.out.println(search);
}
}
寻找两个正序数组的中位数
采用的合并数组的方式的(双指针的方式来是实现)
class Solution {
public double findMedianSortedArrays(int[] nums1, int[] nums2) {
int[] array = new int[nums1.length + nums2.length];
int index = 0;
int left = 0;
int right = 0;
while (left < nums1.length && right < nums2.length) {
if (nums1[left] >= nums2[right]) {
array[index++] = nums2[right++];
} else {
array[index++] = nums1[left++];
}
}
while (left < nums1.length) {
array[index++] = nums1[left++];
}
while (right < nums2.length) {
array[index++] = nums2[right++];
}
if (array.length % 2 == 0) {
return ((array[array.length / 2] + array[array.length / 2 - 1] )*1.0)/2;
} else {
return (array[array.length / 2]);
}
}
}