不骗你,没读这一篇,你不可能懂二分,阿里巴巴社招面试p8多久有结果

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

如果 target < nums[pivot],则在左侧继续搜索 right = pivot - 1。

如果 target > nums[pivot],则在右侧继续搜索 left = pivot + 1。

算法实现:照例贴出三种语言的实现,在Java实现中给出了详细注释

class Solution {

public int search(int[] nums, int target) {

//分别准备好左右端点

int left = 0, right = nums.length - 1;

//循环二分

while (left <= right) {

//取中点

int pivot = left + (right - left) / 2;

//找到答案并返回

if (nums[pivot] == target) return pivot;

//向左继续找

if (target < nums[pivot]) right = pivot - 1;

//向右继续找

else left = pivot + 1;

}

//未找到,返回-1

return -1;

}

}

class Solution:

def search(self, nums: List[int], target: int) -> int:

left, right = 0, len(nums) - 1

while left <= right:

pivot = left + (right - left) // 2

if nums[pivot] == target:

return pivot

if target < nums[pivot]:

right = pivot - 1

else:

left = pivot + 1

return -1

class Solution {

public:

int search(vector& nums, int target) {

int pivot, left = 0, right = nums.size() - 1;

while (left <= right) {

pivot = left + (right - left) / 2;

if (nums[pivot] == target) return pivot;

if (target < nums[pivot]) right = pivot - 1;

else left = pivot + 1;

}

return -1;

}

};

请记住这个代码,因为整篇文章,整个二分思想,都和这段代码息息相关,这也是最基础的二分问题。

3.2 基础二分小变形1

============

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

思路:我们注意,这一题和上一题的区别在于,这一题并不存在**‘没找到’这种情况,因为要插入到”第一个比目标值大的数“的左边,返回这样一个插入位置,你不可能”找不到“,**不可返回-1。

事实上,我们的目标也再不是找到一个相同的数字,而是找到第一个比目标值大的数,也就是插入位置。

而我们第一段代码的返回答案也可以省略了,代码的内部逻辑是**”一路找到最后,返回的一定是答案“,而上段代码的逻辑是”边找边判断,找到最后还没有找到,就返回-1“。**

下面是代码:

public class Solution {

public int searchInsert(int[] nums, int target) {

int len = nums.length;

int left = 0;

int right = len - 1;

while (left <= right) {

int mid = (left + right) / 2;

if (nums[mid] < target) {

left = mid + 1;

} else {

right = mid - 1;

}

}

return left;

}

}

提醒:所以,如果你是一个想注重细节,做到写二分代码不出错,那么你就需要关心最后返回值,while结束条件,while内部是否加判断等等这些细节了。

3.3基础二分小变形2

===========

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

你的算法时间复杂度必须是 O(log n) 级别。

如果数组中不存在目标值,返回 [-1, -1]。

示例 1:输入: nums = [5,7,7,8,8,10], target = 8输出: [3,4]

示例 2:输入: nums = [5,7,7,8,8,10], target = 6输出: [-1,-1]

思路:两个二分,稍微改一下,就可以找到第一个target或最后一个target;

本元素和前面一个元素不相等,才代表找到了最左边的目标元素。

本元素和后面一个元素不相等,才代表找到了最右边的目标元素。

class Solution {

public int[] searchRange(int[] nums, int target) {

int[] ans=new int[2];

ans[0]=searchRangeLeft(nums,target);

ans[1]=searchRangeRight(nums,target);

return ans;

}

public int searchRangeLeft(int[] nums, int target) {

int left=0;

int right=nums.length-1;

while(left<=right){

int mid=(left+right)/2;

if(nums[mid]>target){

right=mid-1;

}else if(nums[mid]<target){

left=mid+1;

}else if(mid==0 || nums[mid-1]!=target){

return mid;

}else{

right=mid-1;

}

}

return -1;

}

public int searchRangeRight(int[] nums, int target) {

int left=0;

int right=nums.length-1;

while(left<=right){

int mid=(left+right)/2;

if(nums[mid]>target){

right=mid-1;

}else if(nums[mid]<target){

left=mid+1;

}else if(mid==nums.length-1 || nums[mid+1]!=target){

return mid;

}else{

left=mid+1;

}

}

return -1;

}

}

注意:如果你找到一个target,然后向左向右线性查找是不对的,这样时间会退化为O(N),和二分的本意违背了。

3.4泛化二分的概念

==========

看到第一节,可能大部分人都没有问题;

看到第二节,可能对有些写二分总有bug的同学有一些帮助,会觉得”细节确实需要注意“,二分查找从此要一次就bug free;

至此,我们看一下百度百科对二分的定义:

简单来说,我们介绍的就是在顺序存储结构(数组)有序的情况下进行二分查找。

从第四节开始,我们介绍的就不是传统意义上的二分查找了,不局限于”有序“,甚至不局限于线性结构,while循环里判断向左还是向右搜索的条件也不会这么单一,而更可能是这样:

while (范围没有缩小为0) {

if (满足某种条件) {

排除一半答案

} else {

排除另一半答案

}

}

我们的思想是:只要可以通过正确逻辑,用二分思想正确的缩小查找范围,都称之为二分。

下面就来体会一下什么是二分思想:

我们正在玩一个猜数字游戏。 游戏规则如下:

我从 1 到 n 选择一个数字。 你需要猜我选择了哪个数字。

每次你猜错了,我会告诉你这个数字是大了还是小了。

你调用一个预先定义好的接口 guess(int num),它会返回 3 个可能的结果(-1,1 或 0):

-1 : 我的数字比较小

1 : 我的数字比较大

0 : 恭喜!你猜对了!

示例 :

输入: n = 10, pick = 6输出: 6

/* The guess API is defined in the parent class GuessGame.

@param num, your guess

@return -1 if my number is lower, 1 if my number is higher, otherwise return 0

int guess(int num); */

public class Solution extends GuessGame {

public int guessNumber(int n) {

int low = 1;

int high = n;

while (low <= high) {

int mid = low + (high - low) / 2;

int res = guess(mid);

if (res == 0)

return mid;

else if (res < 0)

high = mid - 1;

else

low = mid + 1;

}

return -1;

}

}

你看,这就是把条件抽象成一个接口,完全脱离了线性数据结构,更和有序无序没关系,只是二分的思想。

3.5在线性结构上二分的题目积累

====================

例1

峰值元素是指其值大于左右相邻值的元素。给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。

数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞。

示例 1:输入: nums = [1,2,3,1]输出: 2

解释: 3 是峰值元素,你的函数应该返回其索引 2。

示例 2:输入: nums = [1,2,1,3,5,6,4]输出: 1 或 5

解释: 你的函数可以返回索引 1,其峰值元素为 2;

或者返回索引 5, 其峰值元素为 6。

说明:你的解法应该是 O(logN) 时间复杂度的

思路:

可以用二分的要求:线性表能够根据中间元素的特点推测它两侧元素的性质,以达到缩减问题规模的效果即可,不一定非要有序

具体到本题来说,我们如何做到搜索到任何一个“峰值”呢?请看图:

要先有上升的趋势,后有下降的趋势。更通俗一点就是说,要保证前一个数字比峰值小,后一个数字比峰值大,我们只要每次搜索都满足这个条件,搜到最后就一定可以找到某个峰值,因为从递增到递减,肯定是因为中间有峰值的存在所导致的

我们看中点,如果中点向右有递增趋势,我们就继续搜索右边:

反之,有向左递增的趋势,我们就搜左边:

下面给出代码:

public class Solution {

public int findPeakElement(int[] nums) {

int l = 0, r = nums.length - 1;

while (l < r) {

int mid = (l + r) / 2;

if (nums[mid] > nums[mid + 1])

r = mid;

else

l = mid + 1;

}

return l;

}

}

我们发现,这个题目的代码逻辑就是第二节的**”一路找到最后,返回的一定是答案“,不同于最基础的二分”边找边判断,找到最后还没有找到,就返回-1“。**我们是缩小范围到最后,找到了答案l。

例2:

假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请找出其中最小的元素。

你可以假设数组中不存在重复元素。

示例 1:

输入: [3,4,5,1,2]输出: 1

示例 2:

输入: [4,5,6,7,0,1,2]输出: 0

分析:

如果数组没有翻转,即 nums[left] <= nums[right],则 nums[left] 就是最小值,直接返回。

如果数组翻转,需要找到数组中第二部分的第一个元素:

下面讨论数组翻转的情况下,如何收缩区间以找到这个元素:

若 nums[left] <= nums[mid],说明区间 [left,mid] 连续递增,则最小元素一定不在这个区间里,可以直接排除。因此,令 left = mid+1,在 [mid+1,right] 继续查找。

否则,说明区间 [left,mid] 不连续,则最小元素一定在这个区间里。因此,令 right = mid,在 [left,mid] 继续查找

[left,right] 表示当前搜索的区间。

注意 right 更新时会被设为 mid 而不是 mid-1,因为 mid 无法被排除。

class Solution {

public int findMin(int[] nums) {

if(nums.length==1)return nums[0];

if(nums[0]<nums[nums.length-1])return nums[0];

int left=0;

int right=nums.length-1;

int mid=0;

while(left<right){

mid=(left+right)/2;

if(nums[mid]>=nums[0]){

left=mid+1;

}else{

right=mid;

}

}

return nums[right];

}

}

