UVa11988破损的键盘
按下Home键就是光标移动到首,按下End键就是光标移动到尾。
对于表的扩充和在中间插入的问题,搬家太费时间,因此采用链表。链表的本质就是逻辑上相邻而物理上不相邻。
链表与顺序表的差异在于有一个尾巴。存储下一个元素的位置。链式存储只能顺序存取,而顺序存储可以随机存取。
链式存储两个类,一个结点类一个链表类。链表成员函数就是线性表操作,结点类成员函数相对次要。带头结点的链表可以统一插入删除操作的写法。数组写法实际上就是确定了元素所在的位置,和链表指针没有本质区别。
最后一个指针不指向NULL,直接指向头结点,构建出的就是一个循环链表。
first和last指针:
0号结点为头结点,原来的first指针就不再需要了,直接知道头结点是哪一个。仍然需要last指针,这样才知道线性表是否结束以及结束位置在哪儿,便于光标移动到尾。
光标实际上就是插入删除要找的那个p指针。专门用一个指针表示当前光标指向的元素,要插入就在光标指向的元素之后插入,要移动到首就是让光标指向头结点,要移动到尾就是让光标指向last元素。
初始化:
last = 0; // last指针直接指向头结点
data[0] = 0; // 头结点没有实际数据
link[0] = 0; // 头结点还没有指向
int cur = 0; // 当前光标指向头结点
注意:
原始字符串从0开始,而链表的0位置已经是头结点了,赋值的时候要小心。不然会凭空丢掉字母。
第一次输出全部是倒着的,为什么呢?在正常的情况时候,没有更新cur指针。正常情况下cur就是指向当前插入的这个元素,而不是last。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int MAXN = 100010;
char data[MAXN]; // 结点类的data
// 0号结点为头结点,原来的first指针就不再需要了,直接知道头结点是哪一个
// 仍然需要last指针,这样才知道线性表是否结束
int last;
int link[MAXN]; // 结点类的link
int main() {
char temp[MAXN] = {0};
while (fgets(temp, MAXN, stdin) != NULL) {
if (temp[strlen(temp) - 1] == '\n') {
temp[strlen(temp) - 1] = 0;
}
if (temp[0] == '\n' || temp[0] == '\t' || temp[0] == '\0') {
continue;
}
// printf("%s\n", temp);
last = 0; // last指针直接指向头结点
data[0] = 0; // 头结点没有实际数据
link[0] = 0; // 头结点还没有指向
int cur = 0; // 当前光标指向头结点
for (int i = 0; i < strlen(temp); i++) { // 开始处理输入的数据
if (temp[i] == '[') { // 光标移动到首
cur = 0;
} else if (temp[i] == ']') { // 光标移动到尾
cur = last;
} else { // 正常的字母,在光标指向的元素后面插入
// new结点,结点的位置就是temp数组的下标
data[i+1] = temp[i]; // 赋值小心
// printf("data[%d]=temp[%d]\n", i+1, i);
if (link[cur] == 0) { // 是最后一个元素
last = i+1; // 更新last指针
}
link[i+1] = link[cur];
// printf("link[%d]=link[%d]\n", i+1, cur);
link[cur] = i+1;
// printf("link[%d]=%d\n", cur, i+1);
cur = i+1;
}
}
// 打印结果
int p = 0; // 从头结点开始打印
while (link[p] != 0) {
int cur_element = link[p]; // 当前元素位置
printf("%c", data[cur_element]);
p = link[p]; // 往后走
}
printf("\n");
}
return 0;
}