学习了二分查找的算法思想之后,再leetcode上写了一道常见的简单面试题,现在用博客记录一下我学习刷题的笔记!

leetcode 题号69:Sqrt(x)(经典的面试题)

给你一个非负整数 x ,计算并返回 x 的 算术平方根 。由于返回类型是整数,结果只保留 整数部分 ,小数部分将被 舍去 。注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。

解题思路:

①使用二分查找的主要思想(其实也就是分治的思想啦啦啦~~~),对于非负的整数x,我从low=0,一直遍历到high=x,对于这中间的每一个mid,若mid满足:mid*mid <=x这个条件的话呢,就可以更新ans,因为不要求把精确到小数点后几位的数字都显示出来,其实

本质上,这个mySqrt函数要我做的工作就是:求 不大于 非负整数x的平方根 的int型数字!

解题思路:

②(牛顿迭代法,这种数学方法暂时不学了把,主要还是要考察我二分查找法 怎么弄!)

代码:

class Solution {
public://这个mysqrt函数本质:找 不超过 x的平方根 的最大int型的数字!int mySqrt(int x) {int low = 0,high = x,ans = -1;while(low <= high){int mid = low + (high-low)/2;long long temp = (long long)mid*mid;//注意昂!这里是long long ,就是为了防止int型数字算不出来,累加到超过int范围的数字了//为了防止这种爆范围的操作,就把int改为long long即可!if( temp <= x){ans = mid;low = mid + 1;}else{high = mid-1;}}return ans;}
};

运行结果:

leetcode 题号35:搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(log n) 的算法。

提示:

1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 为无重复元素的升序排列数组

(这一条很明显就是要我使用二分查找法的思想来deal问题啦!)
-104 <= target <= 104

解题思路:

①使用二分查找的主要思想(其实也就是分治的思想啦啦啦~~~),因为这里很明显是用数组vector这种数据结构来存储数据,同时该数组已经是一个有序了的且无重复元素的数组,那必须是考察我二分查找算法的思想啊!(其实也就是双指针思想的变形卡~~~)

代码1:(第一遍时自己写的)

