合並IP地址


合並IP地址

問題描述

給定一組IPv4地址,其中每個元素可能由單個IP構成(例如192.168.0.1),也可能由一個IP段構成(如192.168.0.10 - 192.168.0.15),請將給定的IP合並。

輸入例子

		{"192.168.0.1",
        "192.168.0.12-192.168.0.15",
        "192.168.0.2",
        "192.168.0.7-192.168.0.9",
        "192.168.0.11",
        "192.168.0.3-192.168.0.5",
        "192.168.0.16",
        "192.168.0.100"}

輸出例子

		{"192.168.0.1-192.168.0.5",
		"192.168.0.7-192.168.0.9",
		"192.168.0.11-192.168.0.16",
		"192.168.0.100"}

這個問題是基於

LC56區間合並問題

但問題是首先需要將IP地址區間轉換為可比較的區間,但注意到Java中Int類型最大只能表示31位的正數,並不能完整的表示一個32位IP地址,而Java並沒有提供無符號數,所以存儲IP地址,並且用來比較大小,需要用到long類型,輸出要求是同樣的IP格式,所以涉及到IP地址的Stringlong的相互轉換。

StringToLong

顯而易見的是,32位的IP地址由.分割為256進制的四個8位的字段,當轉換成數字時,可以從右往左逐乘相加。

public static long ipToLong(String ip) {
        String ips[] = ip.split("\\.");
		long ipLong = 0;
    
        for (int i = 0; i < 0; i++) {
            ipLong = ipLong * 256 + Long.parseLong(ips[i]);
        }

        return ipLong;
}

但是存在一種效率更高的方法,就是對於每個字段,我們進行移位操作,然后再做一個|操作,比如對於192.168.19.1這個地址來說,轉換成二進制即11000000 10101000 00010011 00000001 192是字段的高8位,

我們將IP按.分割為大小為4的數組后,將第一個數字,左移24位,即得到11000000 00000000 00000000 00000000,同樣將第二個數字左移16位,得到10101000 00000000 00000000,以此類推,將第三個數字左移8位得到00010011 00000000,最低的八位不需要移位,然后將四個結果進行操作,得到的就是IP地址的十進制形式。具體實現形式如下

public static long ipToLong(String ip) {
        String ips[] = ip.split("\\.");

        long ipLong = (Long.parseLong(ips[0]) << 24) |
                      (Long.parseLong(ips[1]) << 16) |
                      (Long.parseLong(ips[2]) << 8) |
                      (Long.parseLong(ips[3]));

        return ipLong;
    }

或者寫成循環

   public static long ipToLong(String ip) {
        String ips[] = ip.split("\\.");
		
        long ipLong = 0;
        for (int i = 0; i < 4; i++) {
            ipLong |= Long.parseLong(ips[i]) << (24 - 8 * i);
        }

        return ipLong;
    }

LongToString

這里同樣可以貫徹上面對位運算的思想,依舊使用上面的栗子IP地址192.168.19.1的數字表示為ip = 3232240385,我們將long類型從左往右逐步取8位,對於高8位只需要,將long類型的IP地址右移24位即可(這里注意一點,如果是32位的int類型進行轉換時需要使用無符號位移>>>),即ip>>24,而要取到從左往右第二個8bit時,我們可以將ip的高8位置零,具體做法0xffffff & ip,然后右移16位即可,同樣取第三個8bit,可以將高16位置零,然后右移8位即可,最后只需將高24位置零可以得到低8位,然后中間假如分隔符拼接得到String類型的IP地址,具體做法如下。

public static String ipToString(Long ip) {
        StringBuilder sb = new StringBuilder();

        sb.append(String.valueOf(ip >> 24));
        sb.append(".");
        sb.append(String.valueOf((0xffffff & ip) >> 16));
        sb.append(".");
        sb.append(String.valueOf((0xffff & ip) >> 8));
        sb.append(".");
        sb.append(String.valueOf(0xff & ip));

        return sb.toString();
}

也可以寫成循環

public static String ipToStringCycle(long ip) {
        String ipString[] = new String[4];

        for (int i = 0; i < 4; i++) {
            //取long類型ip地址的后32位
            long and = ip & (0xffffffff >>> (i * 8));
            //取上面結果的高8位
            ipString[i] = String.valueOf(and >> ((3 - i) * 8));
        }

        return String.join(".", ipString);
}

