MST

星途 面试题库

面试题:C语言指针:复杂数据结构中的指针操作与内存管理

设计一个双向链表结构,每个节点包含一个 `int` 类型的数据和两个指针,分别指向前一个节点和后一个节点。编写函数实现链表的创建、插入节点、删除节点以及释放整个链表内存的操作。在实现过程中,请详细阐述如何正确操作指针以维护链表的结构完整性,以及如何避免内存泄漏问题。特别注意处理边界情况,如链表为空、插入或删除头节点、尾节点等情况。
18.8万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试
#include <iostream>

// 定义双向链表节点结构
struct DoubleListNode {
    int data;
    DoubleListNode* prev;
    DoubleListNode* next;
    DoubleListNode(int val) : data(val), prev(nullptr), next(nullptr) {}
};

// 创建双向链表
DoubleListNode* createList(int arr[], int n) {
    if (n <= 0) return nullptr;
    DoubleListNode* head = new DoubleListNode(arr[0]);
    DoubleListNode* current = head;
    for (int i = 1; i < n; ++i) {
        current->next = new DoubleListNode(arr[i]);
        current->next->prev = current;
        current = current->next;
    }
    return head;
}

// 在指定节点后插入新节点
void insertNode(DoubleListNode* prevNode, int newData) {
    if (prevNode == nullptr) return;
    DoubleListNode* newNode = new DoubleListNode(newData);
    newNode->next = prevNode->next;
    newNode->prev = prevNode;
    if (prevNode->next != nullptr) {
        prevNode->next->prev = newNode;
    }
    prevNode->next = newNode;
}

// 删除指定节点
void deleteNode(DoubleListNode* delNode) {
    if (delNode == nullptr) return;
    if (delNode->prev != nullptr) {
        delNode->prev->next = delNode->next;
    }
    if (delNode->next != nullptr) {
        delNode->next->prev = delNode->prev;
    }
    delete delNode;
}

// 释放整个链表内存
void freeList(DoubleListNode* head) {
    DoubleListNode* current = head;
    DoubleListNode* nextNode;
    while (current != nullptr) {
        nextNode = current->next;
        delete current;
        current = nextNode;
    }
}

// 打印双向链表
void printList(DoubleListNode* head) {
    DoubleListNode* current = head;
    while (current != nullptr) {
        std::cout << current->data << " ";
        current = current->next;
    }
    std::cout << std::endl;
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};
    int n = sizeof(arr) / sizeof(arr[0]);
    DoubleListNode* head = createList(arr, n);

    std::cout << "Original list: ";
    printList(head);

    insertNode(head->next, 6);
    std::cout << "List after insertion: ";
    printList(head);

    deleteNode(head->next->next);
    std::cout << "List after deletion: ";
    printList(head);

    freeList(head);
    return 0;
}

指针操作与结构完整性维护

  1. 创建链表

    • 首先为头节点分配内存并初始化数据。
    • 然后通过遍历数组,为每个新节点分配内存,设置新节点的 prev 指针指向前一个节点,前一个节点的 next 指针指向新节点。
  2. 插入节点

    • 先创建新节点并初始化数据。
    • 新节点的 next 指针指向 prevNode 的下一个节点,prev 指针指向 prevNode
    • 如果 prevNode 的下一个节点存在,则更新其 prev 指针指向新节点。
    • 最后更新 prevNodenext 指针指向新节点。
  3. 删除节点

    • 如果要删除的节点存在,先更新其前后节点的指针,使其绕过被删除节点。
    • 若被删除节点的前一个节点存在,将其 next 指针指向被删除节点的下一个节点。
    • 若被删除节点的下一个节点存在,将其 prev 指针指向被删除节点的前一个节点。
    • 最后释放被删除节点的内存。
  4. 释放链表

    • 使用一个临时指针 nextNode 保存当前节点的下一个节点地址。
    • 释放当前节点的内存,然后将当前节点更新为 nextNode,继续循环直到链表为空。

避免内存泄漏

  1. 动态内存分配:每次使用 new 分配内存后,确保在不需要该内存时使用 delete 释放。在创建链表节点时使用 new,在删除节点和释放链表时使用 delete
  2. 边界情况处理
    • 链表为空:在创建链表时,如果数组长度为0直接返回 nullptr。在插入节点时,如果 prevNodenullptr 直接返回。在删除节点时,如果 delNodenullptr 直接返回。
    • 插入头节点:可以将头节点视为普通节点处理,只要传入的 prevNodenullptr 对应的特殊逻辑即可。例如,在 insertNode 函数中,如果 prevNodenullptr,则新节点成为新的头节点。
    • 删除头节点:在 deleteNode 函数中,若删除的是头节点,更新头节点指针为原头节点的下一个节点(如果存在)。
    • 删除尾节点:在 deleteNode 函数中,若删除的是尾节点,只需更新尾节点前一个节点的 next 指针为 nullptr 即可。