点击上方蓝字设为星标

下面开始今天的学习~

前言

一个优秀的程序员具备挺多特质的,比如好奇心,学习能力等,但在我看来一个优秀的程序员必须具备几项核心能力,哪几项,先卖个关子,程序员最喜欢说的话是「Talk is Cheap, show me your code」,所以先来看一道很常见的面试题

如何快速定位IP对应的省份地址?

我们知道,每个省市都分配了一个 ip 段,如下

[202.102.133.0, 202.102.133.255]    山东东营市
[202.102.135.0, 202.102.136.255]    山东烟台
[202.102.156.34, 202.102.157.255]   山东青岛
[202.102.48.0, 202.102.48.255]   江苏宿迁
[202.102.49.15, 202.102.51.251]  江苏泰州
[202.102.56.0, 202.102.56.255]   江苏连云港

输入一个  ip 地址怎么做到秒级定位此 ip 所在的省市呢?

如图示:在百度上输入一个 ip 地址,能做到秒级展示其所属地,怎么做到的呢,背后用到了什么原理

这就引入了我们要谈的程序员需要具备的第一项能力: 抽象问题或者说数据建模的能力

抽象问题的能力

所谓抽象问题或者说数据建模的能力,即能把一个问题抽象或归类为某种方案来解决,比如要实现负载均衡, 会想到一致性哈希算法,要实现最短路径,想到使用动态规划, 微服务下要保证服务可用引入降级机制等等,一句话就是把具体的问题抽象成到解决此问题背后的方法论,进而用相关的技术方案得以解决。

回归到如何快速定位 IP 对应的省份地址这道题来看,如果我们不具备抽象问题的能力,硬着头皮从头到尾把输入的ip 与所有区间段的 ip 都遍历对比一遍,然后判断它落到哪个区间,那么 ip 地址有 32 位,共有 2^32 个,约有  42.9 亿个,用暴力遍历法每查找一个 ip 最坏情况下要遍历约 42 亿次,这种方法显然是不可行的。

所以我们必须得把这个问题抽象为另一种可行的方法,即:二分查找, ip 地址查找怎么就跟二分查找扯上关系了,背后的逻辑是什么,我们一起来看看。

ip 地址不容易比较,那我们首先把 ip 地址转成整数,于是每个省市对应的 ip 地址区间就变成了整数区间,假设为如下区间

[1, 5]
[11, 15]
[16, 20]
[6, 10]
....

再以每个整数区间的起始数字对这些区间进行排序,排序后的区间如下

[1, 5]
[6, 10]
[11, 15]
[16, 20]
...

看到这些排序后的区间,想到了啥,二分查找就是在一组有序的数字中进行查找!是不是找到相似点了?

这里给没听过二分查找的读者简单普及下啥是二分查找,小时候可能我们都玩过猜字游戏,在纸面上写一个 1 到 100 的数字,比如 70,让对方猜,怎样猜才能猜最快。

  1. 首先猜 1 和 100 的中间数字 (1+ 100) / 2 =  50(取整)

  2. 50 < 70, 于是我们继续猜 50 和 100 的中间数字 (50+100) / 2 = 75

  3. 75 > 70,于是我们继续猜 50 和 75 的中间数字 (50+75) / 2 = 62

  4. 依次持续类似以上的步骤,不断地缩小范围,直至找到 70

总共只猜了 7 次,比起我们从 1 猜到 100 效率高了十几倍,如果被猜字的范围从一扩大到成百上千万,提升的效率是指数级的!二分查找也叫折半查找(注意上文中加粗的中间数字),仔细看上图,每查找一次,问题规模缩小一半,整体时间复杂度是O(logn),即使我们要在 42 亿的数字中查找数字,最多也只要查 32 次,所以采用二分查找对查找性能的提升无疑是巨大的!

二分查找是要在一堆有序的数字中精准地查找所要查找的数是否存在,而回过头来看已经排序好的以下 ip 段

[1, 5]
[6, 10]
[11, 15]
[16, 20]
...

我们要查找的是某个整数是否在一个有序数组的相邻两个数字的区间里,例如:取这些 ip 区间的起始地址组成一个数组 (1,6,11,16,....)(有序数组),如果我们要找的 ip 对应的整型为 14, 由于它在 [11,16) (11是闭区间,16是开区间) 之间,所以这个 ip 就落在 [11, 15] 这个 ip 区间,这样就找到了这个 ip 对应的省市了。

所以就由二分查找某个值是否存在转变成了查找某个值是否在有序数组中相邻的两个值之间了,这就引入了程序员要具备的第二层能力:举一反三或者说修改模型的能力

修改模型的能力

就像机器学习,现在其实有很多现成的模型可用,比如识别物的模型等等,我们需要的话可以直接拿来用,但是现有模型的准确率可能不是那么理想(比如只有80%),如果我们需要进一步地提升识别准确率,可能就需要对其参数进行进一步的调优,以进一步地优化模型,达到我们预期的值。

