Login
首页 > 图文教程 > C++教程

C++链表的创建、赋值和遍历输出【代码可视化】

燃冰世纪教育 2025-07-18 11:43:31 人看过


链表详细教程
一、链表的基本概念
链表是一种线性数据结构,与数组不同的是,其元素并非存储在连续的内存空间中。链表的每个元素称为节点,每个节点包含两部分:一是存储的数据,二是指向其他节点的指针(或引用),通过这些指针,各个节点被串联起来,形成一个链状结构。
数组的元素存储在连续的内存空间,这使得数组可以通过下标直接访问任意元素,但当进行插入或删除操作时,需要移动大量元素,效率较低。而链表的元素通过指针连接,插入和删除元素时只需修改指针指向,无需移动大量元素,但访问元素时需要从头节点开始依次遍历,不能像数组那样直接通过下标访问。
二、链表的类型
(一)单链表
单链表是最简单的链表类型,每个节点只有一个指针,该指针指向其后继节点。单链表有一个头节点(通常不存储实际数据,仅作为链表的起始标志),头节点的指针指向第一个存储数据的节点,最后一个节点的指针为空(NULL),表示链表的结束。
(二)双链表
双链表的每个节点包含两个指针,一个指针指向其前驱节点,另一个指针指向其后继节点。这使得双链表可以方便地向前或向后遍历节点,相比单链表,在某些操作(如删除指定节点、查找前驱节点等)上更加高效。
(三)循环链表
循环链表的结构特点是最后一个节点的指针不指向空,而是指向头节点,形成一个环形结构。循环链表可以从任意节点开始遍历整个链表,在解决一些环形问题时非常有用,比如约瑟夫问题。
三、链表的基本操作
以下将以 C 语言为例,详细介绍链表的基本操作及实现。
(一)节点的定义
首先定义链表节点的结构体,以单链表为例:
// 定义单链表节点结构体
typedef struct Node {
   int data;          // 存储的数据
   struct Node* next; // 指向后继节点的指针
} Node;
(二)创建链表
创建链表通常有两种方式:头插法和尾插法。
  1. 头插法:每次将新节点插入到链表的头部。
// 头插法创建单链表
Node* createListHead(int* arr, int n) {
   Node* head = NULL; // 头指针初始为空
   for (int i = 0; i < n; i++) {
       Node* newNode = (Node*)malloc(sizeof(Node)); // 分配内存
       newNode->data = arr[i]; // 存储数据
       newNode->next = head; // 新节点next指向当前头节点
       head = newNode; // 头指针指向新节点,新节点成为新的头节点
   }
   return head;
}
  1. 尾插法:每次将新节点插入到链表的尾部。
// 尾插法创建单链表
Node* createListTail(int* arr, int n) {
   Node* head = NULL;
   Node* tail = NULL; // 尾指针,始终指向最后一个节点
   for (int i = 0; i < n; i++) {
       Node* newNode = (Node*)malloc(sizeof(Node));
       newNode->data = arr[i];
       newNode->next = NULL;
       if (head == NULL) { // 链表为空时,头节点和尾节点都指向新节点
           head = newNode;
           tail = newNode;
       } else {
           tail->next = newNode; // 尾节点next指向新节点
           tail = newNode; // 尾指针指向新节点
       }
   }
   return head;
}
(三)插入节点
以单链表为例,介绍在指定位置插入节点的操作。
   Node* newNode = (Node*)malloc(sizeof(Node));
   newNode->data = data;
   newNode->next = NULL;
   // 插入到头部
   if (pos == 1) {
       newNode->next = *head;
       *head = newNode;
       return;
   }
   Node* p = *head;
   int i = 1;
   // 找到pos-1个节点
   while (p != NULL && i < pos - 1) {
       p = p->next;
       i++;
   }
   if (p == NULL) {
       printf("插入位置超出链表长度!\n");
       free(newNode);
       return;
   }
   newNode->next = p->next;
   p->next = newNode;
}
(四)删除节点
同样以单链表为例,删除指定位置的节点。
// 删除单链表pos个位置的节点(pos1开始)
void deleteNode(Node** head, int pos) {
   if (*head == NULL) {
       printf("链表为空,无法删除!\n");
       return;
   }
   if (pos < 1) {
       printf("删除位置无效!\n");
       return;
   }
   Node* p = *head;
   // 删除头节点
   if (pos == 1) {
       *head = p->next;
       free(p);
       return;
   }
   int i = 1;
   Node* prev = NULL;
   // 找到pos个节点及其前驱节点
   while (p != NULL && i < pos) {
       prev = p;
       p = p->next;
       i++;
   }
   if (p == NULL) {
       printf("删除位置超出链表长度!\n");
(五)遍历链表
遍历链表并输出所有节点的数据。
// 遍历单链表并输出节点数据
void traverseList(Node* head) {
   Node* p = head;
   while (p != NULL) {
       printf("%d ", p->data);
       p = p->next;
   }
   printf("\n");
}
(六)查找节点
查找指定数据在链表中的位置。
// 查找数据data的节点在链表中的位置(1开始,未找到返0)
int findNode(Node* head, int data) {
   Node* p = head;
   int pos = 1;
   while (p != NULL) {
       if (p->data == data) {
           return pos;
       }
       p = p->next;
       pos++;
   }
   return 0;
}
四、链表与数组的优缺点对比
(一)链表的优点
  1. 插入和删除操作灵活高效,不需要移动大量元素,只需修改指针指向,时间复杂度通常为 O (1)(在已知前驱节点的情况下)。
  1. 不需要预先分配固定大小的内存空间,可以根据需要动态分配内存,内存利用率较高。
(二)链表的缺点
  1. 访问元素时需要从头节点开始依次遍历,不能像数组那样通过下标直接访问,时间复杂度为 O (n)。
  1. 每个节点需要额外存储指针信息,消耗更多的内存空间。
(三)数组的优点
  1. 可以通过下标直接访问任意元素,访问效率高,时间复杂度为 O (1)。
  1. 不需要存储指针信息,内存开销相对较小。
(四)数组的缺点
  1. 插入和删除元素时需要移动大量元素,效率较低,时间复杂度为 O (n)。
  1. 需要预先分配固定大小的内存空间,可能会导致内存浪费或溢出。
五、链表的应用场景
  1. 实现栈和队列:链表可以方便地实现栈的 push 和 pop 操作,以及队列的 enqueue 和 dequeue 操作。
  1. 哈希表的冲突处理:在哈希表中,当发生哈希冲突时,常采用链表(链地址法)来存储冲突的元素。
  1. 图形结构的表示:在图的邻接表表示法中,每个顶点的邻接顶点通常用链表来存储。
  1. 频繁进行插入和删除操作的场景:如编辑器中的文本缓冲区,需要经常插入和删除字符,使用链表可以提高效率。


版权声明:倡导尊重与保护知识产权。未经许可,任何人不得复制、转载、或以其他方式使用本站《原创》内容,违者将追究其法律责任。本站文章内容,部分图片来源于网络,如有侵权,请联系我们修改或者删除处理。

编辑推荐

热门文章