嵌入式大厂面经 链表考点(持续更新中!)

这是一个嵌入式大厂面试题专栏,每天更新高频面试题。专栏将包含题目描述、详细解析、相关知识点扩展以及实际代码示例。内容涵盖操作系统、驱动开发、通信协议等核心领域,并结合实际项目经验进行分析。每道题目都会附带面试官可能的追问方向,帮助大家更好地准备面试!

链表常见面试题总结

1. 链表基础概念

Q: 什么是链表?链表有哪些类型?

链表是一种线性数据结构,由一系列节点组成,每个节点包含数据字段和指向下一个节点的引用(指针)。

常见类型:

  • 单向链表:每个节点只有一个指向下一节点的指针
  • 双向链表:每个节点有两个指针,分别指向前一个和后一个节点
  • 循环链表:最后一个节点指向第一个节点,形成环
  • 双向循环链表:结合双向链表和循环链表特性

Q: 链表与数组的区别是什么?各自的优缺点?

区别与优缺点

内存分配

动态分配,不连续

静态分配,连续空间

随机访问

O(n)

O(1)

插入删除

O(1)(已知位置)

O(n)(需要移动元素)

空间开销

需要额外指针空间

无额外开销

缓存局部性

较差

较好

大小调整

灵活,无需重新分配

固定或需要重新分配

2. 链表基本操作

Q: 如何实现单链表的创建、插入、删除和遍历?

// 定义链表节点结构
typedef struct Node {
    int data;
    struct Node* next;
} Node;

// 创建新节点
Node* createNode(int data) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (newNode == NULL) {
        printf("内存分配失败\n");
        exit(1);
    }
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
}

// 在链表头部插入节点
Node* insertAtBeginning(Node* head, int data) {
    Node* newNode = createNode(data);
    newNode->next = head;
    return newNode;
}

// 在链表尾部插入节点
Node* insertAtEnd(Node* head, int data) {
    Node* newNode = createNode(data);
    
    // 如果链表为空,新节点就是头节点
    if (head == NULL) {
        return newNode;
    }
    
    // 找到最后一个节点
    Node* temp = head;
    while (temp->next != NULL) {
        temp = temp->next;
    }
    
    // 在最后一个节点后添加新节点
    temp->next = newNode;
    return head;
}

// 删除指定值的节点
Node* deleteNode(Node* head, int key) {
    // 如果链表为空,直接返回
    if (head == NULL) {
        return NULL;
    }
    
    // 如果头节点就是要删除的节点
    if (head->data == key) {
        Node* temp = head;
        head = head->next;
        free(temp);
        return head;
    }
    
    // 查找要删除的节点
    Node* current = head;
    Node* prev = NULL;
    
    while (current != NULL && current->data != key) {
        prev = current;
        current = current->next;
    }
    
    // 如果找到了要删除的节点
    if (current != NULL) {
        prev->next = current->next;
        free(current);
    }
    
    return head;
}

// 遍历链表
void traverseList(Node* head) {
    Node* current = head;
    
    while (current != NULL) {
        printf("%d -> ", current->data);
        current = current->next;
    }
    printf("NULL\n");
}

// 释放链表内存
void freeList(Node* head) {
    Node* current = head;
    Node* next;
    
    while (current != NULL) {
        next = current->next;
        free(current);
        current = next;
    }
}

Q: 如何实现双向链表的基本操作?

// 定义双向链表节点结构
typedef struct DNode {
    int data;
    struct DNode* prev;
    struct DNode* next;
} DNode;

// 创建新节点
DNode* createDNode(int data) {
    DNode* newNode = (DNode*)malloc(sizeof(DNode));
    if (newNode == NULL) {
        printf("内存分配失败\n");
        exit(1);
    }
    newNode->data = data;
    newNode->prev = NULL;
    newNode->next = NULL;
    return newNode;
}

// 在双向链表头部插入节点
DNode* insertAtBeginningDLL(DNode* head, int data) {
    DNode* newNode = createDNode(data);
    
    if (head == NULL) {
        return newNode;
    }
    
    newNode->next = head;
    head->prev = newNode;
    return newNode;
}

// 在双向链表尾部插入节点
DNode* insertAtEndDLL(DNode* head, int data) {
    DNode* newNode = createDNode(data);
    
    if (head == NULL) {
        return newNode;
    }
    
    DNode* temp = head;
    while (temp->next != NULL) {
        temp = temp->next;
    }
    
    temp->next = newNode;
    newNode->prev = temp;
    return head;
}

