ARTS是什么? Algorithm:每周至少做一个LeetCode的算法题 Review:阅读并点评至少一篇英文技术文章 Tip:学习至少一个技术技巧 Share:分享一篇有观点和思考的技术文章
Algorithm
题目描述
第一个错误的版本
你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
假设你有 n 个版本 [1, 2, …, n],你想找出导致之后所有版本出错的第一个错误的版本。
你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
示例:
给定 n = 5,并且 version = 4 是第一个错误的版本。
调用 isBadVersion(3) -> false
调用 isBadVersion(5) -> true
调用 isBadVersion(4) -> true
所以,4 是第一个错误的版本。
解题思路
方法一:暴力破解,直接线性扫描,从1开始遍历,找出第一个为true的版本。
最坏的情况可能为查找了n-1次
时间复杂度:O(n)
代码:
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
// 每个值都遍历一遍
for(int i =1; i < n; i++){
if (isBadVersion(i)){
return i;
}
}
return n;
}
}
方法二:二分查找法,设定左边界为1开始和右边界为n。当左边界=右边界时,即为第一个错误版本。
时间复杂度:O(logn)
代码
public class Solution extends VersionControl {
public int firstBadVersion(int n) {
int left = 1, right = n;
while (left < right) {
int mid = left + (right - left) / 2; // 找到中点
if (isBadVersion(mid)) {
right = mid; // 在区间 [left, mid] 中
} else {
left = mid + 1; // 在区间 [mid+1, right] 中
}
}
return left; // 此时有 left == right
}
}
Review
Stop Comparing JSON and XML(停止json和xml的比较)
Tip
上周分享了数组,数组用一块连续的内存空间,来存储相同类型的一组数据;而链表恰恰相反,它并不需要一块连续的内存空间,它通过“指针”将一组零散的内存块串联起来使用。
在进行数组的插入、删除操作时,为了保持内存数据的连续性,需要做大量的数据搬移,所以时间复杂度是 O(n)。而在链表中插入或者删除一个数据,我们并不需要为了保持内存的连续性而搬移结点,因为链表的存储空间本身就不是连续的。所以,在链表中插入和删除一个数据是非常快速的,直接改变链表中的指向即可实现插入删除操作,因此时间复杂度为O(1)。
Share
散列函数(哈希)
散列(Hashing)通过散列函数将要检索的项与索引(散列 key,散列值 value)关联起来,生成一种便于搜索的数据结构(散列表)。
散列算法常被用来加密存在数据库中的密码(password)字符串,由于散列算法所计算出来的散列值(Hash Value)具有不可逆(无法逆向演算回原本的数值)的性质,因此可有效的保护密码。如:MD5加密。
在散列函数存在哈希冲突的问题,因为键的数目总是比索引的数目多,所以会存在一个键对应多个相同的索引位置的情况,于是就产生了哈希冲突。
常用的解决哈希冲突的方法有分离链接法和开放地址法
哈希表一旦发生冲突,其性能就会显著下降。因此建立哈希表时必须规避哈希冲突的产生,大多数哈希表的实现都是:第一步,是通过哈希算法将key值转换一个整数以确定数据的存储位置;第二步,检查是否发生哈希冲突,以及确定发生冲突后的处理方案。