區間合並

接下來看如何將區間進行合並操作,首先將給定的數組按左邊界進行排序操作,然后遍歷排好序的二維數組(數組中每一維兩個元素代表一個區間的左右端點)此時

  1. 如果當前區間的左端點在數組arr中最后一個區間的右端點之后,那么它們不會重合,我們可以直接將這個區間加入數組arr的末尾;

  2. 否則,它們重合,我們需要用當前區間的右端點更新數組arr中最后一個區間的右端點,將其置為二者的較大值。

這樣我們就可以在只遍歷一次O(n)時間復雜度下將區間合並。

public static long[][] mergeSection(long[][] arr) {
    Arrays.sort(arr, (a, b) -> (int)(a[0] - b[0]));

    int  n = arr.length;
    List<long[]> list = new ArrayList<>();

    for (int i = 0; i < n; i++) {
        long left = arr[i][0], right = arr[i][1];
        //[a,b]區間和[b + 1, c]區間可以構成[a,c]區間
        //所以在遍歷[a,b]時,只需要判斷是否b >= b-1即可
        while (i < n - 1 && right  >= arr[i + 1][0] - 1) {
            right = Math.max(right, arr[i + 1][1]);
            i++;
        }
        list.add(new long[]{left, right});
    }
    
    return list.toArray(new long[list.size()][2]);
}

完整代碼

public class IPMerge {
    public static void main(String[] args) {
        String[] ss = {"192.168.0.1",
                "192.168.0.12-192.168.0.15",
                "192.168.0.2",
                "192.168.0.7-192.168.0.9",
                "192.168.0.11",
                "192.168.0.3-192.168.0.5",
                "192.168.0.16",
                "192.168.0.100"};
        List<String> list = new ArrayList<>();
        for (int i = 0; i < ss.length; i++) {
            list.add(ss[i]);
        }


        List<String> res = merge(list);

        for (String s : res) {
            System.out.println(s);
        }
    }

    public static List<String> merge (List<String> input) {
        // write code here
        int n = input.size();
        List<String> res = new ArrayList<>();
        long[][] arr = new long[n][2];

        for (int i = 0; i < n; i++) {
            String ipStr = input.get(i);
            long ip1, ip2;
            int index = ipStr.indexOf('-');
            if (index >= 0) {
                ip1 = ipToLong(ipStr.substring(0, index));
                ip2 = ipToLong(ipStr.substring(index + 1, ipStr.length()));
            } else {
                ip1 = ipToLong(ipStr.substring(0, ipStr.length()));
                ip2 = ip1;
            }
            arr[i][0] = ip1;
            arr[i][1] = ip2;
        }

        arr = mergeSection(arr);

        for (long[] a : arr) {
            if (a[0] == a[1]) {
                res.add(ipToString(a[0]));
            } else {
                String s = ipToString(a[0]) + "-" + ipToString(a[1]);
                res.add(s);
            }
        }

        return res;
    }

    public static long[][] mergeSection(long[][] arr) {
        Arrays.sort(arr, (a, b) -> (int)(a[0] - b[0]));

        int  n = arr.length;
        List<long[]> list = new ArrayList<>();

        for (int i = 0; i < n; i++) {
            long left = arr[i][0], right = arr[i][1];
            //[a,b]區間和[b + 1, c]區間可以構成[a,c]區間
            //所以在遍歷[a,b]時,只需要判斷是否b >= b-1即可
            while (i < n - 1 && right  >= arr[i + 1][0] - 1) {
                right = Math.max(right, arr[i + 1][1]);
                i++;
            }
            list.add(new long[]{left, right});
        }

        return list.toArray(new long[list.size()][2]);
    }

    public static long ipToLong(String ip) {
        String ips[] = ip.split("\\.");

        long ipLong = 0;
        for (int i = 0; i < 4; i++) {
            ipLong |= Long.parseLong(ips[i]) << (24 - 8 * i);
        }
        
        return ipLong;
    }

    public static String ipToString(long ip) {
        String ipString[] = new String[4];

        for (int i = 0; i < 4; i++) {
            //取long類型ip地址的后32位
            long and = ip & (0xffffffff >>> (i * 8));
            //取上面結果的高8位
            ipString[i] = String.valueOf(and >> ((3 - i) * 8));
        }

        return String.join(".", ipString);
    }
}


免責聲明!

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



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