1、二分查找问题:
一个无重复的有序整数数组中查找某个数的位置,如果找到则返回下标,否则返回-1。
1 | def BinarySearch(Arr,x):#二分查找 |
二分查找算法,每次循环都相当于去掉一半的值,并将剩下一半的值内设好right,left,mid等索引,然后在剩下一半的值内查找,直到找到值,由此可以很快的找到所要找的值.
2、 有重复的二分查找
:在一个可重复的升序的整数数组中查找某个数的开始位置和结束位置。 如果数组中不存在,则返回 [-1, -1]。 算法时间复杂度要求为 O(log n) 。
示例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的索引两边的值,直到索引左边或右边的值不再是所查值target时,但很快便发现这样的时间复杂度不是O(log n),于是只好改变算法。
因此想到用二分查找法找到其中一个Arr[mid]=target后,不查找到target的所有范围,而只是对比target左边或右边一个位置的值,如果Arr[mid]左边还有target,则将right等于mid-1,循环将在已找到的target左边继续查找,直到Arr[mid]左边不是target,这样返回的mid则是target在数组中在开始的索引;如果Arr[mid]右边还有target,则将left等于mid+1,循环将在已找到的target右边继续查找,直到Arr[mid]右边不是target,这样返回的mid则是target在数组中最后的索引,而且由于每次循环仅判断target左边或右边一次的值,故数据循环度还是O(log n).这样将找到开始值与结束值分别用函数表示出来,从而得到target的范围,这样完成了有重复的某个数的开始位置和结束位置的查找,且时间复杂度符合要求。
1 | def searchFirstIndex(Arr,target):#定义函数得到查找数的开始位置 |
3、矩阵查找问题:
在一个 m x n 的有序整数矩阵中查找某个数。有序矩阵是指每行中的整数从左到右为升序排列、每行的第一个整数大于前一行的最后一个整数。找到返回true,否则返回false。
示例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
首先想到将矩阵转换为数组,对数组进行二分查找找到所查的数,而且也很好实现。
之后又想到一种方法,由于有序矩阵是指每行中的整数从左到右为升序排列、每行的第一个整数大于前一行的最后一个整数,因此我们可以先对每一行最后一列的值与所查的值比较,这样就能找到所查值所在的行,然后再在该行进行遍历,便能找到所查的值是否存在。存在则返回true,否则返回false。
1 | def searchMatrix(matrix,num): |
4、 寻找两个有序数组的中位数
:给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。 找出这两个有序数组的中位数, 假设 nums1 和 nums2 不会同时为空,要求算法的时间复杂度为 O(log(m + n))。
示例1:输入:nums1 = [1, 3] nums2 = [2] 输出:2.0
示例2:输入:nums1 = [1, 2] nums2 = [3, 4] 输出:2.5
- 看到2个有序数组,由于学习过归并排序的缘故马上想到将两个数组归并到一个数组后使用得到中位数,但要求算法的时间复杂度为 O(log(m + n)),很明显归并法并不满足。
在进过星期三递归算法的学习后,知道递归算法的时间复杂度是log类型的,于是考虑使用递归法求解,但还是没有想到算法。于是百度后发现有很多不同的解析,有些解析写得很深奥,我认真学习了其中最容易懂的算法。
该方法的关键函数就是找到2个有序数组中第k大值(从1开始)的函数,该函数就是运用了递归法,先将所求2个数组的num【k//2-1】进行比较看,哪个数组的num【k//2-1】小,而两个k//2的数最多k个数,则第k大的数必不在num【k//2-1】小的前k//2个数内,故递归,对该数组从num【k//2+1】与另一数组再次使用函数,并使k=k-k//2,一直循环,直到k=1时返回2个数组中最小的值,这就是2个有序数组中第k大的值。如[1,2,3,4]与[5,6,7]两个数组,对比nums【4//2-1】,明显知道[1,2,3,4]内2比[5,6,7]中6小,因此2必是比第k个数小的数,因此去掉1,2,递归继续在[3,4]与[5,6,7]内找到第4-2=2大的数。
若两数组总长度length为奇数,查找中位数即找到两个数组第length//2大的数,两数组总长度length为奇数length为偶数,查找中位数即找到两个数组第length//2与第length//2+1个数的平均值。
1 | def findkth(nums1,nums2,k):#递归找到两个数组中第k大的数 |
5、 假设给定的一个数组描述了 n 个宽度为 1 的柱子高度,请求出按此排列的柱子,下雨之后能接多少雨水。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
示例图:
- 刚看到该题时,以为改题目很难,但认真想了一会便发现很简单,将接到水按每一高度来算,在这一高度想要接到水,至少这一高度要有2个该高度的柱子,且接到水的量等于两个柱子索引的差再减一,即类似种树问题。因此,从数组最大值到1进行遍历,每次遍历找到数组中等于该值的所有索引,再将该高度的水全加起来,如高度=2时,数组值等于2的下标为[3, 7, 8, 10],3到7有3格水,7到8接不到水,8到10一格水,则2这一高度共有4格水。因此将高度从最大值到1每一高度的水加起来就能得到接到水的总量
1 | def sumOfWater(nums): |