C/C++每日一练:查找链表的中间节点

链表(Linked List)

链表是一种线性数据结构,由一系列节点(Node)通过指针链接在一起。与数组不同,链表中的元素在内存中不需要连续存储,每个节点包含两部分:

  • 数据部分:存储节点的值或数据。
  • 指针部分:存储指向下一个节点的地址(单链表)或上一个和下一个节点的地址(双向链表)。

链表的类型主要有以下几种:

  • 单链表:每个节点只指向下一个节点。
  • 双向链表:每个节点既有指向下一个节点的指针,也有指向上一个节点的指针。
  • 循环链表:链表的最后一个节点指向链表的头节点,形成循环。

链表的特点:

  • 动态大小:可以根据需要动态地增加或减少元素,无需预分配存储空间。
  • 插入/删除效率高:在链表的任意位置进行插入或删除操作只需修改指针,不涉及大量元素的移动,效率较高。
  • 随机访问效率低:由于链表不支持直接访问任意位置的元素,需要通过遍历来查找特定位置的节点。

如下图所示:

题目要求

给定一个单链表,编写一个算法来查找链表的中间节点。如果链表长度为奇数,则返回唯一的中间节点;如果为偶数,则返回两个中间节点中的第一个。

示例

假设链表为:1 -> 2 -> 3 -> 4 -> 5

  • 输出:节点 3

假设链表为:1 -> 2 -> 3 -> 4

  • 输出:节点 2

做题思路

寻找链表的中间节点可以用快慢指针的方法。这种方法效率高且不需要额外的空间,适合单链表查找问题。

  1. 定义两个指针:slow 和 fast。
  2. slow 每次向前移动一步,fast 每次向前移动两步。
  3. 当 fast 指针遍历到达链表末尾时,slow 指针正好在链表的中间位置。
  4. 这种方法的时间复杂度为 O(n),空间复杂度为 O(1),是一种高效的解法。

过程解析

  1. 初始化两个指针 slow 和 fast,都指向链表的头节点。
  2. 通过循环控制 fast 每次移动两步,slow 每次移动一步。
  3. 当 fast 为 NULL 或 fast->next 为 NULL 时,结束循环,此时 slow 即为中间节点。

运用到的知识点

  • 链表的基本操作和定义
  • 指针的概念和指针操作
  • 快慢指针算法技巧
  • 时间和空间复杂度分析

示例代码

C 实现

cpp 复制代码
#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 -> 5
    struct 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++ 实现

cpp 复制代码
#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 -> 5
    Node* 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;  // 程序结束
}
相关推荐
yuanbenshidiaos1 小时前
c++---------数据类型
java·jvm·c++
十年一梦实验室1 小时前
【C++】sophus : sim_details.hpp 实现了矩阵函数 W、其导数,以及其逆 (十七)
开发语言·c++·线性代数·矩阵
taoyong0011 小时前
代码随想录算法训练营第十一天-239.滑动窗口最大值
c++·算法
这是我582 小时前
C++打小怪游戏
c++·其他·游戏·visual studio·小怪·大型·怪物
Uu_05kkq2 小时前
【C语言1】C语言常见概念(总结复习篇)——库函数、ASCII码、转义字符
c语言·数据结构·算法
fpcc2 小时前
跟我学c++中级篇——C++中的缓存利用
c++·缓存
呆萌很2 小时前
C++ 集合 list 使用
c++
诚丞成3 小时前
计算世界之安生:C++继承的文水和智慧(上)
开发语言·c++
TT哇4 小时前
【数据结构练习题】链表与LinkedList
java·数据结构·链表
东风吹柳4 小时前
观察者模式(sigslot in C++)
c++·观察者模式·信号槽·sigslot