链表(Linked List)
链表是一种线性数据结构,由一系列节点(Node)通过指针链接在一起。与数组不同,链表中的元素在内存中不需要连续存储,每个节点包含两部分:
- 数据部分:存储节点的值或数据。
- 指针部分:存储指向下一个节点的地址(单链表)或上一个和下一个节点的地址(双向链表)。
链表的类型主要有以下几种:
- 单链表:每个节点只指向下一个节点。
- 双向链表:每个节点既有指向下一个节点的指针,也有指向上一个节点的指针。
- 循环链表:链表的最后一个节点指向链表的头节点,形成循环。
链表的特点:
- 动态大小:可以根据需要动态地增加或减少元素,无需预分配存储空间。
- 插入/删除效率高:在链表的任意位置进行插入或删除操作只需修改指针,不涉及大量元素的移动,效率较高。
- 随机访问效率低:由于链表不支持直接访问任意位置的元素,需要通过遍历来查找特定位置的节点。
如下图所示:
题目要求
给定一个单链表,编写一个算法来查找链表的中间节点。如果链表长度为奇数,则返回唯一的中间节点;如果为偶数,则返回两个中间节点中的第一个。
示例
假设链表为:1 -> 2 -> 3 -> 4 -> 5
- 输出:节点 3
假设链表为:1 -> 2 -> 3 -> 4
- 输出:节点 2
做题思路
寻找链表的中间节点可以用快慢指针的方法。这种方法效率高且不需要额外的空间,适合单链表查找问题。
- 定义两个指针:slow 和 fast。
- slow 每次向前移动一步,fast 每次向前移动两步。
- 当 fast 指针遍历到达链表末尾时,slow 指针正好在链表的中间位置。
- 这种方法的时间复杂度为 O(n),空间复杂度为 O(1),是一种高效的解法。
过程解析
- 初始化两个指针 slow 和 fast,都指向链表的头节点。
- 通过循环控制 fast 每次移动两步,slow 每次移动一步。
- 当 fast 为 NULL 或 fast->next 为 NULL 时,结束循环,此时 slow 即为中间节点。
运用到的知识点
- 链表的基本操作和定义
- 指针的概念和指针操作
- 快慢指针算法技巧
- 时间和空间复杂度分析
示例代码
C 实现
#include <stdio.h> // 包含标准输入输出库,用于输出打印函数
#include <stdlib.h> // 包含标准库,用于内存分配// 定义链表节点结构体
struct Node {int data; // 节点数据部分,存储整型数据struct Node* next; // 指向下一个节点的指针
};// 创建新节点函数
struct Node* createNode(int data) {// 为新节点分配内存空间struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));// 初始化新节点的数据newNode->data = data;// 将新节点的指针设置为 NULL,表示链表的末尾newNode->next = NULL;return newNode; // 返回创建好的新节点
}// 查找链表中间节点的函数
struct Node* findMiddle(struct Node* head) {struct Node *slow = head, *fast = head; // 定义并初始化快慢指针,均指向链表头节点// 循环遍历链表,直到 fast 到达链表末尾while (fast != NULL && fast->next != NULL) {slow = slow->next; // 慢指针每次向前移动一步fast = fast->next->next; // 快指针每次向前移动两步}return slow; // 当循环结束时,慢指针指向链表的中间节点
}// 打印链表的函数
void printList(struct Node* head) {struct Node* temp = head; // 定义一个临时指针,用于遍历链表// 循环遍历链表,直到节点为空while (temp != NULL) {printf("%d -> ", temp->data); // 打印当前节点的数据temp = temp->next; // 移动到下一个节点}printf("NULL\n"); // 链表末尾指示为 NULL
}// 主函数
int main()
{// 创建链表节点并连接,构造链表 1 -> 2 -> 3 -> 4 -> 5struct Node* head = createNode(1);head->next = createNode(2);head->next->next = createNode(3);head->next->next->next = createNode(4);head->next->next->next->next = createNode(5);printf("链表: ");printList(head); // 调用函数打印链表// 查找链表的中间节点struct Node* middle = findMiddle(head);if (middle != NULL) {// 如果链表非空,打印中间节点的数据printf("The data of the middle node is: %d\n", middle->data);} else {// 如果链表为空,提示信息printf("the linked list is empty.\n");}return 0; // 程序结束
}
C++ 实现
#include <iostream> // 包含输入输出流库,用于控制台输出
using namespace std; // 使用标准命名空间,避免每次使用 std:: 前缀// 定义链表节点类
class Node {
public:int data; // 节点数据部分,存储整型数据Node* next; // 指向下一个节点的指针// 构造函数,用于创建新节点并初始化数据Node(int data) {this->data = data; // 初始化节点数据this->next = nullptr; // 初始化指针为 nullptr 表示链表末尾}
};// 查找链表中间节点的函数
Node* findMiddle(Node* head) {Node *slow = head, *fast = head; // 定义快慢指针,均初始化为链表头节点// 循环遍历链表,直到 fast 或 fast->next 到达链表末尾while (fast != nullptr && fast->next != nullptr) {slow = slow->next; // 慢指针每次移动一步fast = fast->next->next; // 快指针每次移动两步}return slow; // 循环结束时,慢指针指向链表的中间节点
}// 打印链表的函数
void printList(Node* head) {Node* temp = head; // 定义临时指针,用于遍历链表// 循环遍历链表,直到节点为空while (temp != nullptr) {cout << temp->data << " -> "; // 打印当前节点的数据temp = temp->next; // 移动到下一个节点}cout << "NULL" << endl; // 链表末尾输出 NULL 表示链表终点
}int main()
{// 创建链表节点并连接,构造链表 1 -> 2 -> 3 -> 4 -> 5Node* head = new Node(1);head->next = new Node(2);head->next->next = new Node(3);head->next->next->next = new Node(4);head->next->next->next->next = new Node(5);cout << "链表: ";printList(head); // 打印链表// 查找链表的中间节点Node* middle = findMiddle(head);if (middle != nullptr) {// 如果链表非空,打印中间节点的数据cout << "The data of the intermediate node is: " << middle->data << endl;} else {// 如果链表为空,提示信息cout << "The linked list is empty" << endl;}return 0; // 程序结束
}