**例3:**其它条件和例2一样,例3要搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。

思路:(请先自行思考)

先通过例2的方法找到分割点,再对左右某一边进行二分即可,代码请自行书写。

3.6二维数组的二分查找

============

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

每行中的整数从左到右按升序排列。

每行的第一个整数大于前一行的最后一个整数。

示例 1:

matrix = [

[1,   3,  5,  7],

[10, 11, 16, 20],

[23, 30, 34, 50]

]

target = 3

输出: true

示例 2:

matrix = [

[1,   3,  5,  7],

[10, 11, 16, 20],

[23, 30, 34, 50]

]

target = 13

输出: false

仔细观察我们发现,其实这个二维数组整体就是有序的,所以当成一维数组二分查找即可,但是要注意二维数组的操作,需要一点功底。

class Solution {

public boolean searchMatrix(int[][] matrix, int target) {

if (matrix == null || matrix.length == 0) {

return false;

}

int row = matrix.length;

int col = matrix[0].length;

int start = 0;

int end = row * col - 1;

while (start <= end) {

int mid = start + (end - start) / 2;

if (matrix[mid / col][mid % col] == target)return true;

else if (matrix[mid / col][mid % col] > target)end = mid - 1;

else start = mid + 1;

}

return false;

}

}

注意:这里有个细节:正规的二分其实都应该这么写,之前的写法可能会溢出。

3.7二叉树上二分的题目积累

==============

我们刚才学会了一些一维二维数组的二分操作,下面我们再去其它数据结构试试看,继续养成二分的思想。

例1:先看一道简单的在二叉树上查找值。

给定一个不为空的二叉搜索树和一个目标值 target,请在该二叉搜索树中找到最接近目标值 target 的数值。

注意:

给定的目标值 target 是一个浮点数,题目保证在该二叉搜索树中只会存在一个最接近目标值的数。

示例:

输入: root = [4,2,5,1,3],目标值 target = 3.714286

4

/ \

2   5

/ \

1   3

输出: 4

思路:二分,当前节点比target大就往左边搜(因为右边的差距更大),当前节点比target小就往右搜(因为左边的差距更大)。

/**

  • Definition for a binary tree node.

最后如何让自己一步步成为技术专家

说句实话,如果一个打工人不想提升自己,那便没有工作的意义,毕竟大家也没有到养老的年龄。

当你的技术在一步步贴近阿里p7水平的时候,毫无疑问你的薪资肯定会涨,同时你能学到更多更深的技术,交结到更厉害的大牛。

推荐一份Java架构之路必备的学习笔记,内容相当全面!!!

成年人的世界没有容易二字,前段时间刷抖音看到一个程序员连着加班两星期到半夜2点的视频。在这个行业若想要拿高薪除了提高硬实力别无他法。

你知道吗?现在有的应届生实习薪资都已经赶超开发5年的程序员了,实习薪资26K,30K,你没有紧迫感吗?做了这么多年还不如一个应届生,真的非常尴尬!

进了这个行业就不要把没时间学习当借口,这个行业就是要不断学习,不然就只能被裁员。所以,抓紧时间投资自己,多学点技术,眼前困难,往后轻松!

【关注】+【转发】+【点赞】支持我!创作不易!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

/ \

1   3

输出: 4

思路:二分,当前节点比target大就往左边搜(因为右边的差距更大),当前节点比target小就往右搜(因为左边的差距更大)。

/**

  • Definition for a binary tree node.

最后如何让自己一步步成为技术专家

说句实话,如果一个打工人不想提升自己,那便没有工作的意义,毕竟大家也没有到养老的年龄。

当你的技术在一步步贴近阿里p7水平的时候,毫无疑问你的薪资肯定会涨,同时你能学到更多更深的技术,交结到更厉害的大牛。

推荐一份Java架构之路必备的学习笔记,内容相当全面!!!

[外链图片转存中…(img-lg5Ad91M-1713674977580)]

成年人的世界没有容易二字,前段时间刷抖音看到一个程序员连着加班两星期到半夜2点的视频。在这个行业若想要拿高薪除了提高硬实力别无他法。

你知道吗?现在有的应届生实习薪资都已经赶超开发5年的程序员了,实习薪资26K,30K,你没有紧迫感吗?做了这么多年还不如一个应届生,真的非常尴尬!

进了这个行业就不要把没时间学习当借口,这个行业就是要不断学习,不然就只能被裁员。所以,抓紧时间投资自己,多学点技术,眼前困难,往后轻松!

【关注】+【转发】+【点赞】支持我!创作不易!

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-z9zzj08b-1713674977580)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 11
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值