N个区间求交集

      博主遇​​到一个问题,要对文章根据用户阅读记录进行去重,但用户阅读记录的文章ID最长可以达到300条,然后在数据库中使用NOT  IN语句在查询时对文章进行去重,但是这样操作在记录比较长时,语句执行效率极其低下,

       最终博主想到了一个优化策略,在redis中缓存用户阅读的文章ID区间(文章ID是递增方式存入数据库)取代之间对文章ID校验去重的方式进行去重,这时就涉及到对用户的阅读文章ID区间进行求交集的操作,具体求交集思路见代码:

import java.util.*;


public class Main {

    public static boolean union(Set<String> set) {
        /*求并集  每次将两个可并集的区间合并*/
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            String old = it.next();
            String[] arr = old.split(",");
            Iterator<String> it2 = set.iterator();
            while (it2.hasNext()) {
                String newstr = it2.next();
                String[] arr2 = newstr.split(",");
                if (!(arr[0].equals(arr2[0]) && arr[1].equals(arr2[1]))) {
                    //标记是否被合并
                    boolean isRemove = false;
                    //是否两个区间有交集 思路:如果没交集 区间二一定在区间一两边
                    if ((Long.parseLong(arr2[1]) - Long.parseLong(arr[0])) > -1 && (Long.parseLong(arr2[0]) - Long.parseLong(arr[1])) < 1) {
                        //如果区间一的终点小于区间二的终点 将区间二的终点代替区间一的终点
                        if (Long.parseLong(arr[1]) < Long.parseLong(arr2[1])) {
                            arr[1] = arr2[1];
                            isRemove = true;
                        }
                        //如果区间一的起点大于区间二的起点 将区间二的起点代替区间一的起点
                        if (Long.parseLong(arr[0]) > Long.parseLong(arr2[0])) {
                            arr[0] = arr2[0];
                            isRemove = true;
                        }
                        set.remove(newstr);
                        if (isRemove) {
                            //如果区间一的值被改变 修改区间一
                            set.remove(old);
                            set.add(arr[0] + "," + arr[1]);
                        }
                        return true;
                    }
                }
            }
        }
        return false;
    }

    public static void main(String[] args) {
      /*测试运行效率*/
        Random random = new Random();
        Set<String> set = new HashSet<String>();
        for (int i = 0; i < 100000; i++) {
            String insStr = "";
            int num = random.nextInt(10000000);
            insStr = random.nextInt(num) + "," + num;
            set.add(insStr);
        }
        /*测试准确性*/
        set.add("0,15");
        set.add("8,15");
        set.add("6,7");
        set.add("100,150");
        set.add("12,85");
        set.add("4,10");
        set.add("5,16");

        System.out.println("0.0 " + new Date().getTime());
        while (union(set)) {
        }
        System.out.println("-.- " + new Date().getTime());
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            System.out.println(it.next().toString());
        }
}

优化代码:后期博主需要获取最大值 并对过小的值进行合并整合  故而优化方法如下(该优化弃用) ;

/**
     * 合并所有集合 并返回最大值
     * @param set
     * @param max
     * @return
     */
    public static Long union(Set<String> set ,Long max) {
        /*求并集  每次将两个可并集的区间合并*/
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            String old = it.next();
            String[] arr = old.split(",");
            Iterator<String> it2 = set.iterator();
            if (Long.parseLong(arr[1]) > max) {
                max = Long.parseLong(arr[1]);
            }
            while (it2.hasNext()) {
                String newstr = it2.next();
                String[] arr2 = newstr.split(",");
                if (!(arr[0].equals(arr2[0]) && arr[1].equals(arr2[1]))) {
                    //标记是否被合并
                    boolean isRemove = false;
                    //是否两个区间有交集 思路:如果没交集 区间二一定在区间一两边
                    if ((Long.parseLong(arr2[1]) - Long.parseLong(arr[0])) > -1 && (Long.parseLong(arr2[0]) - Long.parseLong(arr[1])) < 1) {
                        //如果区间一的终点小于区间二的终点 将区间二的终点代替区间一的终点
                        if (Long.parseLong(arr[1]) < Long.parseLong(arr2[1])) {
                            arr[1] = arr2[1];
                            isRemove = true;
                        }
                        //如果区间一的起点大于区间二的起点 将区间二的起点代替区间一的起点
                        if (Long.parseLong(arr[0]) > Long.parseLong(arr2[0])) {
                            arr[0] = arr2[0];
                            isRemove = true;
                        }
                        set.remove(newstr);
                        if (isRemove) {
                            //如果区间一的值被改变 修改区间一
                            set.remove(old);
                            set.add(arr[0] + "," + arr[1]);
                        }
                        max = union(set , max);
                        return max;
                    }
                }
            }
        }
        return max;
    }

