初识二分查找及力扣题查找插入位置、出现一次的数字


前言

一、二分查找是什么?

二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。

二、二分查找题目

1.引入第一个例子

在这里插入图片描述
这道题的代码如下(示例):

int search(int* nums, int numsSize, int target) {
	int left = 0; int right = numsSize - 1;//左右两边的下标
	while (left <= right) {             //如果写成while(left<right)则后面需要判断left=right的情况                                          
		int mid = left + (right - left) / 2;//mid也可以写成left+((right-left)>>1);
		if (nums[mid] > target) {            //中间数大于找的值说明在左边,这题数组是有序的
			right = mid - 1;                
		}
		else if (nums[mid] < target) {       //中间数小于找的值说明在左边
			left = mid + 1;
		}                      
		else {                               //中间数等于找的值
			return mid;
		}
	}
	return -1;//整个while里都没找到则返回-1,说明不存在
	
}

如果不限制参数可以用递归方法

int search(int* nums, int left, int right, int target) {
	while (left <= right) {
		int mid = left + (right - left )/ 2;
		if (nums[mid] > target) {
			return search(nums,left,mid-1,target);
		}
		else if (nums[mid] < target) {
			return search(nums, mid + 1, right, target);
		}
		else {
			return mid;
		}
	}
	return -1;
}
int main() {
	int arr[] = { 0,3,5,7,9,12 };
	int length = sizeof(arr) / sizeof(arr[0]);
	int target = 9;
	int rs = search(arr,0,length-1,target);
	if (rs != -1) {
		printf("%d出现在nums中并且下标为%d", target, rs);
	}
	else {
		printf("%d不存在nums中因此返回-1", target);
	}

	return 0;
}

二分查找要求数列本身有序,所以在选择的时候需要确认数列是否本身有序,如果无序,则还需要进行排序,为什么用二分查找?举个例子查字典我们都会先从中间找起,这样效率最高,我们只需判断要找的字是在前面还是后面,那找要找的数也是这个道理。

2.小试牛刀(查找插入位置)

在这里插入图片描述

int searchInsert(int* nums, int numsSize, int target) {
	int left = 0; int right = numsSize - 1;
	while (left <= right) {                                                 
		int mid = left + (right - left) / 2;
		if (nums[mid] > target) {            
			right = mid - 1;
		}
		else if (nums[mid] < target) {       
			left = mid + 1;
		}
		else {                               
			return mid;
		}
	}
	if (nums[0] > target) {
		return 0;
	}
	else if (nums[numsSize - 1] < target) {
		return numsSize;
	}
	else {
		for (int i = 0; i < numsSize; i++) {
			if (nums[i]<target && nums[i + 1]>target) {
				return i + 1;
			}
		}
	}
	return 0;
}

这是我的写法,看了leetcode上官方及其他人的思路,我这种写法非常的挫,我看了他们
的解法觉得很妙但不完全讲的清楚,大家可以自己去看看->leetcode查找插入位置题目

3.试试水平(出现一次的数)

在这里插入图片描述

拿到这个题,如果看了我第一篇博客 或者做过寻找落单的数,消失的数等类似题目,应该会想到异或的方法,但这道题最后进阶要求时间复杂度为O(logn),显然异或时间复杂度不符合,这种题乍一看挺没思路的,对于我这样的新手很不友好。那我们看到logn得先想到的二分查找,如果不行那另寻他法。
思考:落单的数有什么特点?
发现这题和下标的奇偶有关,且要找的答案下标一定为奇数,因为其他数是成双出现的。成双出现意味着没有出现落单数时第一个为奇,第二个为偶,出现落单的数会改变奇偶,则第一个为偶第二个为奇数。

int singleNonDuplicate(int* nums, int numsSize) {
	int left = 0; int right = numsSize - 1;
	while (left < right) {
		int mid = left + (right - left)/ 2;
		if (mid%2 == 0) {                            //偶数情况
			if (nums[mid] == nums[mid + 1]) {        //和下一个数比较判断落单数在左还是右
				left = mid + 1;                      //相等说明在右边
			}
			else  {                                 
				right = mid;
			}
		}
		else {                                          //奇数情况
			if (nums[mid] == nums[mid - 1]) {          //和前面比较 相等说明在右边
				left = mid + 1;                        
			}
			else {                                    //不相等说明在左边
				right = mid-1;
			}
		}
	}
	return nums[left];
}
int main(){
	int arr[] = { 1,1,2,3,3,4,4,8,8 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	int rs = singleNonDuplicate(arr, sz);
	printf("%d", rs);
	return 0;
}

在这里插入图片描述
着重说一下偶数情况的else情况,偶数有可能是要找的答案,所以还要分情况考虑因为可能和前面后面都不相等,那就不用二分下去返回答案,这里我一开始写了个嵌套if语句,且while(left<=rigth)是这样,不过在力扣上报了无返回值错,所以想了其他办法,改了while(left<right),当left=right返回出去再改else这里为right=mid-1;发现是错的,因为mid就是结果的话减一就会错过,所以我们不减一,这样去二分,最后当left=right就是要找的落单数。

我能力有限暂且就用了这种,力扣上有很多更优的解法大家可以去看看->leetcode查找出现一次的数

总结

初识二分查找就到这里了,二分查找的题目有很多,我还会继续去识。本次文章有什么错误和不足之处及时提出哈,谢谢大家!

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值