區間並集求解算法實現


最近開發了一個郵政發票系統,其中有個需求是這樣的,發票的打印順序已經排序好了,但是用戶不是一次性全部打印,而是分段打印。比如現在有某種發票有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 + "]";
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM