数据理论基础:二分查找
注意点:
1:数组下标都是从0开始的
2:数组内存空间地址是连续的
3:数组元祖不能删,只能覆盖
4:对于二维数组来说,地址是连续的吗?不同编程语言的内存管理是不一样的。
对于JAVA来说,地址空间不是连续的,对于C++来说,地址空间是连续的
题目
1:二分查找
leetcode.cn/problems/bi…
两种情况:左闭右闭,左闭右开 \
情况1:左闭右闭:
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right) //左闭右闭
{
int middle = left + ((right -left) >> 1);
if(target < nums[middle]){
right = middle - 1;
}
else if(target > nums[middle]){
left = middle + 1;
}
else{
return middle;
}
}
return -1;
}
}
情况2:左闭右开
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length;
while(left < right) //左闭右开[left, right) 这里不一样
{
int middle = left + ((right -left) >> 1);
if(target < nums[middle]){
right = middle ; //这里不一样
}
else if(target > nums[middle]){
left = middle + 1;
}
else{
return middle;
}
}
return -1;
}
}
情况3:左开右闭
class Solution {
public int search(int[] nums, int target) {
int left = -1;
int right = nums.length -1;
while(left + 1 < right) //左闭右开[left, right) 这里不一样
{
int middle = left + ((right -left) >> 1);
if(target < nums[middle]){
right = middle -1; //这里不一样
}
else if(target > nums[middle]){
left = middle ;
}
else{
return middle;
}
}
return -1;
}
*这里还有问题,不太理解什么情况*
总结:
1:使用二分查找的前提条件有两个:有序数组 无重复元素
2:注意区间,一般情况下用左闭右闭或者左闭右开
3:时间复杂度:O(logn),空间复杂度O(1)
6月5日打卡完毕
相关题目:\
1:搜索插入位置
首先查看所有情况:
- 目标值在数组所有元素之前
- 目标值等于数组中某一个元素
- 目标值插入数组中的位置
- 目标值在数组所有元素之后
即要解决这几个情况。
每次做算法题应该首先分析会有哪些情况,再根据情况去思考解题!!!
题解:
方法一:暴力法:
class Solution {
public int searchInsert(int[] nums, int target) {
//暴力法:从左往右找,若target小于于于等于则返回下标,若找到末尾还没找到则返回数组长度
int l = nums.length;
for(int i =0;i < l;i++){
if(target <= nums[i]){
return i;
}
}
return l;
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
方法二:二分法(左闭右闭或左闭右开)
//左闭右闭
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length -1 ;
int result = 0;
while(left <= right){
int middle = left + ((right -left) >> 1);
if(target < nums[middle]){
right = middle - 1;
}
else if(target > nums[middle]){
left = middle + 1;
}
else if(target == nums[middle]){
return middle;
}
}
return right + 1;
}
// 1:l = 0 r = 3 mid = 1 r = 1-1 = 0
//2: l = 0 r = 0 mid = 0 num[mid]=1
}
//左闭右开
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0;
int right = nums.length ; //这里有变化
int result = 0;
while(left < right){ //这里有变化
int middle = left + ((right -left) >> 1);
if(target < nums[middle]){
right = middle; //这里有变化
}
else if(target > nums[middle]){
left = middle + 1;
}
else if(target == nums[middle]){
return middle;
}
}
return right; //这里有变化
}
// 1:l = 0 r = 3 mid = 1 r = 1-1 = 0
//2: l = 0 r = 0 mid = 0 num[mid]=1
}
`` 时间复杂度:O(logn),空间复杂度O(1)
2:在排序数组中查找元素的第一个和最后一个位置
class Solution {
public int[] searchRange(int[] nums, int target) {
/*分三种情况:
1:target不在nums的范围中
2:target在nums的范围中,但是并没有在nums中出现
3:target在nums的范围中,并且在nums中出现 */
int leftBorder = getleftBorder(nums,target);
int rightBorder = getRightBorder(nums,target);
if(leftBorder == -2 || rightBorder == -2){
return new int[]{-1,-1};
}
if(rightBorder - leftBorder > 1){
return new int[]{leftBorder + 1, rightBorder - 1};
}
else{
return new int[]{-1,-1};
}
}
//查找右边界,这里移动left向右查找
int getRightBorder(int[] nums, int target){
int left = 0;
int right = nums.length - 1;
int rightBorder = -2;
while(left <= right){
int middle = left + ((right -left) >> 1);
if(target < nums[middle]){
right = middle - 1;
}
else{ //>= 当等于的时候更新border,最后大于的时候再更新一次border,注意这时候border不包含target
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
}
//查找左边界,这里移动right向左查找
int getleftBorder(int[] nums, int target){
int left = 0;
int right = nums.length - 1;
int leftBorder = -2;
while(left <= right){
int middle = left + ((right -left) >> 1);
if(target <= nums[middle]){//<= 当等于的时候更新border,最后小于于的时候再更新一次border,注意这时候border不包含target
right = middle - 1;
leftBorder = right;
}
else{
left = middle + 1;
}
}
return leftBorder;
}
}
本体还有优化空间,二刷的时候再看看
3:X的平方根
class Solution {
//思路和找边界是一样的,即用left去寻找右边界
public int mySqrt(int x) {
int left = 0;
int right = x;
int result = -1;
while(left <= right){
int middle = left + ((right - left) >> 1);
long sq = (long)middle * middle;
if(sq <= x){ //如果sq小于x,则要增大sq
left = middle + 1;
result = middle;
}
else{
right = middle - 1;
}
}
return result;
}
}
注这里需要注意隐式转换问题,已经总结在问题中
juejin.cn/post/737722…
4:有效的完全平方数
class Solution {
public boolean isPerfectSquare(int num) {
int left = 0;
int right = num;
boolean result = false;
while(left <= right){
int middle = left + ((right - left) >> 1);
long sq = (long)middle * middle;
if(sq < num){
left = middle + 1;
}
else if(sq > num){
right = middle - 1;
}
else if(sq == num){
result = true;
break;
}
}
return result;
}
}