2025A-VLAN资源池
题目描述与示例
题目描述
VLAN是一种对局域网设备进行逻辑划分的技术,为了标识不同的VLAN,引入VLAN ID(1-4094之间的整数)的概念。
定义一个VLAN ID的资源池(下称VLAN资源池),资源池中连续的VLAN用开始VLAN`` ``-`` ``结束VLAN表示;不连续的用单个整数表示,所有的VLAN用英文逗号连接起来。
现在有一个VLAN资源池,业务需要从资源池中申请一个VLAN,需要你输出从VLAN资源池中移除申请的VLAN后的资源池。
题目练习网址:https://www.algomooc.com/problem/P2555
输入描述
第一行为字符串格式的VLAN资源池,第二行为业务要申请的VLAN,VLAN的取值范围为[1,4094]之间的整数。
输出描述
从输入VLAN资源池中移除申请的VLAN后字符串格式的VLAN资源池,输出要求满足题目描述中的格式,并且按照VLAN从小到大升序输出。
如果申请的VLAN不在原VLAN资源池内,输出原VLAN资源池升序排序后的字符串即可
备注
输入VLAN资源池中VLAN的数量取值范围为[2-4094]间的整数,资源池中VLAN不重复且合法([1,4094]之间的整数),输入是乱序的。
示例一
输入
1-5
2输出
1,3-5说明
原VLAN资源池中有VLAN1、2、3、4、5,从资源池中移除2后,剩下VLAN 1、3、4、5,按照题目描述格式并升序后的结果为1,3-5。
示例二
输入
20-21,15,18,30,5-10
15输出
5-10,18,20-21,30说明
原VLAN资源池中有VLAN 5、6、7、8、9、10、15、18、20、21、30,从资源池中移除15后,资源池中剩下的VLAN为5、6、7、8、9、10、18、20、21、30,按照题目描述格式并升序后的结果为5-10,18,20-21,30。
示例三
输入
5,1-3
10输出
1-3,5说明
原VLAN资源池中有VLAN1、2、3``、``5,申请的VLAN 10不在原资源池中,将原资源池按照题目描述格式并按升序排序后输出的结果为1-3,5。
解题思路
用二元组表示区间
输入的第一行表示若干VLAN资源,如果我们用","进行分割,每个字符串有两种可能的情况。
- 单个数字,譬如
"5" - 一个区间,譬如
"1-3"
如果我们用更加方便的二元组形式来表示区间,譬如"1-3"再用"-"进行分割后,容易转换为(1, 3)。
为了保持数据的一致性,对于单个数字的情况,我们也可以用二元组来表示,譬如"5"用(5, 5)来表示。
因此,我们可以构建出如下的代码,所有输入的VLAN资源,都用二元组的区间形式来表示。
# 输入VLAN资源池字符串,用","进行分割
lst = input().split(",")
# 初始化vlan_lst列表,用于储存所有VLAN整数区间
vlan_lst = list()
# 遍历lst中的所有字符串item
for item in lst:
# 如果item不包含"-",说明item为单个整数
if "-" not in item:
idx = int(item)
vlan_lst.append((idx, idx))
# 如果item包含"-",说明item为一个区间
else:
# 将item根据"-"进行分割,并且转为两个整数
start, end = map(int, item.split("-"))
vlan_lst.append((start, end))删除某一元素后区间的变化
在确定了用二元组表示区间之后,假设我们要删除某个区间中的某一个数,我们需要做出如下的分类讨论。
- 区间
(start, end)只包含单个数字(即存在start == end成立),而删除的数字num就是区间中唯一的数字,(即存在num == start == end),那么这个区间直接不存在。
譬如删除区间
(5, 5)中的数字5,这个区间直接不存在了。
- 区间
(start, end)包含不止单个数字(即存在start < end成立),而删除的数字num是区间的左端点(即存在num == start成立),那么会删除后的新区间是(start+1, end)
譬如删除区间
(8, 15)中的数字8,新区间为(9, 15)。
- 区间
(start, end)包含不止单个数字(即存在start < end成立),而删除的数字num是区间的右端点(即存在num == end成立),那么会删除后的新区间是(start, end-1)
譬如删除区间
(8, 15)中的数字15,新区间为(8, 14)。
- 区间
(start, end)包含不止单个数字(即存在start < end成立),而删除的数字num是既非区间的左端点也非区间的右端点(即存在start < num < end成立),那么会删除后这单个区间会变成两个新的区间,分别是(start, num-1)和(num+1, end)。
譬如删除区间
(8, 15)中的数字11,新区间为(8, 10)和(12, 15)。
因此,上述关于删除的分类讨论我们可以将其加入到储存区间的过程中。其对应代码如下
# 遍历lst中的所有字符串item
for item in lst:
# 如果item不包含"-",说明item为单个整数
if "-" not in item:
idx = int(item)
# 如果idx不为要删去的数,则储存区间(idx, idx)
# 区间的开始和结尾相同,表示这个区间只包含一个数idx
if idx != vlan_remove_idx:
vlan_lst.append((idx, idx))
# 如果item包含"-",说明item为一个区间
else:
# 将item根据"-"进行分割,并且转为两个整数
start, end = map(int, item.split("-"))
# 如果要删去的是start,区间变为(start+1, end)
if vlan_remove_idx == start:
vlan_lst.append((start+1, end))
# 如果要删去的是end,区间变为(start, end-1)
elif vlan_remove_idx == end:
vlan_lst.append((start, end-1))
# 如果vlan_remove_idx位于区间(start, end)之间
# 那么原区间被划分为两个新区间(start, vlan_remove_idx-1)和(vlan_remove_idx+1, end)
elif start < vlan_remove_idx < end:
vlan_lst.append((start, vlan_remove_idx-1))
vlan_lst.append((vlan_remove_idx+1, end))
# 剩余情况,(start, end)区间中无需进行删除,直接加入区间(start, end)
else:
vlan_lst.append((start, end))将二元组形式转换回原形式
由于储存vlan_lst这个过程里的每一个区间都是无序的,输入之前我们还需要对vlan_lst进行排序。
最终输出的时候,我们还需要再将二元组区间,转换回原数据形式。
这个可以非常方便地通过推导式结合三目运算符来完成。
# 对vlan_lst数组进行排序,此时确保使用区间的start进行数字序的排序
vlan_lst.sort()
# 将vlan_lst中的二元元组区间,转换为字符串的形式,储存在列表ans中
ans = ["{}-{}".format(start, end) if start != end else "{}".format(start)
for start, end in vlan_lst]
# 将ans中的所有字符串用","连接并输出
print(",".join(ans))或者使用for循环结合if语句完成。
# 对vlan_lst数组进行排序,此时确保使用区间的start进行数字序的排序
vlan_lst.sort()
# 将vlan_lst中的二元元组区间,转换为字符串的形式,储存在列表ans中
ans = list()
for start, end in vlan_lst:
if start != end:
ans.append("{}-{}".format(start, end))
else:
ans.append("{}".format(start))
# 将ans中的所有字符串用","连接并输出
print(",".join(ans))代码
Python
# 欢迎来到「欧弟算法 - 华为OD全攻略」,收录华为OD题库、面试指南、八股文与学员案例!
# 地址:https://www.odalgo.com
# 华为OD机试刷题网站:https://www.algomooc.com
# 添加微信 278166530 获取华为 OD 笔试真题题库和视频
# 输入VLAN资源池字符串,用","进行分割
lst = input().split(",")
# 输入即将移除的VLAN id
vlan_remove_idx = int(input())
# 初始化vlan_lst列表,用于储存所有VLAN整数区间
vlan_lst = list()
# 遍历lst中的所有字符串item
for item in lst:
# 如果item不包含"-",说明item为单个整数
if "-" not in item:
idx = int(item)
# 如果idx不为要删去的数,则储存区间(idx, idx)
# 区间的开始和结尾相同,表示这个区间只包含一个数idx
if idx != vlan_remove_idx:
vlan_lst.append((idx, idx))
# 如果item包含"-",说明item为一个区间
else:
# 将item根据"-"进行分割,并且转为两个整数
start, end = map(int, item.split("-"))
# 如果要删去的是start,区间变为(start+1, end)
if vlan_remove_idx == start:
vlan_lst.append((start+1, end))
# 如果要删去的是end,区间变为(start, end-1)
elif vlan_remove_idx == end:
vlan_lst.append((start, end-1))
# 如果vlan_remove_idx位于区间(start, end)之间
# 那么原区间被划分为两个新区间(start, vlan_remove_idx-1)和(vlan_remove_idx+1, end)
elif start < vlan_remove_idx < end:
vlan_lst.append((start, vlan_remove_idx-1))
vlan_lst.append((vlan_remove_idx+1, end))
# 剩余情况,(start, end)区间中无需进行删除,直接加入区间(start, end)
else:
vlan_lst.append((start, end))
# 对vlan_lst数组进行排序,此时确保使用区间的start进行数字序的排序
vlan_lst.sort()
# 将vlan_lst中的二元元组区间,转换为字符串的形式,储存在列表ans中
ans = ["{}-{}".format(start, end) if start != end else "{}".format(start)
for start, end in vlan_lst]
# 将ans中的所有字符串用","连接并输出
print(",".join(ans))Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// 输入VLAN资源池字符串,用","进行分割
String[] lst = scanner.nextLine().split(",");
// 输入即将移除的VLAN id
int vlanRemoveIdx = scanner.nextInt();
// 初始化vlanList列表,用于储存所有VLAN整数区间
List<int[]> vlanList = new ArrayList<>();
// 遍历lst中的所有字符串item
for (String item : lst) {
// 如果item不包含"-",说明item为单个整数
if (!item.contains("-")) {
int idx = Integer.parseInt(item);
// 如果idx不为要删去的数,则储存区间(idx, idx)
if (idx != vlanRemoveIdx) {
vlanList.add(new int[] { idx, idx });
}
} else {
// 如果item包含"-",说明item为一个区间
String[] range = item.split("-");
int start = Integer.parseInt(range[0]);
int end = Integer.parseInt(range[1]);
// 如果要删去的是start,区间变为(start+1, end)
if (vlanRemoveIdx == start) {
vlanList.add(new int[] { start + 1, end });
}
// 如果要删去的是end,区间变为(start, end-1)
else if (vlanRemoveIdx == end) {
vlanList.add(new int[] { start, end - 1 });
}
// 如果vlanRemoveIdx位于区间(start, end)之间
// 则划分为两个新区间
else if (start < vlanRemoveIdx && vlanRemoveIdx < end) {
vlanList.add(new int[] { start, vlanRemoveIdx - 1 });
vlanList.add(new int[] { vlanRemoveIdx + 1, end });
}
// 其余情况,区间无需删除,直接加入区间
else {
vlanList.add(new int[] { start, end });
}
}
}
// 对vlanList数组进行排序,确保使用区间的start进行数字序的排序
vlanList.sort(Comparator.comparingInt(a -> a[0]));
// 将vlanList中的区间转换为字符串的形式,储存在列表ans中
List<String> ans = new ArrayList<>();
for (int[] range : vlanList) {
if (range[0] == range[1]) {
ans.add(String.valueOf(range[0]));
} else {
ans.add(range[0] + "-" + range[1]);
}
}
// 将ans中的所有字符串用","连接并输出
System.out.println(String.join(",", ans));
scanner.close();
}
}C++
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <algorithm>
using namespace std;
int main() {
string input;
getline(cin, input);
// 输入VLAN资源池字符串,用","进行分割
stringstream ss(input);
string segment;
vector<string> lst;
while (getline(ss, segment, ',')) {
lst.push_back(segment);
}
// 输入即将移除的VLAN id
int vlanRemoveIdx;
cin >> vlanRemoveIdx;
// 初始化vlanList列表,用于储存所有VLAN整数区间
vector<pair<int, int>> vlanList;
// 遍历lst中的所有字符串item
for (const string& item : lst) {
// 如果item不包含"-",说明item为单个整数
if (item.find('-') == string::npos) {
int idx = stoi(item);
// 如果idx不为要删去的数,则储存区间(idx, idx)
if (idx != vlanRemoveIdx) {
vlanList.push_back({ idx, idx });
}
} else {
// 如果item包含"-",说明item为一个区间
int start, end;
sscanf(item.c_str(), "%d-%d", &start, &end);
// 如果要删去的是start,区间变为(start+1, end)
if (vlanRemoveIdx == start) {
vlanList.push_back({ start + 1, end });
}
// 如果要删去的是end,区间变为(start, end-1)
else if (vlanRemoveIdx == end) {
vlanList.push_back({ start, end - 1 });
}
// 如果vlanRemoveIdx位于区间(start, end)之间
// 则划分为两个新区间
else if (start < vlanRemoveIdx && vlanRemoveIdx < end) {
vlanList.push_back({ start, vlanRemoveIdx - 1 });
vlanList.push_back({ vlanRemoveIdx + 1, end });
}
// 其余情况,区间无需删除,直接加入区间
else {
vlanList.push_back({ start, end });
}
}
}
// 对vlanList数组进行排序,确保使用区间的start进行数字序的排序
sort(vlanList.begin(), vlanList.end());
// 将vlanList中的区间转换为字符串的形式,并储存在列表ans中
vector<string> ans;
for (const auto& range : vlanList) {
if (range.first == range.second) {
ans.push_back(to_string(range.first));
} else {
ans.push_back(to_string(range.first) + "-" + to_string(range.second));
}
}
// 将ans中的所有字符串用","连接并输出
for (size_t i = 0; i < ans.size(); ++i) {
if (i > 0) cout << ",";
cout << ans[i];
}
cout << endl;
return 0;
}C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define MAX_SEGMENTS 1000
#define MAX_STR_LEN 20
// 定义区间结构体
typedef struct {
int start;
int end;
} Interval;
int compare(const void *a, const void *b) {
Interval *ia = (Interval *)a;
Interval *ib = (Interval *)b;
return ia->start - ib->start;
}
int main() {
char input[10000];
fgets(input, sizeof(input), stdin);
input[strcspn(input, "\n")] = '\0'; // 去除换行符
// 输入即将移除的VLAN id
int vlanRemoveIdx;
scanf("%d", &vlanRemoveIdx);
// 分割输入字符串,用","分割
char *segments[MAX_SEGMENTS];
int segmentCount = 0;
char *token = strtok(input, ",");
while (token != NULL) {
segments[segmentCount++] = token;
token = strtok(NULL, ",");
}
// 初始化vlanList数组,用于储存所有VLAN整数区间
Interval vlanList[MAX_SEGMENTS];
int vlanCount = 0;
for (int i = 0; i < segmentCount; i++) {
char *item = segments[i];
if (strchr(item, '-') == NULL) {
// 处理单个数字
int idx = atoi(item);
if (idx != vlanRemoveIdx) {
vlanList[vlanCount++] = (Interval){idx, idx};
}
} else {
// 处理区间
int start, end;
sscanf(item, "%d-%d", &start, &end);
if (vlanRemoveIdx == start) {
vlanList[vlanCount++] = (Interval){start + 1, end};
} else if (vlanRemoveIdx == end) {
vlanList[vlanCount++] = (Interval){start, end - 1};
} else if (start < vlanRemoveIdx && vlanRemoveIdx < end) {
vlanList[vlanCount++] = (Interval){start, vlanRemoveIdx - 1};
vlanList[vlanCount++] = (Interval){vlanRemoveIdx + 1, end};
} else {
vlanList[vlanCount++] = (Interval){start, end};
}
}
}
// 按起始值排序区间
qsort(vlanList, vlanCount, sizeof(Interval), compare);
// 输出处理后的区间字符串
for (int i = 0; i < vlanCount; i++) {
if (i > 0) printf(",");
if (vlanList[i].start == vlanList[i].end) {
printf("%d", vlanList[i].start);
} else {
printf("%d-%d", vlanList[i].start, vlanList[i].end);
}
}
printf("\n");
return 0;
}Node JavaScript
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
let inputLines = [];
rl.on('line', (line) => {
inputLines.push(line.trim());
if (inputLines.length === 2) {
processInput();
rl.close();
}
});
function processInput() {
// 输入VLAN资源池字符串,用","进行分割
const lst = inputLines[0].split(",");
// 输入即将移除的VLAN id
const vlanRemoveIdx = parseInt(inputLines[1]);
const vlanList = [];
// 遍历lst中的所有字符串item
for (const item of lst) {
if (!item.includes("-")) {
// 单个整数
const idx = parseInt(item);
if (idx !== vlanRemoveIdx) {
vlanList.push([idx, idx]);
}
} else {
// 区间字符串
const [startStr, endStr] = item.split("-");
const start = parseInt(startStr);
const end = parseInt(endStr);
if (vlanRemoveIdx === start) {
vlanList.push([start + 1, end]);
} else if (vlanRemoveIdx === end) {
vlanList.push([start, end - 1]);
} else if (start < vlanRemoveIdx && vlanRemoveIdx < end) {
vlanList.push([start, vlanRemoveIdx - 1]);
vlanList.push([vlanRemoveIdx + 1, end]);
} else {
vlanList.push([start, end]);
}
}
}
// 排序,按区间起始值排序
vlanList.sort((a, b) => a[0] - b[0]);
// 格式化输出
const ans = vlanList.map(([start, end]) =>
start === end ? `${start}` : `${start}-${end}`
);
console.log(ans.join(","));
}Go
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
"strings"
)
// 定义区间结构体
type Interval struct {
start, end int
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
// 读取第一行:VLAN资源池字符串
scanner.Scan()
line := scanner.Text()
lst := strings.Split(line, ",")
// 读取第二行:要移除的VLAN ID
scanner.Scan()
vlanRemoveIdx, _ := strconv.Atoi(scanner.Text())
var vlanList []Interval
// 遍历每个元素进行解析
for _, item := range lst {
if !strings.Contains(item, "-") {
// 单个整数
idx, _ := strconv.Atoi(item)
if idx != vlanRemoveIdx {
vlanList = append(vlanList, Interval{idx, idx})
}
} else {
// 区间
parts := strings.Split(item, "-")
start, _ := strconv.Atoi(parts[0])
end, _ := strconv.Atoi(parts[1])
if vlanRemoveIdx == start {
vlanList = append(vlanList, Interval{start + 1, end})
} else if vlanRemoveIdx == end {
vlanList = append(vlanList, Interval{start, end - 1})
} else if start < vlanRemoveIdx && vlanRemoveIdx < end {
vlanList = append(vlanList, Interval{start, vlanRemoveIdx - 1})
vlanList = append(vlanList, Interval{vlanRemoveIdx + 1, end})
} else {
vlanList = append(vlanList, Interval{start, end})
}
}
}
// 排序,按start从小到大
sort.Slice(vlanList, func(i, j int) bool {
return vlanList[i].start < vlanList[j].start
})
// 输出格式化字符串
var result []string
for _, interval := range vlanList {
if interval.start == interval.end {
result = append(result, fmt.Sprintf("%d", interval.start))
} else {
result = append(result, fmt.Sprintf("%d-%d", interval.start, interval.end))
}
}
fmt.Println(strings.Join(result, ","))
}时空复杂度
时间复杂度:O(``N``)。N为区间数目,需要遍历所有N个区间。
空间复杂度:O(``N``)。列表所占空间。