class Solution {
public:int searchInsert(vector<int>& nums, int target) {//所谓的时间复杂度位O(n*logn)的算法不就是要我用二分查找算法嘛!//而且,你想想嘛,这里是用的数组这种数据结构来存储数据//那就必须要用二分查找算法啦!if (nums.size() == 1) {if (nums[0] < target) {return 1;}else {return 0;}}int vecSize = (int)nums.size();int low = 0, high = vecSize - 1;int ans = -1;while (low <= high)//二分查找法{int mid = low + ((high - low) >> 1);if (nums[mid] == target) {ans = mid; break;}else if (nums[mid] > target) {high = mid - 1;}else {low = mid + 1;}}if (ans == -1) {int slow = 0, fast = slow + 1;while (fast < vecSize)//快慢指针法{if (nums[slow] <= target && nums[fast] > target){ans = fast;break;}slow++; fast++;}if (ans == -1) {ans = target > nums[0]? vecSize: 0;}}return ans;}
};

运行结果:

代码2:(看了答案之后简化的,这里直接返回对应的low即可解决all的边界case了!比如找不到元素时所要插入的位置这种case!我多拿几个特例来画图看一看即可写出这样的代码了!只不过这样写代码的话可读性不高!!!)

class Solution {
public:int searchInsert(vector<int>& nums, int target) {int vecSize = (int)nums.size();int low = 0, high = vecSize - 1;while (low <= high){int mid = low + ((high - low) >> 1);if (nums[mid] < target) {low = mid + 1;}else {high = mid - 1;}}return low;}
};

leetcode 题号33:搜索旋转排序数组

整数数组 nums 按升序排列,数组中的值 互不相同 。在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

解题思路:

①使用二分查找的主要思想来do!因为这样传入来的数组nums本身就已经做了一次反转,不知道是否是有序的了(当然,这里的测试案例是有有序的也有无序的!)所以我应该分case讨论!

若仍然不清晰的话,那么请看我画的特例:

相信通过以上的特例你已经明白我的解题思路了,那么下面请看我写的代码吧!

但是这个O(N)的时间复杂度,so不太好。

代码:

class Solution {
public:int search(vector<int>& nums, int target) {//这里所谓的旋转数组其实就是循环数组的意思!不要给什么旋转的字样给骗了!int vecSize = (int)nums.size();int length = 0;bool findSuccess = false;//解题思路://1-先在没有排序的数组中找到这个target,然后计算它离index = 0 的位置的距离length//这时不要用二分找,因为不是有序的arr,用顺序找即可!//2-求出排序后找到target这个元素的新位置newIndex//3-返回 newIndex + lengthfor (int i = 0; i < vecSize;i++) {if (nums[i] == target) {length = i; findSuccess = true;cout << "length = " << length << endl;break;}}if (!findSuccess) {return -1;//提前结束!}int newIndex = 0;stable_sort(nums.begin(), nums.end());//用稳定的排序来防止原来的数组中的相同的元素乱序了!int low = 0, high = vecSize - 1;while (low <= high){int mid = low + ((high - low) >> 1);if (nums[mid] == target) {newIndex = mid; cout << "newIndex = " << newIndex << endl;break;}else if (nums[mid] > target) {high = mid - 1;}else {low = mid + 1;}}//我把all的特例case都列出来然后找这个循环数组下标的差值循环规律弄出来了!return newIndex - (newIndex - length);//不能够简单的做差!}
};

运行结果:

解题思路:

②直接暴力顺序查找法,这太简单了,不用想就可以写出来!但是这个O(N)的时间复杂度,so也不太好。

class Solution {
public://本来这道题应该就是考察二分查找算法思想的//但是出的不好,tmd我直接顺序搜就行了int search(vector<int>& nums, int target) {int vecSize = (int)nums.size();int retIndex = 0;bool findSuccess = false;for (int i = 0; i < vecSize;i++) {if (nums[i] == target) {retIndex = i; findSuccess = true;break;}}if (!findSuccess) {return -1;//提前结束!}return retIndex;}
};

运行结果:

解题思路:

③使用二分查找的主要思想来do!并且要求时间复杂度为O(logn)!!!O(logn)才是最高效的查找算法!

大致思路:因为这是旋转数组,再do旋转之前肯定是有序了的,但是旋转之后可能有序可能无序因此,我们应该判断那一部分是有序的,哪一部分是无序的(当然,但凡你一旦判断出某区间无序,那另外一个区间必然是有序的了)通过举例子你就可以发现,旋转数组中以某个分割点为界所do的旋转操作(可看为2个区间)这2区间必须满足:至少有一个区间是有序的,一定会这样再在所判断出来的有序的区间基础上,判断这样指定的target的值是否属于这一个有序的区间如果属于,那就在这部分进行这轮的2分查找即可,如果不属于,就在另一个区间再do这轮的二分查找即可

代码:

class Solution {
public:int search(vector<int>& nums, int target) {int vecSize = nums.size();if (!vecSize) {//处理空数组的特殊casereturn -1;}if (vecSize == 1) {//处理单元素数组的特殊caseint ret = ((target == nums[0]) ? 0 : -1);return ret;}int left = 0, right = vecSize - 1;while (left <= right){int mid = left + ((right - left) >> 1);if (nums[mid] == target)return mid;//当前的mid不是我所指定的元素!if (nums[0] <= nums[mid]){if (target >= nums[0] && target < nums[mid]){right = mid - 1;//我不属于这部分有序的区间内,就必须在另一半区间去查找}else {left = mid + 1;}}else{if (target > nums[mid] && target <= nums[vecSize - 1]){left = mid + 1;//我不属于这部分有序的区间,就必须在另一半区间去查找}else {right = mid - 1;}}}return -1;}
};

运行结果:

leetcode 题号34:在排序数组中查找元素的第一个以及最后一个位置

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。

进阶:你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?

解题思路:

①使用快慢指针法的主要思想。定义2个变量,一个快fast,一个慢slow,然后让nums[fast]与target逐一地对比,但凡满足 nums[fast] == target 的话,就让slow = fast,并让临时数组ret把这个target元素的第一次出现的位置记录下来,最后但凡再遇到就继续记录slow,然后最后输出时判断是否只有0个or1个or2个or更多个(超过2个时那么target最后一次出现的位置就是ret的size-1位置记录的数字了,其他case则相对较容易判断了)

( 这种方法用了大量的if-else语句)

代码:

class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {//首先,开头就先处理一下数组长度为0or1的case!int vecSize = (int)nums.size();if (vecSize == 0 ) {return { -1,-1 };}if (vecSize == 1) {if(target != nums[0])return { -1,-1 };elsereturn { 0,0 };}//快慢指针法!O(n)的时间复杂度int slow = -1, fast = 0;vector<int> ret;while (fast < vecSize){if (nums[fast] == target){slow = fast;ret.push_back(slow);}fast++;}if (!ret.empty()) {if(ret.size() == 1)return { ret[0],ret[0] };elsereturn { ret[0],ret[(int)ret.size()-1] };}return { -1,-1 };}
};

运行结果:

解题思路:

②使用二分查找的主要思想。所谓的在排序数组中查找元素的第一个以及最后一个位置其实就是在排序数组中使用二分查找法的变体1和变体2来deal,变体1就是在数组中查找值等于给定值的第一次出现的元素,变体2就是在数组中查找值等于给定值的最后一次出现的元素,那么加起来就可以查找到数组中指定元素的第一个以及最后一个出现的位置了!

代码:

class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {int vecSize = (int)nums.size();if (vecSize == 0) {return { -1,-1 };}if (vecSize == 1) {if (target != nums[0])return { -1,-1 };elsereturn { 0,0 };}//二分查找法!//其实这本质上找值等于给定值第一次出现的位置,以及值等于给定值最后一次出现的位置!//本质上就是二分查找算法的第一个变体以及第二个变体问题!int low = 0, high = vecSize - 1;int r1 = -1, r2 = -1;//先查找 值 等于 给定值 的第一次出现的位置!while (low <= high){int mid = low + ((high - low) >> 1);if (nums[mid] < target) {low = mid + 1;}else if(nums[mid] > target){high = mid - 1;}else {if ((mid == 0) ||nums[mid - 1] != target) {r1 = mid; break;}else {high = mid - 1;}}}low = 0, high = vecSize - 1;//先查找 值 等于 给定值 的最后一次出现的位置!while (low <= high){int mid = low + ((high - low) >> 1);if (nums[mid] < target) {low = mid + 1;}else if (nums[mid] > target) {high = mid - 1;}else {if ((mid == vecSize - 1) || nums[mid + 1] != target) {r2 = mid; break;}else {low = mid + 1;}}}return { r1,r2 };}
};

运行结果:

解题思路:

③ 使用STL的库函数中的upper_bound以及lower_bound来对数组do处理即可得出指定的元素的第一个以及最后一个位置。

补充知识:

使用这2个库函数需要包含头文件#include<algorithm>

1---lower_bound(vec.begin(),vec.end(),target);

返回数组vec中的第一个大于等于指定元素target的元素的位置,找不到则返回vec.end();

当然,lower_bound的底层是用二分查找来do的!

2---upper_bound(vec.begin(),vec.end(),target);

返回数组vec中的第一个大于指定元素target的元素的位置,找不到则返回vec.end();

当然,upper_bound的底层也是用二分查找来do的!

STL中二分查找函数的用法-关于lower_bound( )和upper_bound( )的常见用法

代码1.0:

class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {int vecSize = (int)nums.size();if (vecSize == 0 ) {return { -1,-1 };}if (vecSize == 1) {if(target != nums[0])return { -1,-1 };elsereturn { 0,0 };}vector<int>::const_iterator itpos = lower_bound(nums.begin(), nums.end(), target);//用STL中的库函数lower_bound来取得第一个大于等于指定值的值的位置!//当然,lower_bound的底层是用二分查找来do的!if (itpos == nums.end() || *itpos != target) {//找不到这个元素!return { -1,-1 };}vector<int>::iterator itpos2 = upper_bound(nums.begin(), nums.end(), target);//用STL中的库函数upper_bound来取得第一个大于指定值出现的位置!//当然,lower_bound的底层是用二分查找来do的!itpos2--;int r1 = itpos - nums.begin(), r2 = itpos2 - nums.begin();return { r1,r2 };}
};

运行结果:

同解题思路的代码2.0:

class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {auto left = lower_bound(nums.begin(), nums.end(), target);auto right = upper_bound(nums.begin(), nums.end(), target);if (left == right)  return vector<int>{-1, -1};else    return vector<int>{(int)(left - nums.begin()), (int)(right - nums.begin() - 1)};}
};

运行结果:

leetcode中关于使用二分查找算法思想deal的题型相关推荐