博主对上面的代码进行大数据(10W+区间)测试后,发现会出现堆栈错误,递归调用太多,线程池满了;

重新优化代码后 耦合度增加  但减少运算次数

import java.util.*;


public class Main {

    /**
     * 合并所有集合 并返回最大值
     *
     * @param set
     * @param max
     * @return
     */
    public static Long union(Set<String> set, Long max) {
        /*求并集  每次将两个可并集的区间合并*/
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            String old = it.next();
            String[] arr = old.split(",");
            Iterator<String> it2 = set.iterator();
            if (Long.parseLong(arr[1]) > max) {
                max = Long.parseLong(arr[1]);
            }
            while (it2.hasNext()) {
                String newstr = it2.next();
                String[] arr2 = newstr.split(",");
                if (!(arr[0].equals(arr2[0]) && arr[1].equals(arr2[1]))) {
                    //标记是否被合并
                    boolean isRemove = false;
                    //是否两个区间有交集 思路:如果没交集 区间二一定在区间一两边
                    if ((Long.parseLong(arr2[1]) - Long.parseLong(arr[0])) > -1 && (Long.parseLong(arr2[0]) - Long.parseLong(arr[1])) < 1) {
                        //如果区间一的终点小于区间二的终点 将区间二的终点代替区间一的终点
                        if (Long.parseLong(arr[1]) < Long.parseLong(arr2[1])) {
                            arr[1] = arr2[1];
                            isRemove = true;
                        }
                        //如果区间一的起点大于区间二的起点 将区间二的起点代替区间一的起点
                        if (Long.parseLong(arr[0]) > Long.parseLong(arr2[0])) {
                            arr[0] = arr2[0];
                            isRemove = true;
                        }
                        set.remove(newstr);
                        if (isRemove) {
                            //如果区间一的值被改变 修改区间一
                            set.remove(old);
                            set.add(arr[0] + "," + arr[1]);
                        }
//                        max = union(set , max);
                        return max;
                    }
                }
            }
        }
        return -1L;
    }

    public static void removeSmall(Set<String> set) {
        Iterator<String> it = set.iterator();
        Long min = 0L;
        Long max = 0L;
        while (it.hasNext()) {

        }
    }

    public static void main(String[] args) {
        /*测试运行效率*/
        Random random = new Random();
        Set<String> set = new HashSet<String>();
        for (int i = 0; i < 100000; i++) {
            String insStr = "";
            int num = random.nextInt(10000000);
            insStr = random.nextInt(num) + "," + num;
            set.add(insStr);
        }
        /*测试准确性*/
        set.add("0,15");
        set.add("8,15");
        set.add("6,7");
        set.add("100,200");
        set.add("12,85");
        set.add("4,10");
        set.add("5,16");

        System.out.println("0.0 " + new Date().getTime());
        Long max = 0L;
        for(Long tempMax=max; (tempMax = union(set, max)) >= 0 ; )
        {
            max = tempMax;
        }
        System.out.println("MAX = " + max);
        System.out.println("-.- " + new Date().getTime());
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            System.out.println(it.next().toString());
        }
    }
}

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值