1.
将 2 个递增的有序链表合并为一个递增的有序链表; 要求结果链表仍然使用两个链表的存储空间, 不另外占用其他的存储空间, 表中不允许有重复的数据
void mergeLinkLikt(LinkList *La, LinkList *Lb, LinkList *Lc) {
LinkList pa = (*La)->next;
LinkList pb = (*Lb)->next;
LinkList pc;
// 这里的 Lc 是外部传进来的空链表, 先指向 La 的头结点, 然后在 while 循环中逐步移动
// 利用结点 pc 的移动, 将每次比较后的较小结点逐步链入进去
*Lc = pc = *La;
while (pa && pb) {
if (pa->data < pb->data) {
// 移动 pc 至小的结点 pa, 然后继续对 pc 的 next 与 pb 进行比较
pc->next = pa;
pc = pa;
pa = pa->next;
}else if (pa->data > pb->data) {
// 移动 pc 至小的结点 pb, 然后继续对 pc 的 next 与 pa 进行比较
pc->next = pb;
pc = pb;
pb = pb->next;
}else{
// 大小相等的情况, 按照 pa 较小的情况处理, 然后删除当前 pb 结点
// 继续使用 pa 的 next 与 pb 的 next 执行重复步骤
pc->next = pa;
pc = pa;
pa = pa->next;
LinkList temp = pb;
pb = pb->next;
free(temp);
}
}
// 循环结束后, 将不为空的结点的后续加点链接到链表中
pc->next = pa ? pa : pb;
free(*Lb);
}
2.
已知 2 个链表 A 和 B 分别表示两个集合, 其元素递增排列, 合集一个算法, 用于求出 A 与 B 的交集, 并存储在 A 链表中; 例如: La = {2, 4, 6, 8}, Lb = {4, 6, 8, 10}, Lc = {4, 6, 8}
// 类似于上面第 1 道题的遍历方式
// 不同点在于, 这里只保留交集, 就是只有在相等的情况下才将结点链入表中, 其余情况直接释放
void intersectionOfLinkList(LinkList *La, LinkList *Lb, LinkList *Lc) {
LinkList pa = (*La)->next;
LinkList pb = (*Lb)->next;
LinkList temp;
LinkList pc;
*Lc = pc = *La;
while (pa && pb) {
if (pa->data < pb->data) {
temp = pa;
pa = pa->next;
free(temp);
}else if (pa->data == pb->data) {
pc->next = pa;
pc = pa;
pa = pa->next;
temp = pb;
pb = pb->next;
free(temp);
}else{
temp = pb;
pb = pb->next;
free(temp);
}
}
// 循环结束以后, 判断哪一个表还没有执行结束, 释放剩余的所有结点
while (pa) {
temp = pa;
pa = pa->next;
free(temp);
}
while (pb) {
temp = pb;
pb = pb->next;
free(temp);
}
// 最后还需要将 pc 的 next 置空处理, 释放掉链表 Lb
pc->next = NULL;
free(*Lb);
}
3.
设计一个算法, 将链表中所有节点的链接方向 "原地旋转", 即要求仅仅利用原表的存储空间. 换句话说, 要求算法空间复杂度为 O(1)
例如: L = {0, 2, 4, 6, 8, 10}, 逆转后 L = {10, 8, 6, 4, 2, 0}
void overLinkList(LinkList *La) {
LinkList p = (*La)->next;
LinkList q;
(*La)->next = NULL;
// 先将 La 头结点的指向置空
// 通过遍历从 p 开始往下取结点
// 然后将取出的结点通过 前插法依次插入到 La 中
while (p) {
q = p->next;
p->next = (*La)->next;
(*La)->next = p;
p = q;
}
}
4.
设计一个算法, 删除递增有序链表中值大于等于 mink 且小于等于 maxk (mink, maxk 是给定的两个参数, 其值可以和表中元素相同也可以不同) 的所有元素
void clearLinkListInSection(LinkList *La, Element mink, Element maxk) {
LinkList p = (*La)->next;
LinkList q = (*La);
LinkList temp;
// 判断, 如果给定的 mink 和 maxk 无法构成区间, 直接 return
// 另一种情况 因为链表为递增序列, 如果首元结点的值大于 maxk, 说明链表一定不会在该区间
if ((mink > maxk) || (p->data > maxk)) {
return;
}
// 遍历, 对出在区间中的结点进行删除, 需要注意避免链表断裂
// 对于不在区间中的结点, 进行移位操作
while (p) {
if (p->data >= mink && p->data <= maxk) {
temp = p;
p = p->next;
free(temp);
continue;
}
q->next = p;
q = p;
p = p->next;
}
q->next = p;
}
5.
设将 n(n > 1) 个整数存放到一维数组 R 中, 试设计一个在时间和空间两方面都尽可能高效的算法; 将 R 中保存的序列循环左移 p 个位置 (0 < p < n) 个位置, 即将 R 中的数据由 (x0, x1, x2, ......, xn-1) 变换为 (xp, xp+1,......, xn-1, x0, x1,......xp-1).
例如: pre[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, n = 10, p = 3, pre[10] = {3, 4, 5, 6, 7, 8, 9, 0, 1, 2}
void moveLinkList(LinkList *La, int p) {
LinkList Lb = (LinkList)malloc(sizeof(Node));
initLinkList(&Lb);
LinkList pb = Lb;
LinkList pa = (*La)->next;
// 循环遍历, 将前面的 p 个结点放入到创建的链表 Lb 中
// 同时也要注意在 La 中将这些结点移除出去
for (int i = 0; i < p; i++) {
if (pa == NULL) {
printf("p 值不合法!!!\n");
exit(0);
}
pb->next = pa;
(*La)->next = pa->next;
pb = pa;
pa = pa->next;
}
// 结束以后需要将 Lb 的尾结点, 也就是 pb 的当前结点置空, 防止错乱
pb->next = NULL;
// 找到 La 的尾结点
while (pa->next) {
pa = pa->next;
}
// 直接将 La 尾结点的 next 指向 Lb 的首元结点
pa->next = Lb->next;
// 最后将 Lb 释放掉
Lb->next = NULL;
free(Lb);
}
6.
已知一个整数序列 A = (a0, a1, a2,...an-1), 其中(0 <= ai <= n), (0 <= i <= n). 若存在 ap1 = ap2 = ... = apm = x, 且 m > n/2 (0 <= pk < n, 1 <= k <= m), 则称 x 为 A 的主元素. 例如: A = (0,5,5,3,5,7,5,5), 则 5 是主元素, 若 B = (0,5,5,3,5,1,5,7), 则 B 中没有主元素, 假设 A 中的 n 个元素保存在一个一位数组中, 请设计一个尽可能高效的算法, 找出数组元素中的主元素, 若存在主元素则输出该元素, 否则输出 -1。
int getMainElement(int *A, int n) {
// 通过 count 来统计某一个元素的出现次数
int count = 1;
// key 为当前假设的主元素
int key = A[0];
// 扫描主元素
// 因为符合主元素的条件, key 出现的次数 m 必须超过一半
// 通过循环和判断确定数组里面可能存在主元素 key
for (int i = 1; i < n; i++) {
if (A[i] == key) {
count++;
}else{
if (count > 0) {
count--;
}else{
key = A[i];
count = 1;
}
}
}
// 如果最后得到的 count 是 大于 0 的, 需要去重新计算一下当前 key 的出现次数
if (count > 0) {
count = 0;
for (int i = 0; i < n; i++) {
if (A[i] == key) {
count++;
}
}
}
// 根据 count 出现的次数, 判断 key 是不是符合主元素的要求
return (count > n/2) ? key : -1;
}
7.
用单链表保存m个整数, 结点的结构为(data,link),且|data|<=n(n为正整数). 现在要去设计一个时间复杂度尽可能高效的算法. 对于链表中的data 绝对值相等的结点, 仅保留第一次出现的结点,而删除其余绝对值相等的结点.例如,链表A = {21,-15,15,-7,15}, 删除后的链表A={21,-15,-7};
void deleteEqualNode(LinkList *La, int n) {
// 对链表进行非空判断
if ((*La)->next == NULL) {
return;
}
// 因为 data 里面的数 |data| <= n, 所以这里建立一个长度为 n 的 int 型数组
int *p = alloca(sizeof(int) * n);
// 初始化数组的值, 将值全部置空
for (int i = 0; i < n; i++) {
p[i] = 0;
}
//
LinkList pa = (*La);
// 对链表进行循环遍历, 在绝对值对应的下标出现时判断数组里面对应的值
// 如果值为 0, 将其变为 1, 如果值为 1, 将后面出现的结点删除
while (pa) {
if (p[abs(pa->next->data)] == 1) {
LinkList temp = pa->next;
pa->next = pa->next->next;
pa = pa->next;
free(temp);
}else{
p[abs(pa->next->data)] = 1;
pa = pa->next;
}
}
}