单链表的基本操作

单链表的定义与结构

单链表由节点(Node)组成,每个节点包含两个部分:数据域和指针域。数据域存储实际数据,指针域存储下一个节点的地址。C语言中通常用结构体定义:

typedef struct Node {
    int data;           // 数据域,假设存储整型
    struct Node *next;  // 指针域
} Node;

 

创建链表

动态内存分配创建链表头节点,初始时链表为空:

该函数用于创建一个空链表的头节点。头节点不存储实际数据,仅作为链表起始位置的标记。头节点的next指针初始化为NULL,表示链表为空。

Node* createList() {
    // 分配头节点内存空间
    Node *head = (Node*)malloc(sizeof(Node));
    
    // 检查内存是否分配成功
    if (head == NULL) {
        printf("Memory allocation failed\n");
        exit(1);  // 分配失败时强制退出程序
    }
    
    // 初始化头节点的指针域为NULL
    head->next = NULL;
    
    // 返回创建好的头节点
    return head;
}
 

 

 

 

插入节点

头插法:新节点插入在链表头部。

这是一个用于在带头节点的单链表头部插入新节点的函数。函数接受链表头指针和要插入的数据作为参数,创建新节点并将其插入到头节点之后的位置。

// 在链表头部插入新节点的函数
// 参数:
//   head - 指向链表头节点的指针
//   data - 要插入的新节点的数据值
void insertAtHead(Node *head, int data) {
    // 为新节点动态分配内存空间
    Node *newNode = (Node*)malloc(sizeof(Node));
    
    // 设置新节点的数据域
    newNode->data = data;
    
    // 将新节点的next指针指向原头节点之后的节点
    newNode->next = head->next;
    
    // 将头节点的next指针指向新节点
    head->next = newNode;
}
 

 

  • 该函数假设传入的head指针不为NULL,且指向一个有效的链表
  • 如果链表为空(只有头节点),函数会将新节点作为第一个实际节点插入
  • 使用malloc分配内存后,在实际应用中应该检查分配是否成功
  • 调用者需要负责最终释放链表内存

 

 

尾插法:新节点插入在链表尾部。

该函数实现向单链表尾部插入新节点的功能。函数接收链表头指针head和要插入的数据data作为参数。

// 在链表尾部插入新节点的函数
// 参数:head - 链表头节点指针,data - 要插入的数据
void insertAtTail(Node *head, int data) {
    // 为新节点分配内存空间
    Node *newNode = (Node*)malloc(sizeof(Node));
    
    // 设置新节点的数据域和指针域
    newNode->data = data;    // 存储数据
    newNode->next = NULL;    // 新节点作为尾节点,next置为NULL

    // 从头节点开始遍历链表
    Node *current = head;
    
    // 循环找到当前链表的最后一个节点
    // 当current->next为NULL时,current就是尾节点
    while (current->next != NULL) {
        current = current->next;
    }
    
    // 将新节点链接到链表尾部
    current->next = newNode;
}
 

 

  • 该函数假设链表已经有一个头节点
  • 头节点的data字段可能未使用或用作特殊用途
  • 调用该函数后,新节点将成为链表的第一个实际数据节点
  • 需要确保传入的head指针不为NULL
  • 使用malloc分配内存后没有检查是否成功,实际应用中应添加错误处理

 

 

指定位置插入:在链表的第pos个位置插入节点(从1开始计数)。

该函数实现在单链表的指定位置插入一个新节点。位置计数从1开始,即pos=1表示在头节点后插入。

// 在链表的指定位置插入一个新节点
// 参数说明:
//   head: 链表头节点的指针
//   pos:  要插入的位置(从1开始计数)
//   data: 新节点的数据值
void insertAtPosition(Node *head, int pos, int data) {
    // 为新节点分配内存空间
    Node *newNode = (Node*)malloc(sizeof(Node));
    // 设置新节点的数据值
    newNode->data = data;

    // 初始化当前节点指针为头节点
    Node *current = head;

    // 遍历链表,找到要插入位置的前一个节点
    // 循环条件:i < pos(因为位置从1开始计数)
    // 同时确保当前节点不为空(防止越界)
    for (int i = 1; i < pos && current != NULL; i++) {
        current = current->next;
    }

    // 如果当前节点为空,说明位置无效
    if (current == NULL) {
        printf("Invalid position\n");
        return;
    }

    // 将新节点的next指针指向当前节点的下一个节点
    newNode->next = current->next;
    // 将当前节点的next指针指向新节点,完成插入操作
    current->next = newNode;
}
 
  1. 该函数没有处理pos=0的情况(在头节点前插入)
  2. 函数假设链表至少有一个头节点
  3. 当pos超过链表长度时,会打印"Invalid position"并返回
  4. 调用者需要确保传入有效的head指针

 

 

删除节点

删除头节点:删除链表的第一个有效节点(非头节点)。

void deleteAtHead(Node *head) {
    // 检查链表是否为空(头哨兵节点的next为空)
    if (head->next == NULL) {
        printf("List is empty\n");
        return;
    }
    
    // 保存待删除的节点(头哨兵节点的下一个节点)
    Node *temp = head->next;
    
    // 更新头哨兵节点的next指针,跳过待删除节点
    head->next = temp->next;
    
    // 释放待删除节点的内存
    free(temp);
}
 

 

 

 

删除尾节点:删除链表的最后一个节点。

void deleteAtTail(Node *head) {
    // 检查链表是否为空(仅包含头节点)
    if (head->next == NULL) {
        printf("List is empty\n");
        return;
    }
    
    // 初始化指针,从头节点开始遍历
    Node *current = head;
    
    // 遍历到倒数第二个节点
    while (current->next->next != NULL) {
        current = current->next;
    }
    
    // 释放尾节点的内存
    free(current->next);
    
    // 将倒数第二个节点的next指针置为NULL
    current->next = NULL;
}
 
  • 该实现假设链表包含头节点
  • 调用前需确保头节点本身不为NULL
  • 删除操作后链表长度减1,但头节点保留

 

 

删除指定位置节点:删除链表的第pos个节点。

void deleteAtPosition(Node *head, int pos) {
    // 检查链表是否为空(仅含头节点)
    if (head->next == NULL) {
        printf("List is empty\n");
        return;
    }

    // 遍历链表直到目标位置的前驱节点
    Node *current = head;
    for (int i = 1; i < pos && current->next != NULL; i++) {
        current = current->next;
    }

    // 检查位置是否超出链表范围
    if (current->next == NULL) {
        printf("Invalid position\n");
        return;
    }

    // 执行删除操作
    Node *temp = current->next;      // 保存待删除节点
    current->next = temp->next;      // 前驱节点跳过待删除节点
    free(temp);                      // 释放内存
}
 

 

 

  • 该实现假设头节点不存储有效数据(哑节点设计)
  • 位置参数pos从1开始计数,对应第一个有效节点
  • 当链表为空或位置无效时,函数会打印错误信息并返回
  • 删除操作的时间复杂度为O(n),需要遍历链表找到前驱节点

查找节点

按值查找:返回第一个匹配值的节点位置,未找到返回-1。

/**
 * 在链表中搜索指定值的位置
 * @param head 链表头节点指针(带哨兵节点)
 * @param value 要搜索的目标值
 * @return 目标值的位置(从1开始计数),未找到返回-1
 */
int searchByValue(Node *head, int value) {
    Node *current = head->next;  // 跳过哨兵节点,从第一个有效节点开始
    int pos = 1;                 // 位置计数器初始化为1
    
    while (current != NULL) {    // 遍历链表直到末尾
        if (current->data == value) {  // 找到目标值
            return pos;          // 返回当前位置
        }
        current = current->next; // 移动到下一个节点
        pos++;                   // 位置计数器递增
    }
    return -1;                   // 遍历结束未找到目标值
}
 

 

 

 

按位置查找:返回第pos个节点的值。

/**
 * 获取链表中指定位置节点的值
 * @param head 链表头节点指针(带哨兵节点)
 * @param pos 要获取的位置(从1开始计数)
 * @return 目标位置节点的值,位置无效返回-1
 */
int searchByPosition(Node *head, int pos) {
    Node *current = head->next;  // 跳过哨兵节点,从第一个有效节点开始
    
    // 遍历到目标位置或链表末尾
    for (int i = 1; i < pos && current != NULL; i++) {
        current = current->next;
    }
    
    if (current == NULL) {       // 位置超出链表范围
        printf("Invalid position\n");
        return -1;
    }
    return current->data;        // 返回目标位置节点的值
}
 

 

 

 

 

 

遍历链表

打印链表中所有节点的值。

void traverseList(Node *head) {
    // 定义一个指针current,初始化为链表的第一个实际节点(跳过头节点)
    Node *current = head->next;
    
    // 遍历链表直到current指向NULL(链表末尾)
    while (current != NULL) {
        // 打印当前节点的数据,并附加" -> "表示指向下一个节点
        printf("%d -> ", current->data);
        // 移动current指针到下一个节点
        current = current->next;
    }
    
    // 打印"NULL"表示链表结束
    printf("NULL\n");
}
 

 

 

销毁链表

释放链表中所有节点的内存,避免内存泄漏。

void destroyList(Node *head) {
    // 初始化当前指针,从头节点的下一个节点开始(跳过头节点)
    Node *current = head->next;
    
    // 遍历链表直到末尾(current为NULL)
    while (current != NULL) {
        // 临时保存当前节点地址用于后续释放内存
        Node *temp = current;
        
        // 移动当前指针到下一个节点
        current = current->next;
        
        // 释放当前节点占用的内存
        free(temp);
    }
    
    // 将头节点的next指针置为NULL,表示空链表
    head->next = NULL;
}
 
  • 函数不会释放头节点本身的内存,仅释放数据节点
  • 调用该函数后,链表变为仅有头节点的空链表
  • 若链表本身为空(head->next为NULL),函数直接执行head->next=NULL操作

 

 

示例代码

以下是一个完整的单链表操作示例:

#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    int data;
    struct Node *next;
} Node;

Node* createList() {
    Node *head = (Node*)malloc(sizeof(Node));
    head->next = NULL;
    return head;
}

void insertAtHead(Node *head, int data) {
    Node *newNode = (Node*)malloc(sizeof(Node));
    newNode->data = data;
    newNode->next = head->next;
    head->next = newNode;
}

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

int main() {
    Node *head = createList();
    insertAtHead(head, 10);
    insertAtHead(head, 20);
    insertAtHead(head, 30);
    traverseList(head);  // 输出: 30 -> 20 -> 10 -> NULL
    return 0;
}

 

以上操作涵盖了单链表的基本功能,实际使用时可根据需求进一步扩展。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值