链表

LongGuan_admin 发布于 2026-01-24 60 次阅读


概述

链表(Linked list)是一种线性的数据结构,其中的每一个元素都是一个节点对象。其中各个节点通过“指针”相互连接。指针记录了下一个节点的内存地址,通过它可以从当前节点访问到下一个节点。链表分布在内存空间各处,不需要连续的内存

内存空间

内存空间是所有程序的公共资源,在一个复杂的系统运行下,空闲的内存空间可能散落在内存各处。

数组的内存空间必须是连续的,当数组非常大时,内存有可能无法提供大量的连续空间,此时就会体现出链表分散在存储空间各处的优势,它不需要一个连续的内存空间。

定义链表

通过创建一个新的类型名为node的结构体用来作为链表的节点。(链表节点的结构体不能是匿名的,因为在创建结构体时,需要创建结构体类型的指针用来作为指向节点,如果是匿名结构体,创建结构体后才能知道结构体的别名,此时就会造成逻辑冲突)

链表的节点有两个作用,一个是用来存储数据(val),另一个需要指向链表的下一个节点(结构体类型的指针变量next)(单向链表)。

typedef struct node{
  int val;             // 用来存放数据
  struct node *next;   // 用来存放结构体指针
} Node;

创建链表结构体(非必须)

在制作链表时,可以选择创建一个链表结构体用来加强数据之间的联系,当然这不是必须的,但是可以方便我们更好的管理链表结构体,提高代码的可读性

创建一个名为LinkList的链表结构体,其中存放链表的头节点和一个整形数据size,用来存放链表的大小。这个结构体可以是匿名的。

头节点的作用

在链表中使用头节点可以很方便的对链表进行数据的更替,因为头节点的固定(也不存放任何数据),删改数据时不需要去判断每个不同链接节点的指向性问题,也提高了代码的阅读方便性。

typedef struct{
  int size;
  Node *head;  //一个Node结构体类型的指针head 作为头节点
} LinkList;

创建一个新节点

Node *create_new_node(int val)    // 创建一个Node类型的指针函数 传入节点数据val
{
  Node *n = malloc(sizeof(Node)); // 创建一个Node结构体类型的指针n ,并且创建一个大小为一个Node结构体大小的堆内存被n所指向
  if(n == NULL) // 如果堆空间创建失败 打印错误信息并且结束函数
  {
     perror("Create new node failed ! \n");
     return NULL;
  }

  n->val = val;    // 写入传入的节点数据
  n->next = NULL;  // 使n的下一个指向为NULL,防止越界访问或随机指向

 return n;         // 返回指针n 表示成功创建了一个新节点,其中的数据为val
}

链表的初始化

以创建一个带有头节点的空链表为例

LinkList *initial_link_list()                 // 创建一个LinkList类型的指针函数
{
  Node *head = create_new_node(-1);           // 创建一个Node结构体类型的指针并为其创建一个新节点,其中的数据val对其没有任何影响,因为实际使用中,头节点是固定的
  if(head == NULL){                           // 如果head创建失败 结束函数返回NULL
    return NULL;
  }
  LinkList *list = malloc(sizeof(LinkList));  // 创建一个LinkList结构体类型的变量list,并为其开辟一个堆内存空间,大小为一个LinkList结构体所占用的大小
  if(list == NULL){
    perror("Create LinkList failed! \n");
    free(head);
    return NULL;
  }
  list->size = 0;                              // 解引用结构体类型指针list内的size变量赋值为0
  list->head = head;                           /// 解引用结构体类型指针list内的指针变量head赋值为head  表示指向一个头节点,至此,带头节点的空链表创建成功
  return list;                                 // 返回list指针
}

链表的插入(增)

带头节点的链表的头插法

int add_list(LinkList *list, int val)
{ 
  Node *new_node = create_new_node(val);   // 创建一个Node结构体类型的指针指向一个新节点
  if(new_node == NULL){
    perror("add_head failed! \n");
    return 0;
  }
  new_node->next = list->head->next;      // 新节点的下一个节点应该指向头节点的下一个节点 //如果头节点后没有节点,那么指向NULL,如果有节点,那么就会指向那个节点
  list->head->next = new_node;            // 头节点的下一个节点应该指向新创建的节点,完成新节点的插入操作
  list->size++;                           // 链表长度+1
  return 1;
}

带头节点的链表的尾插法

int add_tail(LinkList *list, int val)
{
  Node *new_node = create_new_node(val);
  if(new_node == NULL){
    perror("add_tail failed! \n");
    return 0;
   }
  Node *tail = list->head;               // 创建一个Node结构体类型的指针tail指向链表的头节点
  while(tail->next != NULL){             // 循环遍历链表直到tail的下一个节点为空节点
    tail = tail->next;
  }
  tail->next = new_node;                 // 使tail的下一个节点指向新创建的节点
  list->size++;                          // 链表大小+1
  return 1;
}

链表节点的删除

链表的异常判断

int is_empty(LinkList *list)
{
  if(list->head->next == NULL)       //如果头节点的下一个节点是空 返回1
  {
    return 1;
  }
  return 0;
}

带头节点的链表的头删法

