前言:
在算法设计中,你肯定遇到过这样的问题,明明我就是按照大体思路来进行设计的,可为什么 结果就是不对呢?你抓狂,我也抓狂。
这其实就关乎算法细节上的问题了。在这里,细节从未如此重要,程序世界仅仅一个 看似微不足道的错误,就能导致算法的彻底失效!
今天我们从具体实例出发、复现你抓狂的情景,来带你探讨算法细节设计工作中的基本方法——从具体到抽象。
论述:
我们面临的绝大多数的算法都是对数据加工处理的算法,这很好笑,这当然是,因为计算机能做之一就是对数据的运算。我们来看这道算法的运算,相信大家不陌生,是二分查找算法。
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
算法的基本思想是将待查找区间分为两部分,判断中间元素与目标值的关系,以决定是取左半区间还是右半区间继续查找。然后我对着电脑一顿猛巧,把这个思想逻辑用代码的形式实现成了这样
我可能遇到这样中情景:我搞不清楚 while left ? right:
这个循环条件到底是 <
?,还是 <=
?
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left ? right: # 循环条件待确定
mid = left + (right - left) // 2
if arr[mid] < target:
left = mid + 1
elif arr[mid] > target:
right = mid - 1
else:
return mid
return -1
//**********附上C语言版本***********
int binary_search(int arr[], int n, int target) {
int left = 0, right = n - 1;
while (left ? right) {
int mid = left + (right - left) / 2;
if (arr[mid] < target) {
left = mid + 1;
} else if (arr[mid] > target) {
right = mid - 1;
} else {
return mid;
}
}
return -1;
}
唤起你记忆了吧,其实这种类似的情况我们也发生过,这时候该怎么办?那就是把抽象的数据处理过程,转变为具体的数据处理过程,在具体的数据中帮助我们细节的设计。具体的数据我们更容易把握,能容易监控数据处理的过程,发现其中的问题。
我不确定那就假设为“<”,具体数据我设简单点arr = [1, 2, 3, 4, 5]
,目标值target = 5
。那么算法的执行过程是这样的:
- 初始时,
left = 0
,right = 4
。 - 第一次循环,
mid = 2
,arr[mid] = 3
,小于target
,因此left
变为mid + 1
,即left = 3
。 - 第二次循环,
left = 3
,right = 4
,mid = 3
,arr[mid] = 4
,仍然小于target
,left
再次变为mid + 1
,即left = 4
。 - 如果此时循环条件是
left < right
,那么循环将结束,算法返回-1,这显然是错误的,因为target
实际上存在于数组中。
我们分析当target
是数组中的最后一个元素时,如果使用left < right
作为循环条件,算法将提前退出循环。因此,正确的循环条件应该是left <= right
,这样即使left
和right
相等,也会再执行一次循环,确保不会错过目标值。于是我们找到了符合这个具体数据的算法,它是恰巧的吗?我们在回归抽象的在模拟这个算法的执行发现是通用的,我们找到了适用于通用数据的算法。
这样我们解决了这个算法问题。在这次讲述中我并不想让大家过多关注二分查找算法,这仅是一个例子,在这个例子中我们通过带入具体的数据最后找到了正确的算法。不光这一个应用场景,我想告诉大家,在我们面对复杂的算法设计任务时,我们可能会呈现出“稀里糊涂”的状态,遇到不确定的算法细节设计的时候,我们都可以带入具体的数据来帮助我们理清执行过程,找到你最后期望的成功算法。
~~~~~~
以上就是我本篇想讲的所有内容了,如果这篇文章对你有价值的话,还请点个赞,你的支持对我非常重要!
我是阿航,一位胆大包天、梦想成为大牛的学生~
我们下篇文章接着聊