7月算法训练------第九天(二分查找)解题报告
题目类型:二分查找
题目难度:中等
第一题、
- 题目链接:1146. 快照数组
- 思路分析:
我们用一个List sn数组来模仿三维数组;List sn的长度为length,List中存放的时一个ArrayList,ArrayList中存放的时一个二维数组;我们用以下来表示
["SnapshotArray","set","snap","set","get"] [[3],[0,5],[],[0,6],[0,0]]
执行SnapshotArray(3)
时,创建长度为3的List数组sn;
执行set(0,5)
时,我们向sn[0]中加入数组{id,val}
,表示数组下标为0
的元素,在第id次时变成过val
;这里我们就向sn[0]
的ArrayList
中加入-1
次快照时的val
值5
;
当执行snap
时我们先加加再返回id值;
执行get(0,0)
时,我们就用二分查找,返回List sn
中第index
个ArrayList
的版本值与snap_id
对应的数字;
执行完之后List数组成为如下所示的模样:
位置 | 指向 |
---|---|
sn[0] | ArrayList1 |
sn[1] | ArrayList2 |
sn[2] | ArrayList3 |
其中ArrayList1、2、3如下所示:
名 | 内容 | |
ArrayList1 | -1 | 5 |
0 | 6 |
表示版本-1
时,我们将ArrayList1
改为5
,版本0
时,我们将ArrayList1
改为6
;
ArrayList2
和ArrayList3
中什么也没有。
- 代码:
class SnapshotArray {
int id = -1;
List<int[]>[] sn;
public SnapshotArray(int length) {
sn = new List[length];
for(int i = 0; i < length; i++){
sn[i] = new ArrayList<int[]>();
}
}
public void set(int index, int val) {
sn[index].add(new int[]{id, val});
}
public int snap() {
return ++id;
}
public int get(int index, int snap_id) {
List<int[]> snList = sn[index];
int l = 0, r = snList.size() - 1;
int mid = 0;
int ans = 0;
while(l <= r){
mid = (l+r) >> 1;
if(snList.get(mid)[0] < snap_id){
l = mid + 1;
ans = snList.get(mid)[1];
}else{
r = mid - 1;
}
}
return ans;
}
}
/**
* Your SnapshotArray object will be instantiated and called as such:
* SnapshotArray obj = new SnapshotArray(length);
* obj.set(index,val);
* int param_2 = obj.snap();
* int param_3 = obj.get(index,snap_id);
*/
第二题、1498. 满足条件的子序列数目
- 题目链接:1498. 满足条件的子序列数目
- 思路分析:
先对数组进行排序;
然后枚举某个nums[i],二分查找nums[j],由于排序了,所以nums[i] <= nums[j],如果他们的和小于等于target,那么就在区间[i,j]中任意两个数的和都是满足条件的,所以在这个区间内,每个数可以选,也可以不选就是排列组合,直接用二分快速快速幂计算取模即可。 - 代码:
class Solution {
static final int mod = 1000000007;
public int numSubseq(int[] nums, int target) {
Arrays.sort(nums);
int ret = 0;
for(int i = 0; i < nums.length; ++i){
int l = i;
int r = nums.length - 1;
int ans = -1;
while(l <= r){
int mid = (l+r) >> 1;
if(nums[i] + nums[mid] <= target){
ans = mid;
l = mid + 1;
}else{
r = mid - 1;
}
}
if(ans != -1){
ret += power(2, ans - i, mod);
ret %= mod;
}
}
return ret;
}
private static long power(long a, long b, long c){
if(b == 0){
return 1;
}
long tmp = power(a*a%c, b>>1, c);
if((b&1)==1){
tmp = tmp * a % c;
}
return tmp;
}
}
第三题、1802. 有界数组中指定下标处的最大值
-
题目链接:1802. 有界数组中指定下标处的最大值
-
思路分析:
刚开始,我们把数组全都填上1;
第一轮循环,然后我们在index处+1;
第二轮循环,我们再index和index左右两边+1;
…
直到用完maxSum时,就能找到最大值了。 -
代码:
class Solution {
public int maxValue(int n, int index, int maxSum) {
int l = index, r = index;
int rest = maxSum - n;
int ans = 1;
while(l > 0 || r < n - 1){
int m = r - l + 1;
if(rest - m >= 0){
rest -= m;
ans++;
if(l != 0){
l--;
}
if(r != n - 1){
r++;
}
}else{
break;
}
}
ans += rest / n;
return ans;
}
}
第四题、2040. 两个有序数组的第 K 小乘积
- 题目链接:2040. 两个有序数组的第 K 小乘积
- 思路分析:
这一题先占个位,有点难,呜呜(-̩̩̩-̩̩̩-̩̩̩-̩̩̩-̩̩̩___-̩̩̩-̩̩̩-̩̩̩-̩̩̩-̩̩̩)( ´◔ ‸◔`)
先做一下基础题吧!
5月的题:
704. 二分查找
- 题目链接:704. 二分查找
- 思路分析:
运用二分查找,将结果找出。 - 代码:
class Solution {
public int search(int[] nums, int target) {
int l= 0, r = nums.length - 1;
while(l <= r){
int mid = (r - l) / 2 + l;
if(nums[mid] > target){
r = mid - 1;
}else if(nums[mid] < target){
l = mid + 1;
}else{
return mid;
}
}
return -1;
}
}
剑指 Offer 53 - I. 在排序数组中查找数字 I
- 题目链接:剑指 Offer 53 - I. 在排序数组中查找数字 I
- 思路分析:
运用二分查找,将结果找出。 - 代码:
class Solution {
public int search(int[] nums, int target) {
// 搜索右边界 right
int i = 0, j = nums.length - 1;
while(i <= j) {
int m = (i + j) / 2;
if(nums[m] <= target) i = m + 1;
else j = m - 1;
}
int right = i;
// 若数组中无 target ,则提前返回
if(j >= 0 && nums[j] != target) return 0;
// 搜索左边界 right
i = 0; j = nums.length - 1;
while(i <= j) {
int m = (i + j) / 2;
if(nums[m] < target) i = m + 1;
else j = m - 1;
}
int left = j;
return right - left - 1;
}
}