int remove_head(LinkList *list)
{
  if(is_empty(list)){              // 如果返回1 表示为空 结束删除程序
    printf("链表已经为空");
    return 0;
  }
  Node *c_node = list->head->next; // 创建一个中间节点c_node令其指向头节点的下一个节点
  list->head->next = c_node->next; // 令头节点的下一个节点指向中间节点的下一个节点
                                   // 等同于孤立了原头节点的下一个节点
  free(c_node);                    // 释放中间节点,解放指向的节点的内存
  return 0;
}

带头节点的链表的尾删法

int remove_tail(LinkList *list)
{
  if(is_empty(list)){
    printf("链表已经为空");
    return 0;
  }
  Node *c_node = list->head;          // 创建了一个中间节点指向头节点
  while(c_node->next->next != NULL){  // 循环直到中间节点的下个节点后指向NULL
    c_node = c_node->next;   
  }
  Node *tail = c_node->next;          // 创建一个尾节点指向中间节点的下一个节点,也就是指向空节点的前一个节点
  c_node->next = NULL;                // 令中间节点的下一个节点指向NULL
                                      // 等同于孤立了原尾节点
  free(tail);                         // 释放尾节点
  return 1;
}

链表的销毁

如果想要销毁一个链表,应该先销毁链表中的每一个节点,防止内存占用,然后再销毁链表本身

int destroy_list(LinkList *list)
{
  if(list == NULL){               // 如果节点为空,返回0结束程序
    return 0;
  }
  Node *current = list->head;     // 创建一个中间节点令其指向头节点
  Node *node;                     // 创建一个空节点
  while(current != NULL){         // 循环释放每一个节点直到头节点指向NULL
    node = current->next;         // 空指向中间节点的下一个节点,等待下一个节点的释放
    free(current);                // 释放中间节点指向的节点
    current = node;               // 令中间节点指向之前的下一个节点
  }
  free(list);                     // 所有节点释放后再释放链表本身
  return 1;
}

链表的打印

通过循环每次使结构体类型的指针p不断指向下一个节点,直到NULL,每次打印解引用p结构体指针内部的val的值

void for_each_link_list(LinkList *list)
{
  Node *p = list->head->next;
  while(p){
    printf("%d ", p->val);         // 打印Node结构体指针的解引用p结构体内的val的值
    p = p->next;                   // 令p指向下一个节点
  }
}

不带头节点的单链表的操作

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

//定义一个链表节点
typedef struct node
{
  int val;
  struct node *next;
} Node;

//创建一个新节点
Node *create_new_node(int val){
  Node *n = malloc(sizeof(Node));
  if(n == NULL){
    perror("create new node failed");
    return NULL;
  }

  n->val = val;
  n->next = NULL;
  return n;
}

//初始化链表
Node *intial_link_list(){
  Node *node = create_new_node(0);
  if(node == NULL){
    return NULL;
  }

  //创建链表结构体
  Node *list = malloc(sizeof(Node));
  if(list == NULL){
    perror("create LinkList failed!\n");
    free(node);
    return NULL;
 }
    list->next = NULL;

    return list;
}

//不带头节点的链表的头插法
int add_head(Node *list, int val){
  Node *new_node = create_new_node(val);
  if(new_node == NULL){
    perror("new node is failed\n");
    return 0;
  }
  // 让新的节点的next指向链表首节点的next
  new_node->next = list->next;
  // 让链表的next节点指向新节点
  list->next = new_node;
  return 1;
}

//不带头节点的链表的尾插法
int add_tail(Node *list, int val){
  Node *new_node = create_new_node(val);
  if(new_node == NULL){
    perror("new node is failed\n");
    return 0;
  }
  Node *p = list->next;
  while(p->next != NULL){
    p = p->next;
  }
  p->next = new_node;
  new_node->next = NULL;
  return 1;
  //让新的节点的next指向链表尾节点
}

int is_empty(Node *list)
{
    if (list->next == NULL){
        return 1;
    }
    return 0;
}

//不带头节点的链表的头删法
int remove_head(Node *list){
  Node *p = list->next;
  if (is_empty(list)){ 
    printf("链表已经为空!\n");
    return 0;
  }

  list->next = list->next->next;
  free(p);
  return 1;
}

//不带头节点的链表的尾删

int remove_tail(Node *list){
  Node *p = list->next;

  while(p->next->next != NULL){
    p = p->next;
  }

  Node *tail = p->next;
  p->next = NULL;
  free(tail);

  return 1;
}

//不带头节点的链表的销毁
int destory_list(Node *list){
  if(list == NULL){
    return 0;
  }

  Node *curred = list->next;
  Node *next_node;

  while(curred != NULL){
    next_node = curred->next;
    free(curred);
    curred = next_node;
  }
  free(list);
  return 1;
}

//循环打印链表
void for_each_link_list(Node *list){
  Node *p = list->next;
  while(p){
    printf("%d ", p->val);
    p = p->next;
  }
  printf("\n");
}

int main(int argc, char const *argv[])
{
  Node *list = intial_link_list();
  if(list == NULL){
    return -1;
  }

  add_head(list, 1);
  add_head(list, 2);
  add_head(list, 3);
  add_head(list, 4);

  add_tail(list, 1);
  add_tail(list, 2);
  add_tail(list, 3);
  add_tail(list, 4);

  remove_head(list);
  remove_tail(list);

  destory_list(list);
  
  for_each_link_list(list);

  return 0;
}
此作者没有提供个人介绍。
最后更新于 2026-01-24