考研是许多计算机科学专业学生追求高学历、寻求更好就业前景的途径。在考研过程中,数据结构是一个非常重要的科目,而代码实现题更是其中的难点之一。在这篇文章中,我们将探讨如何通过实现数据结构代码问题来提升考研成绩。无论您是否有编程经验,本文将为您提供一些简单但实用的技巧,帮助您应对考研中遇到的数据结构题目。让我们一起踏上这个挑战性的学习旅程吧!
目录
初识单链表
第一题)递归删除不带头节点链表中指定值
第二题)带头节点链表删除指定值
第三题)反向输出链表值
第四题) 带头节点的单链表删除最小值结点
第五题)求两链表交集
第六题)单链表排序
第七题)删除重复结点
第八题)删除绝对值相等的结点
单链表是一种常见的基本数据结构,它由一系列节点组成,每个节点包含两部分:数据(即存储的元素)和指向下一个节点的引用(指针或链接)。单链表中的节点按照其在内存中的位置顺序连接起来,形成一个链式结构。
单链表中的第一个节点称为头节点,最后一个节点的指针部分指向一个空值(通常表示为 null),表示链表的结束。
单链表的特点:
1)动态性: 单链表的长度可以动态地增加或减少,不需要预先指定大小。
2)插入与删除高效: 在单链表中,插入或删除元素时只需要修改节点之间的指针,时间复杂度为 O(1)。
3)随机访问效率低: 与数组不同,单链表中的元素并不是在连续的内存空间中存储的,因此难以通过索引进行快速访问,需要从头节点开始按顺序遍历,时间复杂度为 O(n)。
实现单链表通常包括以下步骤(C语言实现):
1)定义节点结构体:
首先,需要定义一个结构体来表示链表的节点。这个结构体通常包含两部分:数据成员和指向下一个节点的指针成员。2)节点的创建与初始化:
可以通过动态内存分配(malloc 函数)来创建新的节点,并通过赋值操作对节点中的数据和指针进行初始化。3)插入与删除节点:
可以编写函数来实现在链表中插入新节点或删除现有节点的操作。这些操作通常涉及对节点之间的指针进行调整。4)遍历与访问:
为了能够访问链表中的所有节点,需要使用循环结构或递归来依次访问每个节点,并进行相应的操作。5)释放内存:
在链表不再需要时,需要确保释放所有节点所占用的内存,以避免内存泄漏。
具体代码实现如下:
#include
#include
// 定义链表节点的结构体
struct Node {
int data;
struct Node* next;
};
// 在链表末尾插入新节点
void appendNode(struct Node** headRef, int newData) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
struct Node* last = *headRef;
newNode->data = newData;
newNode->next = NULL;
if (*headRef == NULL) {
*headRef = newNode;
return;
}
while (last->next != NULL) {
last = last->next;
}
last->next = newNode;
}
// 打印链表中的所有节点数据
void printList(struct Node* node) {
while (node != NULL) {
printf("%d -> ", node->data);
node = node->next;
}
printf("NULL\n");
}
int main() {
// 初始化头节点
struct Node* head = NULL;
// 在链表末尾依次插入节点
appendNode(&head, 1);
appendNode(&head, 2);
appendNode(&head, 3);
// 打印链表
printf("Linked list: ");
printList(head);
return 0;
}
设计一个递归算法,删除不带头节点的单链表L中所有值为 x 的结点。
实现思路如下:
首先:代码定义了一个单链表的数据结构,每个节点包含一个整型数据和一个指向下一个节点的指针。
然后:在deleteNodes函数中,首先检查链表是否为空。如果为空,则返回空。否则,递归调用deleteNodes函数,对链表的下一个节点进行删除操作。
接着:在deleteNodes函数中,如果当前节点的数据等于要删除的元素x,那么就删除当前节点。具体操作是,先保存下一个节点的指针,然后释放当前节点的内存,最后返回下一个节点的指针。
最后:在main函数中,首先获取用户输入的单链表长度和元素,然后调用deleteNodes函数删除指定元素,最后打印删除后的单链表结果。
#include
#include
struct Node {
int data;
struct Node* next;
};
struct Node* createNode(int data) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
if (newNode == NULL) {
printf("链表为空,请重新创建:\n");
exit(1);
}
newNode->data = data;
newNode->next = NULL;
return newNode;
}
void insertNode(struct Node** head, int data) {
struct Node* newNode = createNode(data);
if (*head == NULL) {
*head = newNode;
return;
}
struct Node* curr = *head;
while (curr->next != NULL) {
curr = curr->next;
}
curr->next = newNode;
}
struct Node* deleteNodes(struct Node* head, int x) {
struct Node* prev = NULL;
struct Node* curr = head;
while (curr != NULL) {
if (curr->data == x) {
if (prev == NULL) {
head = curr->next;
}
else {
prev->next = curr->next;
}
free(curr);
return head;
}
prev = curr;
curr = curr->next;
}
return head;
}
void printList(struct Node* head) {
while (head != NULL) {
printf("%d ", head->data);
head = head->next;
}
printf("\n");
}
int main() {
struct Node* head = NULL;
int n, data, x;
printf("请输入单链表的长度: ");
if (scanf_s("%d", &n) != 1 || n < 0) {
printf("输入长度有误!请重新输入:\n");
return 1;
}
printf("请输入单链表的元素: ");
for (int i = 0; i < n; i++) {
if (scanf_s("%d", &data) != 1 || data < 0) {
printf("输入元素有误!请重新输入.\n");
return 1;
}
insertNode(&head, data);
}
printf("请输入要删除的元素: ");
if (scanf_s("%d", &x) != 1 || x < 0) {
printf("删除元素有误!请重新删除.\n");
return 1;
}
head = deleteNodes(head, x);
printf("最终的单链表结果为: ");
printList(head);
return 0;
}
执行代码结果展示如下:
在带头节点的单链表 L 中,删除所有值为 x 的结点,并释放其空间,假设值为 x 的结点不唯一,试编写算法以实现上述操作。
实现思路如下:
首先:我们定义了单链表的结点结构体,并创建了一个带头节点的单链表。
接着:我们实现了向单链表中插入新结点和删除单链表中所有值为 x 的结点的功能。
然后:在主函数中,我们首先输入单链表的结点个数和值,并将这些值插入到单链表中,输入要删除的结点值,并调用删除结点的函数。
最后:我们打印出删除后的单链表。这样就完成了整体代码的四个步骤。
#include
#include
// 定义单链表结点结构体
typedef struct Node {
int data;
struct Node* next;
} Node;
// 创建带头节点的单链表
Node* createLinkedList() {
Node* head = (Node*)malloc(sizeof(Node));
head->next = NULL;
return head;
}
// 向单链表中插入新结点
void insertNode(Node* head, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = NULL;
Node* p = head;
while (p->next != NULL) {
p = p->next;
}
p->next = newNode;
}
// 删除单链表中所有值为 x 的结点
void deleteNodes(Node* head, int x) {
Node* p = head->next;
Node* prev = head;
while (p != NULL) {
if (p->data == x) {
Node* temp = p;
prev->next = p->next;
p = p->next;
free(temp);
}
else {
prev = p;
p = p->next;
}
}
}
// 打印单链表
void printLinkedList(Node* head) {
Node* p = head->next;
while (p != NULL) {
printf_s("%d ", p->data);
p = p->next;
}
printf_s("\n");
}
int main() {
Node* list = createLinkedList();
// 输入单链表数据
int n;
printf_s("请输入单链表的结点个数:");
scanf_s("%d", &n);
printf_s("请输入单链表的结点值:\n");
for (int i = 0; i < n; i++) {
int value;
scanf_s("%d", &value);
insertNode(list, value);
}
// 输入要删除的结点值
int x;
printf_s("请输入要删除的结点值:");
scanf_s("%d", &x);
// 删除值为 x 的结点
deleteNodes(list, x);
// 打印删除后的单链表
printf_s("删除后的单链表:");
printLinkedList(list);
return 0;
}
执行代码结果展示如下:
设L为带头节点的单链表,编写算法实现从尾到头反向输出每个结点的值。
实现思路如下:
首先:程序定义了一个Link结构体,用于表示链表的节点,每个节点包含一个整数数据和一个指向下一个节点的指针。
接着:在函数reverseOutput中,首先判断传入的链表节点指针是否为空,如果为空,则直接返回。否则,递归地调用reverseOutput函数,并传入该节点的下一个节点指针。这样可以先逆序输出链表后面的节点。
然后:在递归调用之后,打印当前节点的数据,用printf_s
函数输出。
最后:在main函数中,首先读取用户输入的创建链表的节点个数。然后定义两个指针变量q和head,其中q用于指向链表的头指针,head用于指向最新的节点。接下来,通过循环创建链表的节点,每次循环创建一个新的节点,让head->next指向新节点,然后将head指向新节点,以保证head始终指向最新的节点。最后,将链表的最后一个节点的next指针置为NULL,将head重新指向q,即将head指向链表头节点。最后,调用reverseOutput函数输出逆序的链表元素。最后,返回0表示程序正常结束。
#include
#include
struct Link {
int data;
struct Link* next;
};
void reverseOutput(Link* p) {
if (p == NULL) return;
else {
reverseOutput(p->next);
printf_s("%d ", p->data);
}
}
int main() {
int n, data;
printf_s("请输入创建链表的结点个数:");
scanf_s("%d", &n);
struct Link* q;
struct Link* head = (struct Link*)malloc(sizeof(struct Link));
head->next = NULL;
q = head;
for (int i = 0; i < n; i++)
{
struct Link* newP = (struct Link*)malloc(sizeof(struct Link));
printf_s("请输入第 %d 个结点的值:",i+1);
scanf_s("%d", &data);
newP->data = data;
newP->next = NULL;
head->next = newP;
head = head->next; // head要始终指向最新节点
}
head->next = NULL;
head = q; // 最后head要指向头结点
reverseOutput(head->next);
return 0;
}
执行代码结果展示如下:
试编写在带头结点的单链表L中删除一个最小值结点的高效算法(假设最小值结点是唯一的)
实现思路如下:
首先:代码定义了一个链表结点结构体 Node,其中包含数据域 data 和指向下一个结点的指针next。
接着:通过createLinkedList函数创建了一个带头结点的单链表。在函数内部,我们使用malloc函数为头结点分配内存,并将头结点的next指针指向NULL,表示链表为空。
然后:使用insertNode函数向链表中插入新节点。该函数接受一个链表头结点指针head和要插入的值value作为参数。在函数内部,我们首先创建一个新的结点newNode,并为其分配内存。然后,我们遍历链表,找到链表的尾部结点,将新节点插入到尾部结点的next指针处。
最后:通过deleteMinNode函数删除链表中的最小值结点。该函数接受链表头结点指针head作为参数。在函数内部,我们使用两个指针prev和curr来遍历链表,找到最小值结点以及其前一个结点。然后,我们将最小值结点的前一个结点的`next`指针指向最小值结点的下一个结点,并释放最小值结点的内存。
#include
#include
// 定义链表结点结构体
typedef struct Node {
int data;
struct Node* next;
} Node;
// 创建带头结点的单链表
Node* createLinkedList() {
Node* head = (Node*)malloc(sizeof(Node));
head->next = NULL;
return head;
}
// 向链表中插入新节点
void insertNode(Node* head, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = NULL;
Node* curr = head;
while (curr->next != NULL) {
curr = curr->next;
}
curr->next = newNode;
}
// 删除最小值结点
void deleteMinNode(Node* head) {
if (head == NULL || head->next == NULL) {
return;
}
Node* prev = head;
Node* curr = head->next;
Node* minPrev = prev; // 最小值结点的前一个结点
Node* minNode = curr; // 最小值结点
while (curr != NULL) {
if (curr->data < minNode->data) {
minPrev = prev;
minNode = curr;
}
prev = curr;
curr = curr->next;
}
minPrev->next = minNode->next;
free(minNode);
}
// 打印链表
void printLinkedList(Node* head) {
if (head == NULL || head->next == NULL) {
printf("链表为空\n");
return;
}
Node* curr = head->next;
while (curr != NULL) {
printf("%d ", curr->data);
curr = curr->next;
}
printf("\n");
}
int main() {
Node* L = createLinkedList();
int n; // 链表长度
printf("请输入链表的长度:");
scanf_s("%d", &n);
printf("请输入链表的元素:\n");
for (int i = 0; i < n; i++) {
int value;
scanf_s("%d", &value);
insertNode(L, value);
}
printf("原始链表:");
printLinkedList(L);
deleteMinNode(L);
printf("删除最小值结点后的链表:");
printLinkedList(L);
return 0;
}
执行代码结果展示如下:
已知两个链表A和B分别表示两个集合,其元素递增排序。编制函数,求A与B的交集,并存放于A链表中。
实现思路如下:
首先:通过createLinkedList函数创建了两个带头结点的单链表A和B。该函数分配了内存空间,并将头结点的next指针指向NULL,表示链表为空。
接着:使用scanf_s函数获取用户输入的整数n和m,分别表示链表A和B的长度。
然后:调用findIntersection函数,传入A链表和B链表作为参数。该函数用于找到A链表和B链表的交集,并将结果存放在A链表中。
最后:通过printLinkedList函数分别打印出原始的A和B链表以及交集结果。
#include
#include
typedef struct Node {
int data;
struct Node* next;
} Node;
// 创建带头结点的单链表
Node* createLinkedList() {
Node* head = (Node*)malloc(sizeof(Node));
head->next = NULL;
return head;
}
// 向链表中插入新节点
void insertNode(Node* head, int value) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->data = value;
newNode->next = NULL;
Node* curr = head;
while (curr->next != NULL) {
curr = curr->next;
}
curr->next = newNode;
}
// 求两个递增排序链表的交集,并将结果存放于A链表中
void findIntersection(Node* A, Node* B) {
Node* currA = A->next;
Node* currB = B->next;
Node* prevA = A;
Node* temp;
while (currA != NULL && currB != NULL) {
if (currA->data < currB->data) {
prevA->next = currA->next;
temp = currA;
currA = currA->next;
free(temp);
}
else if (currA->data > currB->data) {
currB = currB->next;
}
else {
prevA = currA;
currA = currA->next;
currB = currB->next;
}
}
while (currA != NULL) {
prevA->next = currA->next;
temp = currA;
currA = currA->next;
free(temp);
}
}
// 打印链表
void printLinkedList(Node* head) {
if (head == NULL || head->next == NULL) {
printf("链表为空\n");
return;
}
Node* curr = head->next;
while (curr != NULL) {
printf("%d ", curr->data);
curr = curr->next;
}
printf("\n");
}
int main() {
Node* A = createLinkedList();
Node* B = createLinkedList();
int n, m; // A链表长度和B链表长度
printf("请输入A链表的长度:");
scanf_s("%d", &n);
printf("请输入A链表的元素(递增排序):\n");
for (int i = 0; i < n; i++) {
int value;
scanf_s("%d", &value);
insertNode(A, value);
}
printf("请输入B链表的长度:");
scanf_s("%d", &m);
printf("请输入B链表的元素(递增排序):\n");
for (int i = 0; i < m; i++) {
int value;
scanf_s("%d", &value);
insertNode(B, value);
}
printf("原始A链表:");
printLinkedList(A);
printf("原始B链表:");
printLinkedList(B);
findIntersection(A, B);
printf("A与B的交集结果:");
printLinkedList(A);
return 0;
}
执行代码结果展示如下:
有一个带头结点的单链表L,设计一个算法使其元素递增有序。
实现思路如下:
首先:定义了链表节点结构体ListNode,包含数据域和指向下一个节点的指针。
接着:实现了创建新节点的函数createNewNode,用于分配内存并初始化新节点。
然后:实现了将元素插入有序链表的函数insertInOrder,根据元素大小找到合适的位置插入新节点,并保持链表的有序性。
最后:在主函数中,通过用户输入创建带头结点的链表L,并调用insertInOrder将输入的元素按递增顺序插入链表中,最后打印排列后的链表内容。
#include
#include
// 定义链表节点结构体
typedef struct ListNode {
int data;
struct ListNode* next;
} ListNode;
// 创建新节点
ListNode* createNewNode(int data) {
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 将元素插入有序链表中
void insertInOrder(ListNode** head, int data) {
ListNode* newNode = createNewNode(data);
// 如果链表为空或者新节点的值小于头结点的值,则将新节点作为头结点
if (*head == NULL || data < (*head)->data) {
newNode->next = *head;
*head = newNode;
}
else {
ListNode* curr = *head;
// 找到适当的位置插入新节点
while (curr->next != NULL && data > curr->next->data) {
curr = curr->next;
}
newNode->next = curr->next;
curr->next = newNode;
}
}
// 打印链表的内容
void printLinkedList(ListNode* head) {
ListNode* curr = head;
while (curr != NULL) {
printf("%d ", curr->data);
curr = curr->next;
}
printf("\n");
}
// 从键盘读取用户输入的整数
int readInt() {
int num;
scanf_s("%d", &num);
return num;
}
// 主函数
int main() {
// 创建带头结点的链表L
ListNode* L = createNewNode(0);
printf("请输入链表L的元素个数:");
int n = readInt();
for (int i = 0; i < n; i++) {
printf("请输入第%d个元素:", i + 1);
int data = readInt();
insertInOrder(&L, data);
}
printf("排列后的链表L: ");
printLinkedList(L->next);
return 0;
}
执行代码结果展示如下:
设计一个算法完成以下功能:在单链表中删除重复结点。
实现思路如下:
首先:定义了链表节点的结构体。
接着:实现了创建新节点的函数和打印链表的函数。
然后:实现了删除重复节点的函数。该函数通过遍历链表,对于每一个节点,再次遍历链表来找到并删除与当前节点数据相同的节点。
最后:实现了主函数。在主函数中,首先要求用户输入链表的元素个数和具体数值,然后根据输入创建一个单链表。接着调用删除重复节点的函数,再打印出删除重复节点后的链表。最后,释放链表内存。
#include
#include
// 定义链表节点结构体
typedef struct ListNode {
int data;
struct ListNode* next;
} ListNode;
// 创建新节点
ListNode* createNewNode(int data) {
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 打印链表
void printList(ListNode* head) {
ListNode* current = head;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
// 删除重复节点
void removeDuplicates(ListNode* head) {
if (head == NULL) {
return;
}
ListNode* current = head;
while (current != NULL) {
ListNode* runner = current;
while (runner->next != NULL) {
if (runner->next->data == current->data) {
ListNode* duplicate = runner->next;
runner->next = runner->next->next;
free(duplicate);
}
else {
runner = runner->next;
}
}
current = current->next;
}
}
// 释放链表内存
void freeList(ListNode* head) {
ListNode* current = head;
while (current != NULL) {
ListNode* temp = current;
current = current->next;
free(temp);
}
}
// 主函数
int main() {
int n;
printf("请输入链表元素个数:");
scanf_s("%d", &n);
ListNode* head = NULL;
ListNode* tail = NULL;
printf("请输入链表元素值:");
for (int i = 0; i < n; i++) {
int data;
scanf_s("%d", &data);
ListNode* newNode = createNewNode(data);
if (head == NULL) {
head = newNode;
tail = newNode;
}
else {
tail->next = newNode;
tail = newNode;
}
}
printf("原链表:");
printList(head);
removeDuplicates(head);
printf("删除重复节点后的链表:");
printList(head);
// 释放链表内存
freeList(head);
return 0;
}
执行代码结果展示如下:
设计一个算法完成以下功能:在单链表中删除绝对值相等的元素。
实现思路如下:
首先:定义了一个链表节点的结构体,并实现了创建新节点、打印链表、删除绝对值相等节点和释放链表内存的函数。
接着:主函数中要求用户输入链表元素的个数和具体数值,然后根据输入创建一个单链表。
然后:调用removeAbsoluteDuplicates函数来删除链表中绝对值相等的节点。
最后:打印出删除绝对值相等节点后的链表,并释放链表的内存。
#include
#include
#include
// 定义链表节点结构体
typedef struct ListNode {
int data;
struct ListNode* next;
} ListNode;
// 创建新节点
ListNode* createNewNode(int data) {
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 打印链表
void printList(ListNode* head) {
ListNode* current = head;
while (current != NULL) {
printf("%d ", current->data);
current = current->next;
}
printf("\n");
}
// 删除绝对值相等节点
void removeAbsoluteDuplicates(ListNode* head) {
if (head == NULL) {
return;
}
ListNode* current = head;
while (current != NULL && current->next != NULL) {
ListNode* runner = current;
while (runner->next != NULL) {
if (abs(runner->next->data) == abs(current->data)) {
ListNode* duplicate = runner->next;
runner->next = runner->next->next;
free(duplicate);
}
else {
runner = runner->next;
}
}
current = current->next;
}
}
// 释放链表内存
void freeList(ListNode* head) {
ListNode* current = head;
while (current != NULL) {
ListNode* temp = current;
current = current->next;
free(temp);
}
}
// 主函数
int main() {
int n;
printf("请输入链表元素个数:");
scanf_s("%d", &n);
ListNode* head = NULL;
ListNode* tail = NULL;
printf("请输入链表元素值:");
for (int i = 0; i < n; i++) {
int data;
scanf_s("%d", &data);
ListNode* newNode = createNewNode(data);
if (head == NULL) {
head = newNode;
tail = newNode;
}
else {
tail->next = newNode;
tail = newNode;
}
}
printf("原链表:");
printList(head);
removeAbsoluteDuplicates(head);
printf("删除绝对值相等节点后的链表:");
printList(head);
// 释放链表内存
freeList(head);
return 0;
}
执行代码结果展示如下: