链表部分伪代码
1、设计一个递归算法,删除不带头结点的单链表L中所有值为x的结点
文字思想;
1)建立递归函数,每一轮进行判断L->data是否为x,是则进行删除结点操作,然后 L = L->next,直到遍历到链表尾部L->next = NULL
LNode *getElemByPos(LinkList L,int i) {
int j = 1;
LNode *P = L->next;
if (i == 0) {
return L;
}
if (i < 1) {
return NULL;
}
while (P != NULL && j < i) {
P = P->next;
j++;
}
return P;
}
void delX(LinkList &L,ElemType x) {
int i = 1;
// 不带头结点
if (L != NULL) {
if (L->data == x) {
LNode *P = getElemByPos(L,i-1);
P->next = L->next;
}
i++;
delX(L,x);
} else {
return ;
}
}
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( n ) O(n) O(n)
参考答案:
文字思想:
1)终止条件:表空
2)划分为更小的问题:假设头指针为L
如果表头元素为X,则删除X,子表为L->next,执行相同的操作
如果表头元素不为X,子表为L->next,执行相同的操作
void delX(LinkList &L,ElemType x) {
// 空表结束
if (L == NULL) {
return ;
}
// 数据域等于X的结点,执行删除
if (L->data == x) {
// 暂存X所在的结点
LNode *P = L;
// L指向X所在结点的下一个结点
L = L->next;
// 释放X所在结点
free(P);
// 递归处理子表
delX(L,x);
} else {
// 第一个结点数据域不是x,则处理子表L->next
delX(L->next,x);
}
}
2、在带头结点的单链表L中,删除所有值为x的结点,并释放空间,假设值为x的结点不唯一,试编写算法以实现上述操作。
文字思想:
和上题的区别就是带了头结点,且没有要求使用递归,所以可以用循环来替代尾递归,思路不变
void delX(LinkList &L,ElemType x) {
// 跳过头结点
L = L->next;
int i = 1;
// 只要结点不为空,就持续操作
while (L != NULL) {
if (L->data == x) {
// 存储当前结点
LNode *P = L;
// 存储前驱结点
LNode *Q = getElemByPos(L,i-1);
// 使链表连续
Q->next = P->next;
L = L->next;
free(P);
}
i++;
}
}
LNode *getElemByPos(LinkList L,i) {
if (i == 0) {
return NULL;
}
if (i < 1) {
return ;
}
int j = 1;
while (L != NULL && j < i) {
L = L->next;
j++;
}
return L;
}
时间复杂度: O ( n ) O(n) O(n)
空间复杂度: O ( 1 ) O(1) O(1)
参考答案:
文字思想:
1)将表L的头结点与其后序部分断开
2)遍历后序部分,对于其值不等于x的结点,执行尾插到L的头结点后;对于其值等于x的结点,执行释放操作
void delXByTailInsert(LinkList L,ElemType x) {
LNode *subHead = L->next; // 摘下来的子表表头
LNode *tailL = L; // L表示的表的表尾
LNode *aux; // 工作指针
while (subHead != NULL) {
// 尾插到L代表的表尾
if (subHead->data != x) {
// 插入表尾,并更新表尾指针
tailL->next = subHead;
tailL = subHead;
// 子表后移
subHead = subHead->next;
// 释放子表当前结点
} else {
// 暂存当前结点
aux = subHead;
// 子表表头后移
subHead = subHead->next;
// 释放
free(aux);
}
}
// L代表的表的尾结点的next设置为NULL
tailL->next = NULL;
}
时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1)
3、设L为带头结点的单链表,编写算法实现从尾到头反向输出每个结点的值
文字思想:
1)实现从头到尾反向输出,可想到单链表的"头插法"
2)注意L是带头结点的单链表
bool LinkReverse(LinkList &L) {
if (L == NULL) {
return false;
}
// 跳过头结点
LNode *subHead = L->next;
L->next = NULL;
while (subHead != NULL) {
// aux存储当前结点
LNode *aux = subHead;
// 子表结点后移
subHead = subHead->next;
// 将当前结点头插到L中
aux->next = L->next;
L->next = aux;
free(aux);
}
return true;
}
时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1)
4、编写在带头结点的单链表L中删除一个最小值结点的高效算法(假设最小值结点是唯一的)
文字思想:
1)遍历整个单链表,创建指针变量接收当前结点及其前驱结点,找到比它值小的就更新指针指向当前结点
2)整个循环下来,指针指向的就是最小值结点
void delMinNode(LinkList &L) {
// 存储头结点
LNode *subHead = L;
L = L->next;
ElemType min = L->data;
// 存储最小值结点的前驱结点
LNode *aux;
// 直到表空才退出循环
while (subHead != NULL) {
// 遇到比min小的就更新min值,并记下前驱结点
if (min > subHead->next->data) {
min = subHead->next->data;
aux = subHead;
}
// 指向下一个结点
subHead = subHead->next;
}
// 最后的aux指向的就是最小值结点的前驱结点
LNode *Z = aux->next;
aux->next = Z->next;
free(Z);
}
时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1)
5、试编写算法将带头结点的单链表就地逆置,所谓"就地"是指辅助空间复杂度为 O ( 1 ) O(1) O(1)
文字思想:
1)要求存储单链表数据反序,可以考虑使用单链表的头插法
void LinkReverse(LinkList &L) {
// 存储头结点后面结点
LNode *subHead = L->next;
// 作为头结点存在
L->next = NULL;
LNode *aux;
while (subHead != NULL) {
// 存储当前结点
aux = subHead;
subHead = subHead->next;
L->next = aux;
}
}
时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1)
6、有一个带头结点的单链表L,设计一个算法使其元素递增有序
文字思想:
1)使用数组存储L中各个元素的值,然后进行排序
2)设置LNode *subHead = L,L->next = NULL,则L保存的是原先的头结点,则利用头插法可以将数组中的元素依次插入到L后,则将有序表全部插完后整个L存储的结点元素值便是递增有序的
void LinkSort(LinkList L) {
LNode *subHead = L;
// 存储头结点,对有序表进行头插法
L->next = NULL;
// 计算结点的个数,方便使用数组存储
int count = 0;
while (subHead != NULL) {
count += 1;
L->next = subHead;
subHead = subHead->next;
}
int *temp = (int *)malloc(sizeof(int) * count);
memset(temp,0,sizeof(int)*count);
subHead = L;
L->next = NULL;
int i;
for (i = 0;i < count;i++) {
temp[i] = subHead->data;
subHead = subHead->next;
}
// 进行选择排序,每一轮选择最大的元素,直到表为空
int j = 0,min;
while (j < count) {
min = temp[j];
for (j = 0;j < count;j++) {
if (min > temp[j]) {
min = temp[j];
}
}
// 使用头插法插入到L后
LNode *r;
r->data = min;
r->next = L->next;
L->next = r;
}
L->next = NULL;
}
时间复杂度: O ( n 2 ) O(n^2) O(n2) 空间复杂度: O ( n ) O(n) O(n)
参考答案:
文字思想:
1)待排序列L[1…n]某时状态如此:有序序列L[1,…i-1],L[i],无序序列L[i+1…n]
2)将L[i]插入到已有序的子序列L[1…i-1]中
// 使用直接插入排序算法进行排序
void insertSort(LinkList L) {
LNode *curp = L->next, // 第一个元素结点
*prep,
// 用于标识无序部分链表
// 第一个元素结点认为是有序的,无序部分从第二个开始
*subL = curp->next;
// 刚开始,有序部分只有一个元素,最后一个元素的next应该是NULL
curp->next = NULL;
// 从第二个元素结点开始处理【无序部分】
curp = subL;
// 无序部分还有结点的时候
while (curp != NULL) {
// curp将会插入到有序部分,所以先保存它的后继【新的无序部分】
subL = curp->next;
// 准备从前向后找第一个大于curp元素的前一个结点
prep = L;
while (prep->next != NULL && prep->next->data < curp->data) {
// 当前元素最后一位仍然小于curp
// 有序部分后移
prep = prep->next;
}
// 退出时,prep后面就是curp的插入位置
/*
1、curp是最大的
2、prep->next是第一个大于curp的结点
*/
// 典型的插入操作【待插入的结点主动】
curp->next = prep->next;
prep->next = curp;
// 处理无序部分的下一个结点
curp = subL;
}
}
时间复杂度: O ( n 2 ) O(n^2) O(n2) 空间复杂度: O ( 1 ) O(1) O(1)
7、设在一个带表头结点的单链表中所有元素结点的数据值无序,试编写一个函数,删除表中所有介于给定的两个值(作为函数参数给出)之间的元素的元素(若存在)
文字思想:
1)设置前驱结点指针和当前结点指针,在遍历到合适的结点满足数据值在给定范围内时,通过前驱结点指针删除当前结点,并连接到当前结点的后继结点
void delRange(LinkList L,int start,int end) {
LNode *aux = L,*subHead = L->next;
while (subHead != NULL) {
if (subHead->data >= start && subHead->data <= end) {
// 存储当前结点
LNode *P = subHead;
aux->next = P->next;
free(P);
} else {
// 指向后一结点
aux = subHead;
subHead = subHead->next;
}
}
}
8、给定两个链表,编写算法找出两个链表的公共结点
文字思想:
1)公共结点就是地址(指针)相同
2)问题转化为遍历链表A和B,找到地址值相同的第一个结点
LNode *findFirstNode(LinkList A,LinkList B) {
// 假设都是带头结点的链表
LNode *subHead = A->next;
A->next = NULL;
// A链表中结点逐个比较B中结点情况
while (subHead != NULL) {
LNode *head = B->next;
B->next = NULL;
while (head != NULL) {
// 若找到两个地