30002:顺序表区间插入与删除
题目
为动态整数顺序表实现区间插入和区间删除,同时保持元素次序、表示不变式和失败时状态保证。
解析
顺序表始终满足:
text
size <= capacity
capacity == 0 当且仅当 items == NULL
capacity <= SIZE_MAX / sizeof(int)1
2
3
2
3
插入区间时,先扩容,再用 memmove 把后缀整体向右移动,最后用 memcpy 写入不重叠的来源区间。删除时则把删除区间之后的后缀整体向左移动。
所有可能失败的检查都在移动元素之前完成。扩容失败时 realloc 保留原存储,因此返回 false 不会破坏原顺序表。
解析
c
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int *items;
size_t size;
size_t capacity;
} IntList;
static bool int_list_valid(const IntList *list) {
if (list == NULL || list->size > list->capacity) {
return false;
}
if ((list->capacity == 0) != (list->items == NULL)) {
return false;
}
return list->capacity <= SIZE_MAX / sizeof *list->items;
}
void int_list_init(IntList *list) {
if (list == NULL) {
return;
}
list->items = NULL;
list->size = 0;
list->capacity = 0;
}
void int_list_destroy(IntList *list) {
if (list == NULL) {
return;
}
free(list->items);
int_list_init(list);
}
static bool int_list_reserve(
IntList *list,
size_t minimum
) {
size_t maximum = SIZE_MAX / sizeof *list->items;
if (minimum > maximum) {
return false;
}
if (minimum <= list->capacity) {
return true;
}
size_t next = list->capacity == 0
? (maximum < 8 ? maximum : 8)
: list->capacity;
while (next < minimum) {
if (next > maximum / 2) {
next = maximum;
} else {
next *= 2;
}
}
int *replacement = realloc(
list->items,
next * sizeof *replacement
);
if (replacement == NULL) {
return false;
}
list->items = replacement;
list->capacity = next;
return true;
}
bool int_list_insert_range(
IntList *list,
size_t index,
const int *source,
size_t count
) {
if (!int_list_valid(list) || index > list->size) {
return false;
}
if (count == 0) {
return true;
}
if (source == NULL || count > SIZE_MAX - list->size) {
return false;
}
size_t new_size = list->size + count;
if (!int_list_reserve(list, new_size)) {
return false;
}
size_t tail_count = list->size - index;
memmove(
list->items + index + count,
list->items + index,
tail_count * sizeof *list->items
);
memcpy(
list->items + index,
source,
count * sizeof *list->items
);
list->size = new_size;
return true;
}
bool int_list_erase_range(
IntList *list,
size_t index,
size_t count
) {
if (!int_list_valid(list) || index > list->size) {
return false;
}
if (count > list->size - index) {
return false;
}
if (count == 0) {
return true;
}
size_t after = index + count;
size_t tail_count = list->size - after;
memmove(
list->items + index,
list->items + after,
tail_count * sizeof *list->items
);
list->size -= count;
return true;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
来源与改编
题型借鉴 Pat Morin 的 Open Data Structures 中数组表的增长与区间搬移思路,原作采用 CC BY 2.5 Canada。本题改写为 C 接口,并增加重叠规则、大小溢出和失败保证;完整记录见习题来源与许可。