// 删除双向链表中的节点
DNode* deleteNodeDLL(DNode* head, int key) {
    if (head == NULL) {
        return NULL;
    }
    
    // 如果头节点是要删除的节点
    if (head->data == key) {
        DNode* temp = head;
        head = head->next;
        
        if (head != NULL) {
            head->prev = NULL;
        }
        
        free(temp);
        return head;
    }
    
    DNode* current = head;
    
    while (current != NULL && current->data != key) {
        current = current->next;
    }
    
    if (current == NULL) {
        return head; // 没找到要删除的节点
    }
    
    // 调整前一个节点的next指针
    if (current->prev != NULL) {
        current->prev->next = current->next;
    }
    
    // 调整后一个节点的prev指针
    if (current->next != NULL) {
        current->next->prev = current->prev;
    }
    
    free(current);
    return head;
}

// 遍历双向链表
void traverseDLL(DNode* head) {
    DNode* current = head;
    
    printf("NULL <-> ");
    while (current != NULL) {
        printf("%d <-> ", current->data);
        current = current->next;
    }
    printf("NULL\n");
}

3. 链表经典问题

Q: 如何检测链表中是否有环?

Floyd的龟兔算法(快慢指针法)

bool hasCycle(Node* head) {
    if (head == NULL || head->next == NULL) {
        return false;
    }
    
    Node* slow = head;
    Node* fast = head;
    
    while (fast != NULL && fast->next != NULL) {
        slow = slow->next;          // 慢指针每次走一步
        fast = fast->next->next;    // 快指针每次走两步
        
        if (slow == fast) {         // 如果两个指针相遇,说明有环
            return true;
        }
    }
    
    return false;  // 如果fast到达链表尾部,说明没有环
}

Q: 如何找到链表的中间节点?

快慢指针法

Node* findMiddle(Node* head) {
    if (head == NULL) {
        return NULL;
    }
    
    Node* slow = head;
    Node* fast = head;
    
    // 快指针每次走两步,慢指针每次走一步
    // 当快指针到达链表尾部时,慢指针恰好在中间
    while (fast != NULL && fast->next != NULL) {
        slow = slow->next;
        fast = fast->next->next;
    }
    
    return slow;  // 慢指针指向中间节点
}

Q: 如何反转单链表?

迭代方法

Node* reverseList(Node* head) {
    Node* prev = NULL;
    Node* current = head;
    Node* next = NULL;
    
    while (current != NULL) {
        next = current->next;    // 保存下一个节点
        current->next = prev;    // 反转当前节点的指针
        prev = current;          // 移动prev指针
        current = next;          // 移动current指针
    }
    
    return prev;  // 新的头节点
}

递归方法

Node* reverseListRecursive(Node* head) {
    // 基本情况:空链表或只有一个节点
    if (head == NULL || head->next == NULL) {
        return head;
    }
    
    // 递归反转剩余部分
    Node* newHead = reverseListRecursive(head->next);
    
    // 改变指针方向
    head->next->next = head;
    head->next = NULL;
    
    return newHead;
}

Q: 如何合并两个有序链表?

Node* mergeTwoLists(Node* l1, Node* l2) {
    // 创建一个哑节点作为合并链表的头部
    Node dummy;
    Node* tail = &dummy;
    
    while (l1 != NULL && l2 != NULL) {
        if (l1->data <= l2->data) {
            tail->next = l1;
            l1 = l1->next;
        } else {
            tail->next = l2;
            l2 = l2->next;
        }
        tail = tail->next;
    }
    
    // 连接剩余部分
    if (l1 != NULL) {
        tail->next = l1;
    } else {
        tail->next = l2;
    }
    
    return dummy.next;
}

Q: 如何检测并找到环的入口点?

Node* detectCycleStart(Node* head) {
    if (head == NULL || head->next == NULL) {
        return NULL;
    }
    
    Node* slow = head;
    Node* fast = head;
    bool hasCycle = false;
    
    // 第一步:检测是否有环
    while (fast != NULL && fast->next != NULL) {
        slow = slow->next;
        fast = fast->next->next;
        
        if (slow == fast) {
            hasCycle = true;
            break;
        }
    }
    
    if (!hasCycle) {
        return NULL;
    }
    
    // 第二步:找到环的入口点
    slow = head;
    while (slow != fast) {
        slow = slow->next;
        fas

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

嵌入式面试八股文全集 文章被收录于专栏

这是一个全面的嵌入式面试专栏。主要内容将包括:操作系统(进程管理、内存管理、文件系统等)、嵌入式系统(启动流程、驱动开发、中断管理等)、网络通信(TCP/IP协议栈、Socket编程等)、开发工具(交叉编译、调试工具等)以及实际项目经验分享。专栏将采用理论结合实践的方式,每个知识点都会附带相关的面试真题和答案解析。

全部评论

相关推荐

评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务