二分法的一个变形

仅做个人记录


原始二分法

返回的是index

public int halfSearch(int a[], int s, int e, int key) {

while(s <= e) {

int mid =  (s + e)/2;

int m = mid/2;

if(m==key) return mid;

else if(m < key) e = mid-1;

else if(m > key) s = mid+1;

}

return -1;

}


新的需求如下

比如 1 2 3 4 5

给定一个start和end(下面用简称),找到在[s,e]区间内的个数

当然可以for循环走一遍,但是这里的12345已经是有序的了,整个走一遍,效率不高


思路


可以是没找到,如灰色小箭头;可以是找到了,如红色小箭头

所以统一用一个float表示


s和e的位置有这几种情况

为了提升效率,我们可以先求出s,就是横线的左边的箭头位置,s之前的数字就不用检索了

比如12345我们s=3.5,那么我们检索右边的时候,只需要检索45即可了

之后再次用e带进去查找,e右边的将被割舍,最终s和e的距离就是包含区间的距离


最终代码

package toutiao_test;

import java.util.*;

public class toutiao_2018_1 {
    public static void main(String args[]) {
        HashMap<Integer, List<Integer>> mapList = new HashMap<>();

        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int[] a = new int[n];//不同用户的不同喜爱度
        for (int i = 0; i < n; i++) {
            a[i] = scanner.nextInt();
            if (mapList.containsKey(a[i])) {
                List<Integer> list = mapList.get(a[i]);
                list.add(i);
            } else {
                List<Integer> list = new ArrayList<>();
                list.add(i);
                mapList.put(a[i], list);
            }
        }

        int q = scanner.nextInt();
        for (int i = 0; i < q; i++) {
            int s = scanner.nextInt() - 1;
            int e = scanner.nextInt() - 1;
            int k = scanner.nextInt();

            List<Integer> list = mapList.get(k);
            if (list == null) {
                System.out.println(0);
            } else {
                //拿到s-e区间内的总数
                float left = search(list, 0, list.size() - 1, s);
                int l;
                if (left < 0) {
                    l = 0;
                } else {
                    if ((left * 10)%10 == 5) {
                        l = (int) (left + 1);
                    } else {
                        l = (int) left;
                    }
                }
                float right = search(list, l, list.size() - 1, e);
                int count;
                if (right < l) {
                    count = 0;
                } else {
                    count = (int) right - l + 1;//!
                }
                System.out.println(count);
            }
        }
    }

    private static float search(List<Integer> list, int s, int e, int val) {
        if (s > e) {
            return -999;
        }

        float index = -999;

        while (s <= e) {
            int mid = (s + e) / 2;
            int midVal = list.get(mid);
            if (midVal == val) {
                return mid;
            } else if (val < midVal) {
                e = mid - 1;
                index = mid - 0.5f;
            } else {
                s = mid + 1;
                index = mid + 0.5f;
            }
        }

        return index;
    }
}

//5
//1 2 3 3 5
//3
//1 2 1
//2 4 5
//3 5 3

//5
//1 2 3 3 5
//1
//3 5 3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值