【算法修炼】二分查找最接近元素(最接近下标)

之前学习的二分模板基本都是:查找相等元素的下标、第一个大于等于元素的下标和第一个大于元素的下标。但题目往往会考察:与当前元素差值最小的元素下标、最小的差值、最小差值的元素等,这就需要在二分的基础上加一些修改。

查找最接近的元素(简单)

在这里插入图片描述
只需要用到:"第一个大于等于key元素"的二分模板

考虑使用该函数后的返回值:

  • 返回等于key元素的元素下标,也就是说直接找到了key,此时答案就是key元素自身。
  • 返回数组长度,也就是说没有找到大于等于key的元素(数组中所有元素小于key),此时答案就是num[idx-1]。
  • 排除情况一的情况下,返回下标=0,说明key元素的值小于所有数组中元素,此时答案就是num[0]。
  • 排除情况一的情况下,返回下标,此时说明找到了第一个大于key元素的值的下标,此时答案需要比较位于key元素左右两边的元素值,看哪边的差值更小,也就是:Math.min(num[idx] - key, key - num[idx])。
import java.util.*;
import java.io.*;

public class Main {
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    public static void main(String[] args) throws IOException {
        int n = Integer.parseInt(reader.readLine().trim());
        String[] input = reader.readLine().trim().split(" ");
        int[] num = new int[n];
        for (int i = 0; i < n; i++) num[i] = Integer.parseInt(input[i]);
        int m = Integer.parseInt(reader.readLine().trim());
        while (m-- > 0) {
            // 最接近相应给定值的元素值
            int key = Integer.parseInt(reader.readLine().trim());
            int pos = search1(num, key);
            // 返回值可能为:key本身的下标,数组长度(没有大于等于key的数),大于key的数的下标(需要判断小于key还是大于key的数的差值小)
            if (pos == n) pos--;
            else if (pos != 0){
                if (Math.abs(num[pos - 1] - key) <= Math.abs(num[pos] - key)) pos--;
            }
            System.out.println(num[pos]);
        }
    }
    static int search1(int[] num, int key) {
        int left = 0;
        int right = num.length;
        int mid = 0;
        // 找第一个大于等于key的数
        while (left < right) {
            mid = left + (right - left) / 2;
            if (num[mid] >= key) {
                right = mid;  // 保证是第一个大于等于key的数!
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
}

最近距离(中等)

上一题在找最接近的元素,本题在找最接近0的下标,本质还是一样的,因为要 |i - j|尽可能小,i是a数组中各元素的下标,j是0值元素的下标,那就是找最接近的0值元素下标,那么就可以单独存储0值元素的下标。
在这里插入图片描述

在这里插入图片描述

import java.util.*;
import java.io.*;

public class Main {
    static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
    static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    static List<Integer> idx;
    public static void main(String[] args) throws IOException {
        int n = Integer.parseInt(reader.readLine().trim());
        String[] input = reader.readLine().trim().split(" ");
        idx = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            if (input[i].equals("0")) {
                // 记录0值的下标
                idx.add(i);
            }
        }
        // 二分先排序
        Collections.sort(idx);
        for (int i = 0; i < n; i++) {
            if (input[i].equals("0")) {
                writer.write(input[i] + " ");
            } else {
                // 返回结果是下标的下标
                int ans = search(i);
                if (ans == 0) {
                    writer.write(Math.abs(idx.get(ans) - i) + " ");
                } else {
                    if (ans == idx.size()) {
                        ans--;
                    } else if (Math.abs(idx.get(ans - 1) - i) < Math.abs(idx.get(ans) - i)) {
                        ans--;
                    }
                    writer.write(Math.abs(idx.get(ans) - i) + " ");
                }
            }
        }
        writer.flush();
    }
    // 找第一个大于key的下标位置
    static int search(int key) {
        int left = 0;
        int right = idx.size();
        int mid = 0;
        while (left < right) {
            mid = left + (right - left) / 2;
            if (idx.get(mid) > key) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        return left;
    }
}
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@u@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值