zoukankan      html  css  js  c++  java
  • 区间并集求解算法实现

    最近开发了一个邮政发票系统,其中有个需求是这样的,发票的打印顺序已经排序好了,但是用户不是一次性全部打印,而是分段打印。比如现在有某种发票有1w条,打印顺序为1-10000,用户现在打印了4次,第一次为从1-3000,第二次为 3000-6000,第三次为7000-1000。打印完了后,用户怀疑中间有一段没有打印。又打印了5000-6500的发票,现在我要计算用户共打印了多少条发票?

    其实这个问题可以抽象为数学上计算区间的并集。

    A=[1,3000], B=[3000,6000], C=[7000,10000], D=[5000,65000]

    A∪B∪C∪D.

    这个题目用一个数轴来求解很简单,但是如果是N个区间的并集呢? 这就不是人干的了.

    现在给出算法,思路如下:

    假设算出来的集合如下:

    …[an,bn]∪[an+1 ,bn+1]∪[an+2, bn+2]∪…

    记为Z

    现在再加入一个区间 [c,d]

    1. 判断 如果c∈Z,那么再判断d,如果d不属于Z,那么我们把包含在[c,d]之间区间都去掉,并修改包含c的那个区间右端点为d;如果d属于Z,那么我们把包含c,d的区间以及他们之间的区间都合并为一个区间

    2. 判断 如果c不属于Z,那么把[c,d]加到Z中,…[am,am+1] [c,d] [am+2,am+3]…,然后从区间[c,d]开始遍历,去掉重复的部分。

    遍历集合Z的方法为,从某一个区间左端点开始,

    代码如下: 共两个类:UnionAlgorithm.java , Interval.java

    package test;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    /**
     * <pre>
     * 该类用ArrayList表示区间的集合,每一个元素表示一个区间,
     * ArrayList中元素顺序为这些元素表示的区间按照在数轴上在顺序排列,
     * 
     * 
     * </pre>
     * @author luoxian
     * @since Sep 5, 2008 4:38:09 PM
     * @version 1.0
     */
    public class UnionAlgorithm {
        /**
         * 存放结果 集合
         */
        private List value = new ArrayList();
        
        /**
         * 计算主方法
         * 
         * @param param
         * @return
         */
        public List calc(Interval[] param) {
            Interval[] interval = check(param);
            if (interval == null || interval.length == 0)
                return null;
            if (value.size() == 0 && interval.length >= 1)
                value.add(interval[0]);
            for (int i = 1; i < interval.length; i++) {
                //判断该区间的左端点在value中的位置
                int[] left_position = getPosition(value, 0, interval[i].getLeft());
                if (left_position[0] == 1) {
                    //1 左端点属于value的情况
                    int[] right_position = getPosition(value, left_position[1], interval[i].getRight());
                    if (right_position[0] == 1) {
                        //1.1 右端点也属于value
                        if (left_position[1] == right_position[1])
                            continue;
                        
                        ((Interval)value.get(left_position[1])).setRight(
                                ((Interval)value.get(right_position[1])).getRight());
                        
                        for (int j = left_position[1] + 1; j <= right_position[1]; j++) {
                            value.remove(j);
                        }
                    } else {
                        //1.2右端点不属于value
                        ((Interval)value.get(left_position[1])).setRight(interval[i].getRight());
                        for (int j = left_position[1] + 1; j < right_position[1]; j++) {
                            value.remove(j);
                        }
                    }
                } else {
                    //2 左端点不属于value
                    value.add(left_position[1], interval[i]);
                    refresh(left_position[1]);
                    
                }
            }
            
            return value;
        }
        
        public void refresh(int index) {
            Interval temp = (Interval)value.get(index);
            int[] index_right_position = getPosition(value, index + 1, temp.getRight());
            if (index_right_position[0] == 1) {
                Interval include_right = (Interval)value.get(index_right_position[1]);
                temp.setRight(include_right.getRight());
                
                for (int i = index + 1; i <= index_right_position[1]; i++) {
                    value.remove(i);
                }
            } else if (index_right_position[0] == 0){
                //删除该区间内的所有区间
                for (int i = index + 1; i < index_right_position[1]; i++) {
                    value.remove(i);
                }
            }
        }
        
        /**
         * <pre>
         * 在集合value中查找点point的位置
         * 从序列号为from开始查找
         * 
         * 返回:为一个数组,包括两个值[type,index]
         * 如果type=0,表示point点不在集合value中的任何一个区间内,该点在第index区间的前面
         * 如果type=1,表示point点在集合value中的第index的元素区间内
         * 
         * (说明:type为0的特殊情况为该点在集合中最后一个区间的后面,此时
         * 尽管value.get(value.size())并不存在,我们仍然把index赋值为value.size())
         * 
         * </pre>
         * @param value
         * @param from
         * @param point
         * @return
         */
        public int[] getPosition(List value, int from, int point) {
            if (from >= value.size())
                return new int[]{0, value.size()};
            
            if (point < ((Interval)value.get(from)).getLeft()) {
                return new int[]{0, from};
            }
            
            for (int i = from; i < value.size() - 1; i++) {
                Interval tmp = (Interval)value.get(i);
                if (tmp.isContain(point))
                    return new int[]{1,i};
                Interval tmpLater = (Interval)value.get(i + 1);
                if (point >tmp.getRight() && point < tmpLater.getLeft())
                    return new int[]{0, i + 1};
            }
            
            //比较最后一个区间
            Interval last = (Interval)value.get(value.size() - 1);
            if (last.isContain(point))
                return new int[]{1, value.size() - 1};
            else
                return new int[]{0,value.size()};
        }
        
        /**
         * 数组的检查, 数组元素右边的数不得小于右边的元素
         * @param duan
         * @return
         */
        public Interval[] check(Interval[] temp){
            //:-不破坏参数原则
            Interval[] interval = new Interval[temp.length];
            for (int i = 0; i < interval.length; i++) {
                interval[i] = new Interval(temp[i]);
            }
            //:-
            int length = interval.length;
            for (int i = 0; i < interval.length; i++) {
                if (interval[i].getRight() < interval[i].getLeft()) {
                    interval[i] = null;
                    length --;
                }
            }
            Interval[] result = new Interval[length];
            int index = 0;
            for (int i = 0; i < interval.length; i++) {
                if (interval[i] != null){
                    result[index] = interval[i];
                    index ++;
                }
            }
            return result;
        }
        
        
        public List getValue() {
            return value;
        }
        public static void main(String[] args) {
            Interval[] v = {new Interval(1,3), new Interval(4,2)};
            
            UnionAlgorithm ua = new UnionAlgorithm();
            Interval d1 = new Interval(7,9);
            Interval d2 = new Interval(5,7);
            Interval d3 = new Interval(-1,4);
            Interval d4 = new Interval(8,10);
            Interval d5 = new Interval(10,12);
            Interval d6 = new Interval(4,1);
            Interval d7 = new Interval(8,10);
            Interval d8 = new Interval(3,14);
            Interval d9 = new Interval(15,17);
            Interval c1 = new Interval(0,4545);
            Interval c2 = new Interval(32,54);
            Interval c3 = new Interval(123,456);
            Interval c4 = new Interval(34,54);
            Interval c5 = new Interval(12,23);
            
            Interval[] duan = {d1,d2,d3};
    //        Interval[] duan = {d1,d2,d3,d4,d5,d6,d7,d8,d9,c1,c2,c3,c4,c5};
            long sc = System.currentTimeMillis();
            ua.calc(duan);
            //Thread.sleep(1);
            sc = System.currentTimeMillis() - sc;
            
            System.out.println(ua.getValue() + "花费:" + sc);
            
            
        }
    }
    
    
    //下面是区间bean
    
    package test;
    
    /**
     * 表示一个区间[left, right]
    * @author luoxian
     * @since Sep 5, 2008 4:48:15 PM
     * @version 1.0
     */
    public class Interval {
        private int left;
        private int right;
        
        public Interval(Interval interval) {
            this.left = interval.getLeft();
            this.right = interval.getRight();
        }
        
        public Interval(int left, int right) {
            this.left = left;
            this.right = right;
        }
        
        public boolean isContain(int point) {
            if (point <= right && point >= left)
                return true;
            else
                return false;
        }
        
        public int getLeft() {
            return left;
        }
        public int getRight() {
            return right;
        }
        
        public void setLeft(int left) {
            this.left = left;
        }
    
        public void setRight(int right) {
            this.right = right;
        }
    
        public String toString(){
            return "[" + left + " , " + right + "]";
        }
    }
  • 相关阅读:
    求最大公约数
    计算变量
    JavaScript高级浏览器原理V8引擎js执行原理
    JavaScript小游戏按下按钮移动方块
    黑马程序员WPF中的DoubleAnimation
    黑马程序员浅谈partial class的理解
    黑马程序员C#与Javascript变量、函数之间的相互调用
    黑马程序员关于工作流的模式
    黑马程序员C#解析HTML
    黑马程序员关于System.Collections空间
  • 原文地址:https://www.cnblogs.com/gangzi2013/p/6489571.html
Copyright © 2011-2022 走看看