算法训练营——day1数组二分查找

2024-09-01 22:28

本文主要是介绍算法训练营——day1数组二分查找,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

数组是存放在连续空间上的相同数据类型的集合。

注意:下标从0开始;内存空间连续。

正因为数组的内存地址空间连续,所以在删除、添加元素的时候需要移动其他元素。

数组的元素不能删除,只能覆盖!

二维数组特殊

在C++中,二位数组在内存中也是连续的,相当于多个一维数组。

void test_arr() {int array[2][3] = {{0, 1, 2},{3, 4, 5}};cout << &array[0][0] << " " << &array[0][1] << " " << &array[0][2] << endl;cout << &array[1][0] << " " << &array[1][1] << " " << &array[1][2] << endl;
}int main() {test_arr();
}Result:
0x7ffee4065820 0x7ffee4065824 0x7ffee4065828
0x7ffee406582c 0x7ffee4065830 0x7ffee4065834
由于是int类型,所有每个之间差4字节

而在Java中,由于是由JVM处理,所以毫无规则可言。

public static void test_arr() {int[][] arr = {{1, 2, 3}, {3, 4, 5}, {6, 7, 8}, {9,9,9}};System.out.println(arr[0]);System.out.println(arr[1]);System.out.println(arr[2]);System.out.println(arr[3]);
}
Result:
[I@7852e922
[I@4e25154f
[I@70dea4e
[I@5c647e05

练习题

1. 二分查找-力扣704(简单)

1.1 题目704. 二分查找

1.2 全闭区间写法

我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right] 

因为定义target在[left, right]区间,所以有如下两点:

  • while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
  • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
  • 时间复杂度:O(log n)
  • 空间复杂度:O(1)
//Java代码:
class Solution {public int search(int[] nums, int target) {
// 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算if(target<nums[0]||target>nums[nums.length-1]){return -1;}int left=0;int right=nums.length-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;}}return -1;}
}

1.3 左闭右开

如果说定义 target 是在一个在左闭右开的区间里,也就是[left, right) 

有如下两点:

  • while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
  • if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
//左闭右开
class Solution {public int search(int[] nums, int target) {int left=0;int right=nums.length;while(left<right){int mid = left+(right-left)/2;if(nums[mid]>target){right=mid;}else if(nums[mid]<target){left=mid+1;}else{return mid;}}return -1;}
}

2 搜索位置-力扣35(简单)

2.1 题目35:搜索插入位置

2.2 二分解法(Java)

class Solution {public int searchInsert(int[] nums, int target) {//二分法int left = 0;int right = nums.length-1;while(left<=right){int mid = left+(right-left)/2;if(target==nums[mid]){return mid;}else if(target<nums[mid]){right=mid-1;}else if(target>nums[mid]){left=mid+1;}}return right+1;}
}

2.3 C解法

class Solution {// lower_bound 返回最小的满足 nums[i] >= target 的 i// 如果数组为空,或者所有数都 < target,则返回 nums.size()// 要求 nums 是非递减的,即 nums[i] <= nums[i + 1]// 闭区间写法int lower_bound(vector<int>& nums, int target) {int left = 0, right = (int) nums.size() - 1; // 闭区间 [left, right]while (left <= right) { // 区间不为空// 循环不变量:// nums[left-1] < target// nums[right+1] >= targetint mid = left + (right - left) / 2;if (nums[mid] < target) {left = mid + 1; // 范围缩小到 [mid+1, right]} else {right = mid - 1; // 范围缩小到 [left, mid-1]}}return left;}// 左闭右开区间写法int lower_bound2(vector<int>& nums, int target) {int left = 0, right = nums.size(); // 左闭右开区间 [left, right)while (left < right) { // 区间不为空// 循环不变量:// nums[left-1] < target// nums[right] >= targetint mid = left + (right - left) / 2;if (nums[mid] < target) {left = mid + 1; // 范围缩小到 [mid+1, right)} else {right = mid; // 范围缩小到 [left, mid)}}return left;}// 开区间写法int lower_bound3(vector<int>& nums, int target) {int left = -1, right = nums.size(); // 开区间 (left, right)while (left + 1 < right) { // 区间不为空// 循环不变量:// nums[left] < target// nums[right] >= targetint mid = left + (right - left) / 2;if (nums[mid] < target) {left = mid; // 范围缩小到 (mid, right)} else {right = mid; // 范围缩小到 (left, mid)}}return right;}public:int searchInsert(vector<int>& nums, int target) {return lower_bound(nums, target); // 选择其中一种写法即可}
};

3 查找元素第一位和最后一位的下标-力扣34(中等)

3.1 题目:34在排序数组中查找元素的第一个和最后一个位置

3.2 解法JAVA

寻找target在数组里的左右边界,有如下三种情况:

  • 情况一:target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1}
  • 情况二:target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}
  • 情况三:target 在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}
//JAVA版本
class Solution {int[] searchRange(int[] nums, int target) {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};// 情况二:在数组范围内但是没有这个数字return new int[]{-1, -1};}int getRightBorder(int[] nums, int target) {int left = 0;int right = nums.length - 1;int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况while (left <= right) {int middle = left + ((right - left) / 2);if (nums[middle] > target) {right = middle - 1;} else { // 寻找右边界,nums[middle] == target的时候更新leftleft = middle + 1;rightBorder = left;}}return rightBorder;}int getLeftBorder(int[] nums, int target) {int left = 0;int right = nums.length - 1;int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况while (left <= right) {int middle = left + ((right - left) / 2);if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新rightright = middle - 1;leftBorder = right;} else {left = middle + 1;}}return leftBorder;}
}

3.2 解法C

//C嘎嘎版本
class Solution {
public:vector<int> searchRange(vector<int>& nums, int target) {int leftBorder = getLeftBorder(nums, target);int rightBorder = getRightBorder(nums, target);// 情况一if (leftBorder == -2 || rightBorder == -2) return {-1, -1};// 情况三if (rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};// 情况二return {-1, -1};}
private:int getRightBorder(vector<int>& nums, int target) {int left = 0;int right = nums.size() - 1;int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况while (left <= right) {int middle = left + ((right - left) / 2);if (nums[middle] > target) {right = middle - 1;} else { // 寻找右边界,nums[middle] == target的时候更新leftleft = middle + 1;rightBorder = left;}}return rightBorder;}int getLeftBorder(vector<int>& nums, int target) {int left = 0;int right = nums.size() - 1;int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况while (left <= right) {int middle = left + ((right - left) / 2);if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新rightright = middle - 1;leftBorder = right;} else {left = middle + 1;}}return leftBorder;}
};

4 平方根-力扣69(简单)

4.1 题目:x 的平方根 

4.2 二分法求解

class Solution {public int mySqrt(int x) {int left=0;int ret=-1;int right=x;while(left<=right){int mid = left+(right-left)/2;if((long)mid*mid<=x){ret = mid;left=mid+1;}else{right=mid-1;}}return ret;}
}

5 有效完全平方数-力扣367(简单)

5.1 题目:367. 有效的完全平方数

5.2 暴力与二分

//暴力解法
class Solution {public boolean isPerfectSquare(int num) {long x=1;long sq=1;while(sq<=num){if(sq==num){return true;}x++;sq=x*x;}return false;}
}
class Solution {public boolean isPerfectSquare(int num) {//二分int left=0;int right=num;while(left<=right){int mid = left+(right-left)/2;long sq = (long) mid*mid;if(sq<num){left=mid+1;}else if(sq>num){right=mid-1;}else {return true;}}return false;}
}

这篇关于算法训练营——day1数组二分查找的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1128214

相关文章

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

C++原地删除有序数组重复项的N种方法

《C++原地删除有序数组重复项的N种方法》给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度,不要使用额外的数组空间,你必须在原地修改输入数组并在使用O(... 目录一、问题二、问题分析三、算法实现四、问题变体:最多保留两次五、分析和代码实现5.1、问题分析5.

Windows系统下如何查找JDK的安装路径

《Windows系统下如何查找JDK的安装路径》:本文主要介绍Windows系统下如何查找JDK的安装路径,文中介绍了三种方法,分别是通过命令行检查、使用verbose选项查找jre目录、以及查看... 目录一、确认是否安装了JDK二、查找路径三、另外一种方式如果很久之前安装了JDK,或者在别人的电脑上,想

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

Java中数组转换为列表的两种实现方式(超简单)

《Java中数组转换为列表的两种实现方式(超简单)》本文介绍了在Java中将数组转换为列表的两种常见方法使用Arrays.asList和Java8的StreamAPI,Arrays.asList方法简... 目录1. 使用Java Collections框架(Arrays.asList)1.1 示例代码1.

golang字符串匹配算法解读

《golang字符串匹配算法解读》文章介绍了字符串匹配算法的原理,特别是Knuth-Morris-Pratt(KMP)算法,该算法通过构建模式串的前缀表来减少匹配时的不必要的字符比较,从而提高效率,在... 目录简介KMP实现代码总结简介字符串匹配算法主要用于在一个较长的文本串中查找一个较短的字符串(称为

C++一个数组赋值给另一个数组方式

《C++一个数组赋值给另一个数组方式》文章介绍了三种在C++中将一个数组赋值给另一个数组的方法:使用循环逐个元素赋值、使用标准库函数std::copy或std::memcpy以及使用标准库容器,每种方... 目录C++一个数组赋值给另一个数组循环遍历赋值使用标准库中的函数 std::copy 或 std::

通俗易懂的Java常见限流算法具体实现

《通俗易懂的Java常见限流算法具体实现》:本文主要介绍Java常见限流算法具体实现的相关资料,包括漏桶算法、令牌桶算法、Nginx限流和Redis+Lua限流的实现原理和具体步骤,并比较了它们的... 目录一、漏桶算法1.漏桶算法的思想和原理2.具体实现二、令牌桶算法1.令牌桶算法流程:2.具体实现2.1

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::