再比如当当网基于 Dubbo 的扩展版本开发的 Dubbox 也是由于原来的 Dubbo 功能不满足其团队需求而在其基础上修改扩展的。

回过头来看以上说的原来二分查找只是查找某个值是否存在,而我们现在要解决的问题是查找某个值是否在相邻的两个值之间,这本质是也是对模型的调优或修改,以进一步满足我们的要求。于是我们写下了如下代码

public static int bsearch(int[] a, int length, int value) {int low = 0;int high = length - 1;while (low <= high) {int mid = (low + high) / 2;if (a[mid] > value) {if (mid == 0) {return -1;}if (a[mid-1] <= value) {return mid-1;} else {high = mid-1;}}else {low = mid + 1;}}return -1;
}

那这段代码有啥问题吗,或者说有哪些可以优化的空间,这就引入了程序员需要具备的第三项能力: 代码要有足够的健壮性

代码要有足够的健壮性

仔细看上文的代码,有两个地方有潜在隐患,一个是 length 可能是负数,而显然数组的长度不可能是负数,也就是说对这种异常数据应该抛异常。另外 (low + higth) / 2 这段代码中的 low+high 如果在数组很大的情况下比较容易造成溢出,所以可以改造成 low + (high - low) / 2, 另外为了提升性能可以把除以 2 改成位运算,即 low + ((high - low) >> 1),于是代码变成了

