Java最全不骗你,没读这一篇,你不可能懂二分,java面试简历项目经验

最后

权威指南-第一本Docker书

引领完成Docker的安装、部署、管理和扩展,让其经历从测试到生产的整个开发生命周期,深入了解Docker适用于什么场景。并且这本Docker的学习权威指南介绍了其组件的基础知识,然后用Docker构建容器和服务来完成各种任务:利用Docker为新项目建立测试环境,演示如何使用持续集成的工作流集成Docker,如何构建应用程序服务和平台,如何使用Docker的API,如何扩展Docker。

总共包含了:简介、安装Docker、Docker入门、使用Docker镜像和仓库、在测试中使用Docker、使用Docker构建服务、使用Fig编配Docke、使用Docker API、获得帮助和对Docker进行改进等9个章节的知识。

image

image

image

image

关于阿里内部都在强烈推荐使用的“K8S+Docker学习指南”—《深入浅出Kubernetes:理论+实战》、《权威指南-第一本Docker书》,看完之后两个字形容,爱了爱了!

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

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

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

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

我从 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.

  • public class TreeNode {

  • int val;
    
  • TreeNode left;
    
  • TreeNode right;
    
  • TreeNode(int x) { val = x; }
    
  • }

*/

class Solution {

public int closestValue(TreeNode root, double target) {

int val, closest = root.val;

while (root != null) {

val = root.val;

closest = Math.abs(val - target) < Math.abs(closest - target) ? val : closest;

root = target < root.val ? root.left : root.right;

}

return closest;

}

}

例2

给出一个完全二叉树,求出该树的节点个数。

说明:

完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

示例:

输入:

1

/ \

2   3

/ \  /

4  5 6

输出: 6

思路:如果不考虑最后一层,节点数完全可以算出来,每一层分别为1,2,4,8…你会发现是2的n次方,那么总和也很好求,所以问题就在于如何知道最后一层有多少节点,换句话说,最后一层的最右边的那个节点在哪里?我们需要二分搜索。

目标:找箭头指向的结点。

我们采用二分法:

1)找到右子树的最左结点

我们知道总深度为4,如果右子树深度为3(4-1),说明图中最后的1是存在的(说明最后一行最右结点一定来自右子树),否则,如果我们修改一下如下图:

右子树深度为2!=4-1,不存在最后一行的结点。(说明最后一行最右结点一定来自左子树).

2)判断之后,如果是这种情况,我们排除了左子树,计算排除的结点个数,并对右子树(如图2)做相同的处理。

更新结点数(未被框起的部分,满二叉树公式+1)+1是根结点

3)对方框内重复此过程。

我们继续看右子树,发现右子树深度为1!=3-1.

说明最深层最右结点来自于左子树。所以对左子树重复上述过程

我们发现,右子树深度=1=2(整棵树深度)-1,说明最深层最右结点来自于右子树,所以对右子树重复此过程。

最终找到它。

public class Demo {

public static class Node {

public int value;

public Node left;

public Node right;

public Node(int data) {

this.value = data;

}

}

//返回结点个数

public static int nodeNum(Node head) {

if (head == null) {

return 0;

}

return bs(head, 1, mostLeftLevel(head, 1));

}

//返回根为node,当前层数为l,总深度为h的结点个数

public static int bs(Node node, int l, int h) {

if (l == h) {

return 1;

}

if (mostLeftLevel(node.right, l + 1) == h) { //右子树最深一行最左为空

return (1 << (h - l)) + bs(node.right, l + 1, h); //右bs+左子树结点个数

} else { //右子树最深一行最左不为空

return (1 << (h - l - 1)) + bs(node.left, l + 1, h);//左bs+右子树结点个数

}

}

//计算树的高度

public static int mostLeftLevel(Node node, int level) {

while (node != null) {

level++;

node = node.left;

}

return level - 1;

}

public static void main(String[] args) {

Node head = new Node(1);

head.left = new Node(2);

head.right = new Node(3);

head.left.left = new Node(4);

head.left.right = new Node(5);

head.right.left = new Node(6);

System.out.println(nodeNum(head));

}

}

我们再回忆第4节泛化概念时给的伪代码:

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

if (满足某种条件) {

排除一半答案

} else {

排除另一半答案

}

}

其实真的就是这样的,一个终止条件(本题就是l==h),一个if判断条件(本题就是mostLeftLevel(node.right, l + 1) == h),注意细节,就可以进行各种广义的二分啦。

3.8数学中的二分

=========

实现 int sqrt(int x) 函数。

计算并返回 x 的平方根,其中 x 是非负整数。

由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

示例 1:

输入: 4输出: 2

示例 2:

输入: 8输出: 2

说明: 8 的平方根是 2.82842…,

由于返回类型是整数,小数部分将被舍去。

思路依旧是二分,格式和上文都一样。

public class Solution {

public int mySqrt(int x) {

long left = 0;

long right = Integer.MAX_VALUE;

while (left < right) {

long mid = (left + right + 1) >>> 1;

long square = mid * mid;

if (square > x) {

right = mid - 1;

} else {

left = mid;

}

}

return (int) left;

}

}

总结:心得体会

既然选择这个行业,选择了做一个程序员,也就明白只有不断学习,积累实战经验才有资格往上走,拿高薪,为自己,为父母,为以后的家能有一定的经济保障。

学习时间都是自己挤出来的,短时间或许很难看到效果,一旦坚持下来了,必然会有所改变。不如好好想想自己为什么想进入这个行业,给自己内心一个答案。

面试大厂,最重要的就是夯实的基础,不然面试官随便一问你就凉了;其次会问一些技术原理,还会看你对知识掌握的广度,最重要的还是你的思路,这是面试官比较看重的。

最后,上面这些大厂面试真题都是非常好的学习资料,通过这些面试真题能够看看自己对技术知识掌握的大概情况,从而能够给自己定一个学习方向。包括上面分享到的学习指南,你都可以从学习指南里理顺学习路线,避免低效学习。

大厂Java架构核心笔记(适合中高级程序员阅读):

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

long right = Integer.MAX_VALUE;

while (left < right) {

long mid = (left + right + 1) >>> 1;

long square = mid * mid;

if (square > x) {

right = mid - 1;

} else {

left = mid;

}

}

return (int) left;

}

}

总结:心得体会

既然选择这个行业,选择了做一个程序员,也就明白只有不断学习,积累实战经验才有资格往上走,拿高薪,为自己,为父母,为以后的家能有一定的经济保障。

学习时间都是自己挤出来的,短时间或许很难看到效果,一旦坚持下来了,必然会有所改变。不如好好想想自己为什么想进入这个行业,给自己内心一个答案。

面试大厂,最重要的就是夯实的基础,不然面试官随便一问你就凉了;其次会问一些技术原理,还会看你对知识掌握的广度,最重要的还是你的思路,这是面试官比较看重的。

最后,上面这些大厂面试真题都是非常好的学习资料,通过这些面试真题能够看看自己对技术知识掌握的大概情况,从而能够给自己定一个学习方向。包括上面分享到的学习指南,你都可以从学习指南里理顺学习路线,避免低效学习。

大厂Java架构核心笔记(适合中高级程序员阅读):

[外链图片转存中…(img-Yte5XZZ5-1715335346970)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

  • 14
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值