  1. Leetcode中几道二分查找(Binary Search)的算法题总结

    二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法.但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列.二分查找法的时间复杂度是对数级别的,O(lo ...

  2. 二分查找算法递归实现

    二分查找(Binary Search),又称折半查找,是一种效率较高的查找算法.折半查找需要线性表采用顺序存储结构,即查找的对象必须是一个有序序列.如{1,2,3,4,5...},{10 ,8,6,4 ...

  3. 二分查找算法(折半查找算法)

    二分查找算法(折半查找算法) 二分查找又称折半查找.二分搜索.折半搜索等,是在分治算法基础上设计出来的查找算法,对应的时间复杂度为O(logn). 二分查找算法仅适用于有序序列,它只能用在升序序列或者 ...

  4. leetcode刷题笔记——二分查找

    leetcode刷题笔记--二分查找 目前完成的贪心相关的leetcode算法题序号: 中等:80,81 困难:4 来源:力扣(LeetCode) 链接:https://leetcode-cn.com ...

  5. LeetCode面试刷题技巧-二分查找算法代码思路解析

    二分查找的思想 提及二分查找算法,我想大部分人都不陌生,就算不是学计算机的,基本上也都使用过二分查找的思想,不信的话,且听我慢慢为你道来. 不知道你有没有玩过这样一个游戏,猜数字.就是说一个人心里想了 ...

  6. 关于二分查找算法中中间值的获取

    关于二分查找算法中中间值的获取 //left是左边元素索引的变量,right是右边元素索引的变量 int mid = (left + right) / 2; //这样写的话可能会出现问题,当数组中的元 ...

  7. 考研数据结构之查找(9.8)——练习题之编写一个函数利用二分查找算法在一个有序表中插入关键字k并保持表的有序性(C表示)

    题目 编写一个函数,利用二分查找算法在一个有序表中插入一个关键字k,并保持表的有序性. 分析 先在有序表中利用二分查找算法查找关键字值等于或小于k的结点,m指向正好等于k的结点或l指向关键字正好大于k ...

  8. SGISTL源码探究-stl_alog.h中的二分查找算法

    前言 在上一小节中我们分析了stl_algo.h中的部分算法.本小节中我们将继续分析其中关于二分查找类的算法,即lower_bound.upper_bound.binary_search.equal_ ...

  9. list 查找_五千字长文带你学习 二分查找算法

    点击上方"与你一起学算法",选择"星标"公众号 重磅干货,第一时间送达 二分查找的思想 提及二分查找算法,我想大部分人都不陌生,就算不是学计算机的,基本上也都使 ...

最新文章

  1. mediawiki常用设置
  2. Oracle Recyclebin
  3. Oracle入门(十四F)之PL/SQL定义变量
  4. 如何导入数据模板到MVC
  5. unity 平移图片_unity实现贴图矩阵运算(旋转平移缩放)
  6. c语言数组用户注册登入管理系统_[内附完整源码和文档] 基于JAVA的干部档案管理系统...
  7. linux libpng dev,linux下实用图形库--libpng下载地址及说明
  8. 在BAE tomcat环境下实现讯飞TTS在线文字转语音
  9. 最最超级无敌的冷笑话,能把人噎死
  10. listbox java_listbox读取数据库
  11. 视频教程-JavaSE基础视频精讲⑰:IO流高级用法-Java
  12. java 运算规则_java四则运算规则
  13. 《童虎学习笔记》20分钟实战ProxySQL MGR高可用及读写分离架构
  14. 选择SAP IBP(集成业务计划云)的十大理由
  15. 2021高考秦安一中成绩查询,2021年天水高考状元是谁分数多少分,历年天水高考状元名单...
  16. IT大侦“碳”:VxRail的可持续法宝
  17. sinh和asinh--双曲正弦和反双曲正弦函数
  18. 做好公司各部门数据报表支撑的几个简单思维
  19. Reactor模式!
  20. JS中怎样使用Date对象加减日期

热门文章

  1. 源码 | Arduino + EMQ X + Spring Boot + Vue 开源全栈物联网智能家居系统
  2. 华为eNSP生成树基础实验配置
  3. 图神经网络基础——(一)图论基础
  4. node.js配置ssl免费证书
  5. 中国移动oneos框架基础及其组件解析
  6. 修理电脑笔记 -- 驱动的问题(如触摸屏 指纹解锁之类的驱动在重装系统之后无法使用)
  7. 《Graph Neural Networks Foundations,Frontiers and Applications》第一部分第一章1.1节翻译和解读
  8. 华为鸿蒙概念机990,华为屏下摄像头概念新机:麒麟990+双模5G,还有望搭载鸿蒙OS系统...
  9. nokia x android 界面,终于踏上Android路!Nokia X试玩
  10. 一种基于多图注意力机制的虚假新闻检测方法