查看: 4885| 回复: 8
跳转到指定楼层
上一主题 下一主题
收起左侧

[二分/排序/搜索] 空气床面经题ip2cidr C++实现

全局:

注册一亩三分地论坛,查看更多干货!

您需要 登录 才可以下载或查看附件。没有帐号?注册账号

x
最近看版上空气床的面经,发现大家经常提到一个ip2cidr的题目,找不到代码,于是自己现实了一把,供大家讨论参考。

题目:给一个IP地址范围,比如"10.10.1.1" - "10.12.235.12",转换成其CIDR表示。CIDR(classless inter-domain routing)的定义可以自行google,这题的主要意思就是转换成一系列的CIDR地址,刚好覆盖给出的IP地址范围(不多不少)。

算法:
从一个简单的例子开始:
比如IP地址范围是 0.0.0.111 - 0.0.0.120,对应的二进制表示如下
1101111   - 111
1110000   - 112
   ...
1110111   - 119
1111000   - 120

显然 0.0.0.(11000000)/24可以覆盖整个地址区间。但是这个CIDR不是一个“合格”的CIDR,因为它的范围太大了,超出了输入的IP地址范围,所以我们要进一步缩小。

缩小的办法是把这个最大的CIDR对半分得到两个CIDR:
- 0.0.0.(11000000)/24
- 0.0.0.(11100000)/25
我们可以把输入的IP地址范围分到这两个CIDR中去,然后再递归求解,直到CIDR恰好覆盖IP区间,则得到合格的CIDR。

具体代码

  1. uint32_t IpToInt(string &ip) {
  2.         vector<string> segments;
  3.         boost::split(segments, ip, [](char ch) {return ch == '.'; });
  4.         uint32_t res = 0;
  5.         uint32_t mul = 1;
  6.         for (int i = 0; i < segments.size(); ++i) {
  7.                 res = res << 8; //every time left shift 8 bits
  8.                 res += boost::lexical_cast<uint32_t>(segments[i]);
  9.         }
  10.         return res;
  11. }

  12. string IntToIp(uint32_t ip) {
  13.         string res;
  14.         for (int i = 0; i < 4; ++i) {
  15.                 uint32_t mod = ip % 256;
  16.                 res = to_string(mod) + (i != 0 ? ".": "") + res;
  17.                 ip = ip >> 8;
  18.         }
  19.         return res;
  20. }

  21. void helper(uint32_t start, uint32_t end, uint32_t prefix, int prefix_length, vector<string> &ans) {
  22.         uint32_t range_start = prefix;
  23.         uint32_t range_end = prefix + ((uint64_t)1 << (32 - prefix_length)) - 1;
  24.         if (start == range_start && end == range_end) {
  25.                 string cidr;
  26.                 cidr = IntToIp(prefix);
  27.                 cidr.append("/");
  28.                 cidr.append(to_string(prefix_length));
  29.                 ans.push_back(cidr);
  30.                 return;
  31.         }
  32.         uint32_t prefix_1 = prefix + (1 << (31 - prefix_length));
  33.         if (end < prefix_1) {
  34.                 helper(start, end, prefix, prefix_length + 1, ans);
  35.         }
  36.         else if(start >= prefix_1){
  37.                 helper(start, end, prefix_1, prefix_length + 1, ans);
  38.         }
  39.         else {
  40.                 helper(start, prefix_1 - 1, prefix, prefix_length + 1, ans);
  41.                 helper(prefix_1, end, prefix_1, prefix_length + 1, ans);
  42.         }
  43. }

  44. void IpToCidr(string start_ip, string end_ip, vector<string> &ans) {
  45.         uint32_t start = IpToInt(start_ip);
  46.         uint32_t end = IpToInt(end_ip);
  47.         int prefix_length = 0;
  48.         while (prefix_length <= 31) {
  49.                 uint32_t mask = 1 << (31 - prefix_length);
  50.                 if (mask & end) {
  51.                         break;
  52.                 }
  53.                 else {
  54.                         ++prefix_length;
  55.                 }
  56.         }
  57.         helper(start, end, 0, prefix_length, ans);
  58. }
