1对给定的带头结点的单链表 head,编写一个删除head中值为 x的结点的直接前驱
结点的算法(假设链表中最多只有一个值为×的结点)。
思路:要删除x的直接前驱,需要判断其直接前驱的前一个结点的情况
- 初始检查:如果链表为空或只有一个节点(即只有头节点),直接返回。
- 遍历链表:
current
从第一个实际节点开始遍历。prev
初始为头节点,用于保存当前节点的前一个节点。prev_prev
初始为空,用于保存前一个节点的前一个节点。
- 删除操作:
- 如果找到值为
x
的节点,根据prev_prev
是否为空来决定如何调整链表结构。 - 如果
prev_prev
为空,说明前驱节点是头节点后的第一个节点,直接将头节点的next
指针指向当前节点。 - 否则,将
prev_prev
的next
指针指向当前节点。 - 释放前驱节点的内存。
- 删除操作完成后,退出循环。
- 如果找到值为
代码:
struct ListNode* delete_predecessor_of_x(struct ListNode* head, int x) {
if (!head || !head->next) {
// 如果链表为空或只有一个节点(即只有头节点),直接返回
return head;
}
struct ListNode* current = head->next; // 当前节点,从第一个实际节点开始
struct ListNode* prev = head; // 前一个节点,初始为头节点
struct ListNode* prev_prev = NULL; // 前前一个节点,初始为空
while (current) {
if (current->val == x) {
// 找到了值为 x 的节点
if (!prev_prev) {
// 如果前驱节点是头节点后的第一个节点
head->next = current; // 跳过前驱节点,直接连接到当前节点
} else {
prev_prev->next = current; // 跳过前驱节点,连接到当前节点
}
free(prev); // 释放前驱节点的内存
break; // 删除操作完成,退出循环
}
prev_prev = prev; // 更新前前一个节点
prev = current; // 更新前一个节点
current = current->next; // 移动到下一个节点
}
return head; // 返回修改后的链表头节点
}
2有一个单链表head,编写函数从单链表中删除自第i个结点起的k个结点
思路:
-
边界条件检查: 在执行删除操作之前,我们需要检查:
- 单链表是否为空。
i
是否小于 0(即无效索引)。k
是否小于或等于 0(即无效删除数)。
-
找到第
i
个节点: 我们需要遍历链表,以找到第i
个节点的位置。设定一个指针current
来遍历,另一个指针prev
用于记录current
的前一个节点。 -
删除节点: 从第
i
个节点开始,逐个删除后面k
个节点。每次删除一个节点时,更新current
指针以指向下一个节点。 -
更新链接: 删除操作完成后,需要小心地更新指向链表的指针。如果我们删除的是头节点,则需要更新
head
,否则将i-1
节点的next
指针指向i+k
的节点。
代码:
void deleteNodes(ListNode* head, int i, int k) {
if (!head || i < 0 || k <= 0) return; // 无效输入处理
ListNode *prev = NULL;
ListNode *current = head;
// 移动到第 i 个节点
for (int j = 0; j < i && current; ++j) {
prev = current;
current = current->next;
}
// 如果 current 为空,说明链表长度不足 i 个节点
if (!current) return;
// 删除 k 个节点
for (int j = 0; j < k && current; ++j) {
ListNode *temp = current;
current = current->next;
delete temp; // 释放内存
}
// 连接第 i-1 个节点和第 i+k 个节点
if (prev) {
prev->next = current;
} else {
head = current; // 如果删除的是头节点,更新 head
}
}
3有一个单链表(不同结点数据域的值可能相同),其头指针为head,编写函数计算 值为×的结点个数。
思路:遍历链表,统计值为 x
的节点个数。
代码:
//计算值为 x 的节点个数的函数
int countNodesValue(struct ListNode* head, int x) {
int count = 0; // 初始化计数器
struct ListNode* current = head; // 从链表头开始遍历
// 遍历链表
while (current != NULL) {
if (current->val == x) { // 如果当前节点的值等于 x
count++; // 计数器加一
}
current = current->next; // 移动到下一个节点
}
return count; // 返回总计数
}
4.有两个单向循环链表,链头指针分别为 headl和head2,编写一个函数将链表
headl原地连接到链表head2之后,连接后的链表仍是单向循环链表
思路:
-
找到链表的尾节点 (
findTail
函数):- 输入: 链表的头节点
head
。 - 输出: 链表的尾节点(即指向头节点的前一个节点)。
- 逻辑:
- 如果链表为空 (
head
为 NULL),则返回 NULL。 - 初始化一个指针
tail
指向head
,用于遍历链表。 - 使用一个
while
循环遍历链表,条件是tail->next != head
。当tail->next
指向head
时,tail
就是尾节点,循环结束。 - 返回
tail
,即整个链表的尾节点。
- 如果链表为空 (
- 输入: 链表的头节点
-
连接两个单向循环链表 (
connectLists
函数):- 输入: 指向链表
head1
的指针(struct ListNode** head1
),链表head2
的头节点指针(struct ListNode* head2
)。 - 逻辑:
- 首先检查
head1
是否为空:- 如果为空,直接将
head2
赋值给head1
,即相当于连接后head1
完全指向head2
,形成一个循环。
- 如果为空,直接将
- 如果
head2
为空,则不需要连接,直接返回。 - 找到
head1
的尾节点tail1
和head2
的尾节点tail2
:tail1
用于将head1
的最后一个节点连接到head2
的头节点。tail2
用于将head2
的最后一个节点连接回head1
的头节点。
- 连接操作:
tail1->next = head2;
将head1
的尾节点连接到head2
的头节点。tail2->next = *head1;
将head2
的尾节点连接回head1
的头节点,形成一个完整且循环的链表。
- 首先检查
- 输入: 指向链表
代码:
// 找到链表的尾节点
struct ListNode* findTail(struct ListNode* head) {
if (head == NULL) return NULL; // 如果链表为空,返回 NULL
struct ListNode* tail = head; // 初始化尾节点为头节点
// 遍历链表,直到找到指向头节点的节点,即尾节点
while (tail->next != head) {
tail = tail->next; // 移动到下一个节点
}
return tail; // 返回链表的尾节点
}
// 连接两个单向循环链表 **head1二级指针
void connectLists(struct ListNode**head1, struct ListNode* head2) {
if (*head1 == NULL) {
// 如果 head1 为空,直接将 head2 赋值给 head1
*head1 = head2; // 此时可以将 head1 直接指向 head2,因此无需再进行连接
return;
}
if (head2 == NULL) {
// 如果 head2 为空,不需要做任何操作
return; // 直接返回,无需连接
}
// 找到 head1 的尾节点
struct ListNode* tail1 = findTail(*head1);
// 找到 head2 的尾节点
struct ListNode* tail2 = findTail(head2);
// 将 head1 的尾节点指向 head2 的头节点
tail1->next = head2;
// 将 head2 的尾节点指向 head1 的头节点
tail2->next = *head1;
}
完整代码:
#include <stdio.h>
#include <stdlib.h>
// 定义单向循环链表节点结构体
struct ListNode {
int val; // 节点值
struct ListNode* next; // 指向下一个节点的指针
};
// 创建新节点的函数
struct ListNode* createNode(int value) {
struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
newNode->val = value;
newNode->next = newNode; // 单向循环链表,指向自身
return newNode;
}
// 找到链表的尾节点
struct ListNode* findTail(struct ListNode* head) {
if (head == NULL) return NULL;
struct ListNode* tail = head;
while (tail->next != head) {
tail = tail->next;
}
return tail;
}
// 连接两个单向循环链表
void connectLists(struct ListNode** head1, struct ListNode* head2) {
if (*head1 == NULL) {
// 如果 head1 为空,直接将 head2 赋值给 head1
*head1 = head2;
return;
}
if (head2 == NULL) {
// 如果 head2 为空,不需要做任何操作
return;
}
struct ListNode* tail1 = findTail(*head1); // 找到 head1 的尾节点
struct ListNode* tail2 = findTail(head2); // 找到 head2 的尾节点
// 将 head1 的尾节点指向 head2 的头节点
tail1->next = head2;
// 将 head2 的尾节点指向 head1 的头节点
tail2->next = *head1;
}
// 打印单向循环链表
void printList(struct ListNode* head) {
if (head == NULL) return;
struct ListNode* current = head;
do {
printf("%d -> ", current->val);
current = current->next;
} while (current != head);
printf("(回到头部)\n");
}
// 主函数示例
int main() {
// 创建链表 head1: 1 -> 2 -> 3 -> 1
struct ListNode* head1 = createNode(1);
head1->next = createNode(2);
head1->next->next = createNode(3);
head1->next->next->next = head1; // 形成循环
// 创建链表 head2: 4 -> 5 -> 6 -> 4
struct ListNode* head2 = createNode(4);
head2->next = createNode(5);
head2->next->next = createNode(6);
head2->next->next->next = head2; // 形成循环
printf("连接前:\n");
printf("链表 head1: ");
printList(head1);
printf("链表 head2: ");
printList(head2);
// 连接两个链表
connectLists(&head1, head2);
printf("连接后:\n");
printf("链表 head1: ");
printList(head1);
// 清理内存(由于循环链表会形成无限循环,清理内存会更加复杂,这里省略清理部分)
return 0; // 返回成功
}
5已知A、B和C为3个递增有序的线性表,现要求对A表做如下操作:删除那些 在B表中出现又在C表中出现的元素。试用顺序表编写实现上述操作的算法(注意题中没有特别指明同一表中的元素值各不相同)
#include <stdio.h>
// 函数声明
void delete_common_elements(int A[], int *a_size, int B[], int b_size, int C[], int c_size);
int is_in_both(int x, int B[], int b_size, int C[], int c_size);
int is_in(int x, int arr[], int size);
int main() {
// 示例数据
int A[] = {1, 2, 3, 4, 5, 6, 7, 8}; // 数组A
int B[] = {2, 4, 6, 8}; // 数组B
int C[] = {4, 6, 8}; // 数组C
int a_size = sizeof(A) / sizeof(A[0]); // 数组A的大小
int b_size = sizeof(B) / sizeof(B[0]); // 数组B的大小
int c_size = sizeof(C) / sizeof(C[0]); // 数组C的大小
// 调用函数删除同时出现在B和C中的元素
delete_common_elements(A, &a_size, B, b_size, C, c_size);
// 打印结果,使用中文提示
printf("修改后的数组A为: ");
for (int i = 0; i < a_size; i++) {
printf("%d ", A[i]);
}
printf("\n");
return 0;
}
// 删除同时出现在B和C中的元素
void delete_common_elements(int A[], int *a_size, int B[], int b_size, int C[], int c_size) {
int i, j;
for (i = 0, j = 0; i < *a_size; i++) {
// 如果A[i]不在B和C中同时出现,则保留A[i]
if (!is_in_both(A[i], B, b_size, C, c_size)) {
A[j] = A[i];
j++;
}
}
// 更新数组A的大小
*a_size = j;
}
// 检查元素x是否同时存在于数组B和数组C中
int is_in_both(int x, int B[], int b_size, int C[], int c_size) {
// 如果x在B和C中都存在,返回1,否则返回0
return is_in(x, B, b_size) && is_in(x, C, c_size);
}
// 检查元素x是否存在于数组arr中
int is_in(int x, int arr[], int size) {
for (int i = 0; i < size; i++) {
// 如果找到x,返回1
if (x == arr[i]) {
return 1;
}
}
// 如果没有找到,返回0
return 0;
}
6已知线性表中的所有元素按值递增的顺序有序排列,并以单链表作为存储结松 试设计算法,删除表中所有值构同的多余元素(值相同的结点只保留第1个),使得操 后的线性表中所有元素的值均不相同,同时释放被删除结点的空间。
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
typedef struct Node {
int data;
struct Node* next;
} Node;
Node* createNode(int data) {
Node* newnode = (Node*)malloc(sizeof(Node));
if (newnode == NULL) {
printf("新节点创建失败");
exit(1);
}
newnode->data = data;
newnode->next = NULL;
return newnode;
}
void delteNode(Node** head) {
Node* cur = *head;
while (cur != NULL && cur->next != NULL) {
if (cur->data == cur->next->data) {
Node* temp = cur->next;
cur->next = cur->next->next;
free(temp);
} else {
cur = cur->next;
}
}
}
void printfList(Node* head) {
Node* cur = head;
while (cur != NULL) {
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
int main() {
Node* head = createNode(1);
head->next = createNode(1);
head->next->next = createNode(2);
head->next->next->next = createNode(3);
head->next->next->next->next = createNode(3);
head->next->next->next->next->next = createNode(4);
printf("正常链表:");
printfList(head);
delteNode(&head);
printf("删除后的链表:");
printfList(head);
return 0;
}