public static int bsearch(int[] a, int length, int value) throws Exception {if (length < 0) {// 实际应该抛出一个继续自Exception的异常,这里为了方便直接抛出Exceptionthrow new Exception("数据长度不合法");}int low = 0;int high = length - 1;while (low <= high) {int mid = low + ((high - low) >> 1);if (a[mid] > value) {if (mid == 0) {return -1;}if (a[mid-1] <= value) {return mid-1;} else {high = mid-1;}}else {low = mid + 1;}}return -1;
}

有人可能觉得判断数组长度小于 0 过于严苛了,但是是人就会犯错误,这里也是为了强调我们对异常情况的处理要到位,说到代码的健壮性,这里再多说几句,在创业初期我司主要用的是 php,主要是创业团队追求快,用 PHP 这种弱类型语言开发确实效率高,不过不安全,线上多次出现因为变量可以随意赋值造成的多次线上故障,而 Java 这种强类型语言虽然开发效率上比 PHP 慢了不少,但强类型语言的特征保证了它的稳定,足够安全,所以后期随着人员的扩充,为了保证线上足够安全,我司去年把大部分的服务都 Java 化了,近年来有不少人唱衰 Java,但 Java 的安全,稳定性以及强大的生态能力注定了它的长久生命力。

代码写成这样看起来确实完美了,还能再优化吗,注意上文中的代码只适用于 int 的数组,如果用二分查找法进行区间查找具有通用性,比如我们想针对 short 或 long 型等类型的数组进行查找就无能为力了,所以这就引入了程序员需要具备的第四项能力: 代码要有足够的可扩展性

代码要有足够的可扩展性

怎么让 bsearch 这个二分查找也支持 long 型或 short  型数组呢,Java 支持重载,再针对 bsearch 进行多个函数的重载是一种方式,不过会造成代码的大量冗余,所以另一种更合适的方式是利用 Java 语言中的泛型,于是我们的代码改造如下

 public static <T extends Comparable> int bsearch(T[] a, int length, T value) throws Exception {if (length < 0) {// 实际应该抛出一个继承自Exception的异常,这里为了方便直接抛出Exceptionthrow new Exception("数据长度不合法");}int low = 0;int high = length - 1;while (low <= high) {int mid = low + ((high - low) >> 1);if (a[mid].compareTo(value) > 0) {if (mid == 0) {return -1;}if (a[mid-1].compareTo(value) <= 0) {return mid-1;} else {high = mid-1;}}else {low = mid + 1;}}return -1;
}

写成这样,可以说我们的代码具有足够的健壮性与可扩展性了。

总结

本文通过一个常见的面试题来详细阐述了优秀程序员必须具备的四项核心能力:抽象问题,修改模型,写出健壮性,可扩展性的代码!

所以为什么面试中大厂喜欢考算法,主要是想详细地了解你是否具备解决此算法题背后的思想,即 抽象问题 的能力,面试官还喜欢对相应算法题进行各种变形,其实也是为了考察你是否具有 修改模型 的能力(比如一个翻转链表,可以引申出顺序每 k 个一组翻转,逆序每 k 个一组翻转),所以为了同时具备这两项能力,我们需要提前掌握大量的理论知识,做大量的刻意练习。

共勉!大家加油:)

从一个面试题看程序员的几项基本功相关推荐

  1. 如何成为一个更好的程序员,或者说是学习者?给你七个建议!

    点击关注上方"五分钟学算法", 设为"置顶或星标",第一时间送达干货. 转自编码之外 今天庆哥就来和大家聊聊,如何成为一个更好的程序员,毕竟别人都说程序员都是屌 ...

  2. 臻好黄金百香果苗做一个有脑子的程序员

    程序员是最理性的一个群人,除非面对电子产品的时. 程序员是一群高智商的群体,唯一的缺点就是发际线总是很难防守. 程序员是一群情商比较低的人群,常常看到程序员仅仅因为对技术的理解不同而大吵起来. 程序员 ...

  3. 争取做一个良性循环的程序员

    争取做一个良性循环的程序员,莫让恶性循环上身. 以下阐述仅仅的是个人的想法和意见!觉得有说的不对的地方您老人家可以随手关掉页面,顺便可以嘀咕一句(太水了,简直就是胡诌)!^_^ 一:需求与概要 一点1 ...

  4. python好学吗 老程序员-今天面试了一个34岁大龄程序员,有感而发

    原标题:今天面试了一个34岁大龄程序员,有感而发 " 昨天,我面试了一个34岁的大龄程序员--我给人事的建议是P4,结果人事说:那直接让他走吧. " 我一直以为他们在开玩笑! 结果 ...

  5. 如何做一个懂产品的程序员?

    这篇是之前发过的<懂程序员的产品经理是什么样子?>的镜像篇,这次是程序员视角. 两个相爱相杀的岗位,想要更好的达成共识.更好的合作,自然不仅仅是一方的事情.这次Z哥先会带你看看产品经理眼中 ...

  6. 用php写一个可以抽取随机数的工具一次只抽四个怎么实现?_面试了一个32岁的程序员,场面一度很尴尬。...

    招人背景 首先说一下朋友的公司招人背景,公司招聘PHP高级岗位,负责公司的B2B项目研发.并发问题的处理和解决.领导给了他两个要求:(接下来的讲述我会以朋友的第一人称来进行) (1)技术比较好 (2) ...

  7. 面试阿里挂了却拿到网易、点我达offer,一个三年经验Java程序员的面试总结

    转载自  面试阿里挂了却拿到网易.点我达offer,一个三年经验Java程序员的面试总结 前言 15年毕业到现在有三年多了,最近去面试了阿里集团(菜鸟网络,蚂蚁金服).网易.滴滴.点我达,最终收到点我 ...

  8. 30分钟,让你成为一个更好的程序员

    我相信激励是非常重要的.这也是为什么我常常把时间管理(这些书激励我不管改进我的时间管理方法)的书和软件开发拿出来看看.我最近刚看完一本 书,"Apprenticeship Patterns: ...

  9. 我是一个来自泰兴的程序员,我喜欢C++

    我们学习了一个感人的故事,我是一个来自泰兴的程序员,我喜欢C++ 还有一条短短地小尾巴,它有一副坚硬的龟壳,腿脚落下了残疾,每天晚上我做家庭作业时,不愿意让同学看到她走路的姿势,婆婆更爱我,离开,一缕 ...

最新文章

  1. Python教学课程分享10-异常处理结构
  2. JavaScript基础学习3
  3. Log4net 配置使用总结(一)
  4. Java黑皮书课后题第8章:*8.31(几何:交点)编写一个方法,返回两条直线的交点。四个点存放在4*2的二维数组points中。编写一个程序,提示用户输入4个点,并显示交点
  5. php 编程祝新年快乐_用于测试自动化的7种编程语言
  6. 5个步骤带你入门FPGA设计流程
  7. php标准库string,PHP中的一些标准库
  8. PHP定义常量define和const的区别
  9. eclipse插件下载地址
  10. android手机常用浏览器,Android平台三款手机浏览器对比评测
  11. 在字节实习8个月后,成功转正
  12. Android ORC文字识别之识别身份证号等(附源码)
  13. Power BI中的填充功能
  14. Label 标签使用
  15. 7-65 字符串替换 (15 分) 本题要求编写程序,将给定字符串中的大写英文字母按以下对应规则替换: 原字母 对应字母 A Z B Y C X D W … … X C Y B Z A
  16. python绘制饼图的如何设置高度宽度_Matplotlib添加pictu时调整饼图大小
  17. 巨简单 在eclips上面新建一个网页动态项目
  18. 干货分享:大数据可视决策关键技术有哪些?分析篇
  19. MySQL8.0的下载、安装、配置
  20. 项目管理中最常见的问题有哪些?

热门文章

  1. 【计算机视觉】EmguCV学习笔记(4)分离颜色通道以及多通道图像混合
  2. 高中计算机个人总结怎么写,毕业生自我总结范文
  3. 为什么要研究游戏 AI 呢?
  4. 年收入百万美元AI科学家的烦恼与思考
  5. ECCV 2020 | 对损失信息进行建模,实现信号处理高保真还原
  6. 就因为一个笔记本,运营和产品吵得不可开交......
  7. 免费公开课 | 基于定制数据流技术的AI计算加速
  8. 我发现了一个非常酷的软件,用自然语言编程!
  9. 00后的AI开发者进阶之道:从入门到鏖战MIT编程大赛 | 人物志
  10. 手机芯片谁是AI之王?高通、联发科均超华为