复制代码

评分

参与人数 3大米 +18 收起 理由
Jerry_37 + 10 很有用的信息!
yoyou1988 + 5 很有用的信息!
mayfieldcr + 3 good

查看全部评分


上一篇:求问lc 371为什么这种做法java和c都能过但python过不了
下一篇:寻找一起刷题的队友
🔗
storypku 2017-10-23 11:15:41 | 只看该作者
全局:
这种题目用递归有点杀鸡牛刀哈。

#include <string>
#include <cstdint>
#include <iostream>

using namespace std;

class Solution {
public:
   void cidr(const string& start_ip, const string& end_ip, vector<string>& result) {
        uint32_t start = aton(start_ip), end = aton(end_ip);
        while (start <= end) {
            uint32_t val = lsb(start);
            val = std::min(val, end - start + 1);
            int pos = indexOf(val);
            int mask = 32 - pos;
            result.push_back(ntoa(start) + "/" + std::to_string(mask));
            start += 1 << pos;
        }
    }
private:
    static constexpr int N = 4;
private:
    uint32_t aton(const string& ip) {
        uint32_t result = 0, val = 0;
        int n = ip.size();
        for (int i = 0; i <= n; ++i) {
            if (i == n || ip[i] == '.') {
                result = (result << 8) + val;
                val = 0;
            } else {
                val = 10 * val + ip[i] - '0';
            }
        }
        return result;  
    }
   
    string ntoa(uint32_t ip) {
        string s;
        for (int i = N - 1; i >= 0; --i) {
            s  = (i != 0 ? "." : "") + std::to_string(ip % 256) + s;
            ip >>= 8;
        }
        return s;
    }
   
    int indexOf(uint32_t num) {
        int count = 0;
        for (/*NOOP*/; num != 1; num >>= 1) {
            count++;
        }
        return count;
    }
   
    uint32_t msb(uint32_t num) {
        uint32_t mask = 1U << 31;
        while (! num & mask) {
            mask >>= 1;
        }
        return mask;
    }
   
    uint32_t lsb(uint32_t num) {
        return num & (-num);
    }
};

int main() {
    vector<string> result;
    Solution sol;
    sol.cidr("10.5.2.10", "10.5.2.209", result);
    for (auto& c : result) {
        cout << c << " ";
    }
    return 0;
}
回复

使用道具 举报

🔗
 楼主| southriver 2018-7-9 05:47:22 | 只看该作者
全局:
自己顶一下,发现当年用C++刷题简直太实诚了
回复

使用道具 举报

🔗
magicsets 2018-7-9 08:30:22 | 只看该作者
全局:
这题最好是用位运算,包括__builtin_clz和__bulitin_ctz函数的使用,这两条指令在x86架构的处理器上一般都有硬件实现。

很多情况下,__builtin_ctz(Count Trailing Zeros)的功能可以用x & -x这样的写法替代。

参考这里:http://www.cnblogs.com/grandyang/p/8440087.html

上面的代码也还不是最优的,因为有个内while循环:while (step > n) step /= 2;
这个循环本质上是要找n最高位1,可以用__builtin_clz改写。
回复

使用道具 举报

🔗
better1016 2018-9-24 22:00:06 | 只看该作者
全局:
和 乐扣七五一 是一样的原题吧??
回复

使用道具 举报

🔗
lisun97 2019-8-11 20:59:17 | 只看该作者
全局:
southriver 发表于 2018-7-9 05:47
自己顶一下,发现当年用C++刷题简直太实诚了

哈哈哈哈为什么这么说?求详解!
回复

使用道具 举报

🔗
anders 2020-1-6 02:01:26 | 只看该作者
全局:
谢谢分享!有收获!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表