03-双链表与循环链表

C语言数据结构系列:双链表与循环链表篇

C语言数据结构系列(三):双链表与循环链表

🎯 本篇目标:掌握双链表和循环链表的概念与实现,理解它们相比单链表的优势!


一、前言

哈喽小伙伴们!👋

上一篇我们学习了单链表,它只能单向遍历,想要找前一个节点还得从头开始找,太不方便了!😫

今天我们来学习两个链表的"进化版":

  • 双链表 :可以双向行走,前进后退都很方便!
  • 循环链表 :首尾相连,形成一个闭环,适合约瑟夫等问题!

#mermaid-svg-NOuo30If5vRy1W8E{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NOuo30If5vRy1W8E .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NOuo30If5vRy1W8E .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NOuo30If5vRy1W8E .error-icon{fill:#552222;}#mermaid-svg-NOuo30If5vRy1W8E .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NOuo30If5vRy1W8E .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NOuo30If5vRy1W8E .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NOuo30If5vRy1W8E .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NOuo30If5vRy1W8E .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NOuo30If5vRy1W8E .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NOuo30If5vRy1W8E .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NOuo30If5vRy1W8E .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NOuo30If5vRy1W8E .marker.cross{stroke:#333333;}#mermaid-svg-NOuo30If5vRy1W8E svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NOuo30If5vRy1W8E p{margin:0;}#mermaid-svg-NOuo30If5vRy1W8E .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NOuo30If5vRy1W8E .cluster-label text{fill:#333;}#mermaid-svg-NOuo30If5vRy1W8E .cluster-label span{color:#333;}#mermaid-svg-NOuo30If5vRy1W8E .cluster-label span p{background-color:transparent;}#mermaid-svg-NOuo30If5vRy1W8E .label text,#mermaid-svg-NOuo30If5vRy1W8E span{fill:#333;color:#333;}#mermaid-svg-NOuo30If5vRy1W8E .node rect,#mermaid-svg-NOuo30If5vRy1W8E .node circle,#mermaid-svg-NOuo30If5vRy1W8E .node ellipse,#mermaid-svg-NOuo30If5vRy1W8E .node polygon,#mermaid-svg-NOuo30If5vRy1W8E .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NOuo30If5vRy1W8E .rough-node .label text,#mermaid-svg-NOuo30If5vRy1W8E .node .label text,#mermaid-svg-NOuo30If5vRy1W8E .image-shape .label,#mermaid-svg-NOuo30If5vRy1W8E .icon-shape .label{text-anchor:middle;}#mermaid-svg-NOuo30If5vRy1W8E .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NOuo30If5vRy1W8E .rough-node .label,#mermaid-svg-NOuo30If5vRy1W8E .node .label,#mermaid-svg-NOuo30If5vRy1W8E .image-shape .label,#mermaid-svg-NOuo30If5vRy1W8E .icon-shape .label{text-align:center;}#mermaid-svg-NOuo30If5vRy1W8E .node.clickable{cursor:pointer;}#mermaid-svg-NOuo30If5vRy1W8E .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NOuo30If5vRy1W8E .arrowheadPath{fill:#333333;}#mermaid-svg-NOuo30If5vRy1W8E .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NOuo30If5vRy1W8E .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NOuo30If5vRy1W8E .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NOuo30If5vRy1W8E .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NOuo30If5vRy1W8E .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NOuo30If5vRy1W8E .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NOuo30If5vRy1W8E .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NOuo30If5vRy1W8E .cluster text{fill:#333;}#mermaid-svg-NOuo30If5vRy1W8E .cluster span{color:#333;}#mermaid-svg-NOuo30If5vRy1W8E div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NOuo30If5vRy1W8E .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NOuo30If5vRy1W8E rect.text{fill:none;stroke-width:0;}#mermaid-svg-NOuo30If5vRy1W8E .icon-shape,#mermaid-svg-NOuo30If5vRy1W8E .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NOuo30If5vRy1W8E .icon-shape p,#mermaid-svg-NOuo30If5vRy1W8E .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NOuo30If5vRy1W8E .icon-shape .label rect,#mermaid-svg-NOuo30If5vRy1W8E .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NOuo30If5vRy1W8E .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NOuo30If5vRy1W8E .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NOuo30If5vRy1W8E :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 双链表
NULL
节点1
节点2
NULL

让我们开始今天的学习吧! 🚀


二、双链表

2.1 什么是双链表?

双链表(Doubly Linked List) 每个节点包含三个域:前驱指针、数据域、后继指针。

就像一列双向火车🚆:

  • 每节车厢可以往前走,也可以往后走
  • 每节车厢都知道自己的前一节和后一节是谁

2.2 节点结构

#mermaid-svg-ZeHQKmO5WH7a93UO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZeHQKmO5WH7a93UO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZeHQKmO5WH7a93UO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZeHQKmO5WH7a93UO .error-icon{fill:#552222;}#mermaid-svg-ZeHQKmO5WH7a93UO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZeHQKmO5WH7a93UO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZeHQKmO5WH7a93UO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZeHQKmO5WH7a93UO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZeHQKmO5WH7a93UO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZeHQKmO5WH7a93UO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZeHQKmO5WH7a93UO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZeHQKmO5WH7a93UO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZeHQKmO5WH7a93UO .marker.cross{stroke:#333333;}#mermaid-svg-ZeHQKmO5WH7a93UO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZeHQKmO5WH7a93UO p{margin:0;}#mermaid-svg-ZeHQKmO5WH7a93UO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZeHQKmO5WH7a93UO .cluster-label text{fill:#333;}#mermaid-svg-ZeHQKmO5WH7a93UO .cluster-label span{color:#333;}#mermaid-svg-ZeHQKmO5WH7a93UO .cluster-label span p{background-color:transparent;}#mermaid-svg-ZeHQKmO5WH7a93UO .label text,#mermaid-svg-ZeHQKmO5WH7a93UO span{fill:#333;color:#333;}#mermaid-svg-ZeHQKmO5WH7a93UO .node rect,#mermaid-svg-ZeHQKmO5WH7a93UO .node circle,#mermaid-svg-ZeHQKmO5WH7a93UO .node ellipse,#mermaid-svg-ZeHQKmO5WH7a93UO .node polygon,#mermaid-svg-ZeHQKmO5WH7a93UO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZeHQKmO5WH7a93UO .rough-node .label text,#mermaid-svg-ZeHQKmO5WH7a93UO .node .label text,#mermaid-svg-ZeHQKmO5WH7a93UO .image-shape .label,#mermaid-svg-ZeHQKmO5WH7a93UO .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZeHQKmO5WH7a93UO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZeHQKmO5WH7a93UO .rough-node .label,#mermaid-svg-ZeHQKmO5WH7a93UO .node .label,#mermaid-svg-ZeHQKmO5WH7a93UO .image-shape .label,#mermaid-svg-ZeHQKmO5WH7a93UO .icon-shape .label{text-align:center;}#mermaid-svg-ZeHQKmO5WH7a93UO .node.clickable{cursor:pointer;}#mermaid-svg-ZeHQKmO5WH7a93UO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZeHQKmO5WH7a93UO .arrowheadPath{fill:#333333;}#mermaid-svg-ZeHQKmO5WH7a93UO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZeHQKmO5WH7a93UO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZeHQKmO5WH7a93UO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZeHQKmO5WH7a93UO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZeHQKmO5WH7a93UO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZeHQKmO5WH7a93UO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZeHQKmO5WH7a93UO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZeHQKmO5WH7a93UO .cluster text{fill:#333;}#mermaid-svg-ZeHQKmO5WH7a93UO .cluster span{color:#333;}#mermaid-svg-ZeHQKmO5WH7a93UO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZeHQKmO5WH7a93UO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZeHQKmO5WH7a93UO rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZeHQKmO5WH7a93UO .icon-shape,#mermaid-svg-ZeHQKmO5WH7a93UO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZeHQKmO5WH7a93UO .icon-shape p,#mermaid-svg-ZeHQKmO5WH7a93UO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZeHQKmO5WH7a93UO .icon-shape .label rect,#mermaid-svg-ZeHQKmO5WH7a93UO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZeHQKmO5WH7a93UO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZeHQKmO5WH7a93UO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZeHQKmO5WH7a93UO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 双链表节点
prev

前驱指针
data

数据域
next

后继指针

C语言结构体定义:

c 复制代码
// 双链表节点结构体
typedef struct DNode {
    int data;               // 数据域
    struct DNode *prev;     // 前驱指针
    struct DNode *next;     // 后继指针
} DNode;

2.3 双链表 vs 单链表

特性 单链表 双链表
指针数 1个(next) 2个(prev, next)
遍历方向 单向 双向
查找前驱 O(n) O(1)
空间开销 较小 较大
插入删除 需知道前驱 直接操作

2.4 双链表的基本操作

创建节点
c 复制代码
DNode* createDNode(int value) {
    DNode *newNode = (DNode*)malloc(sizeof(DNode));
    newNode->data = value;
    newNode->prev = NULL;
    newNode->next = NULL;
    return newNode;
}
头插法

#mermaid-svg-uAqDANVebp3IbRGe{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-uAqDANVebp3IbRGe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-uAqDANVebp3IbRGe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-uAqDANVebp3IbRGe .error-icon{fill:#552222;}#mermaid-svg-uAqDANVebp3IbRGe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-uAqDANVebp3IbRGe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-uAqDANVebp3IbRGe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-uAqDANVebp3IbRGe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-uAqDANVebp3IbRGe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-uAqDANVebp3IbRGe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-uAqDANVebp3IbRGe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-uAqDANVebp3IbRGe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-uAqDANVebp3IbRGe .marker.cross{stroke:#333333;}#mermaid-svg-uAqDANVebp3IbRGe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-uAqDANVebp3IbRGe p{margin:0;}#mermaid-svg-uAqDANVebp3IbRGe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-uAqDANVebp3IbRGe .cluster-label text{fill:#333;}#mermaid-svg-uAqDANVebp3IbRGe .cluster-label span{color:#333;}#mermaid-svg-uAqDANVebp3IbRGe .cluster-label span p{background-color:transparent;}#mermaid-svg-uAqDANVebp3IbRGe .label text,#mermaid-svg-uAqDANVebp3IbRGe span{fill:#333;color:#333;}#mermaid-svg-uAqDANVebp3IbRGe .node rect,#mermaid-svg-uAqDANVebp3IbRGe .node circle,#mermaid-svg-uAqDANVebp3IbRGe .node ellipse,#mermaid-svg-uAqDANVebp3IbRGe .node polygon,#mermaid-svg-uAqDANVebp3IbRGe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-uAqDANVebp3IbRGe .rough-node .label text,#mermaid-svg-uAqDANVebp3IbRGe .node .label text,#mermaid-svg-uAqDANVebp3IbRGe .image-shape .label,#mermaid-svg-uAqDANVebp3IbRGe .icon-shape .label{text-anchor:middle;}#mermaid-svg-uAqDANVebp3IbRGe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-uAqDANVebp3IbRGe .rough-node .label,#mermaid-svg-uAqDANVebp3IbRGe .node .label,#mermaid-svg-uAqDANVebp3IbRGe .image-shape .label,#mermaid-svg-uAqDANVebp3IbRGe .icon-shape .label{text-align:center;}#mermaid-svg-uAqDANVebp3IbRGe .node.clickable{cursor:pointer;}#mermaid-svg-uAqDANVebp3IbRGe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-uAqDANVebp3IbRGe .arrowheadPath{fill:#333333;}#mermaid-svg-uAqDANVebp3IbRGe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-uAqDANVebp3IbRGe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-uAqDANVebp3IbRGe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uAqDANVebp3IbRGe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-uAqDANVebp3IbRGe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uAqDANVebp3IbRGe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-uAqDANVebp3IbRGe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-uAqDANVebp3IbRGe .cluster text{fill:#333;}#mermaid-svg-uAqDANVebp3IbRGe .cluster span{color:#333;}#mermaid-svg-uAqDANVebp3IbRGe div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-uAqDANVebp3IbRGe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-uAqDANVebp3IbRGe rect.text{fill:none;stroke-width:0;}#mermaid-svg-uAqDANVebp3IbRGe .icon-shape,#mermaid-svg-uAqDANVebp3IbRGe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-uAqDANVebp3IbRGe .icon-shape p,#mermaid-svg-uAqDANVebp3IbRGe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-uAqDANVebp3IbRGe .icon-shape .label rect,#mermaid-svg-uAqDANVebp3IbRGe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-uAqDANVebp3IbRGe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-uAqDANVebp3IbRGe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-uAqDANVebp3IbRGe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} next
prev
双向连接
新节点
原头节点

c 复制代码
void insertAtHead(DNode **head, int value) {
    DNode *newNode = createDNode(value);
    
    if (*head != NULL) {
        newNode->next = *head;
        (*head)->prev = newNode;
    }
    
    *head = newNode;
}
尾插法
c 复制代码
void insertAtTail(DNode **head, int value) {
    DNode *newNode = createDNode(value);
    
    if (*head == NULL) {
        *head = newNode;
        return;
    }
    
    DNode *current = *head;
    while (current->next != NULL) {
        current = current->next;
    }
    
    current->next = newNode;
    newNode->prev = current;
}
指定位置插入
c 复制代码
void insertAtPos(DNode **head, int pos, int value) {
    if (pos == 0) {
        insertAtHead(head, value);
        return;
    }
    
    DNode *current = *head;
    for (int i = 0; i < pos && current != NULL; i++) {
        current = current->next;
    }
    
    if (current == NULL) {
        printf("位置无效!\n");
        return;
    }
    
    DNode *newNode = createDNode(value);
    newNode->prev = current->prev;
    newNode->next = current;
    current->prev->next = newNode;
    current->prev = newNode;
}
删除节点
c 复制代码
void deleteNode(DNode **head, int value) {
    if (*head == NULL) return;
    
    DNode *current = *head;
    
    // 找到要删除的节点
    while (current != NULL && current->data != value) {
        current = current->next;
    }
    
    if (current == NULL) {
        printf("未找到值为%d的节点!\n", value);
        return;
    }
    
    // 如果是头节点
    if (current->prev == NULL) {
        *head = current->next;
        if (*head != NULL)
            (*head)->prev = NULL;
    } else {
        current->prev->next = current->next;
        if (current->next != NULL)
            current->next->prev = current->prev;
    }
    
    free(current);
}
反向打印

双链表的独特优势------可以从后往前打印!

c 复制代码
void printReverse(DNode *head) {
    if (head == NULL) return;
    
    // 先走到最后
    DNode *current = head;
    while (current->next != NULL) {
        current = current->next;
    }
    
    // 从后往前打印
    while (current != NULL) {
        printf("%d", current->data);
        if (current->prev) printf(" <- ");
        current = current->prev;
    }
    printf("\n");
}

2.5 完整示例

c 复制代码
#include <stdio.h>
#include <stdlib.h>

typedef struct DNode {
    int data;
    struct DNode *prev;
    struct DNode *next;
} DNode;

DNode* createDNode(int value) {
    DNode *newNode = (DNode*)malloc(sizeof(DNode));
    newNode->data = value;
    newNode->prev = NULL;
    newNode->next = NULL;
    return newNode;
}

void insertAtHead(DNode **head, int value) {
    DNode *newNode = createDNode(value);
    if (*head != NULL) {
        newNode->next = *head;
        (*head)->prev = newNode;
    }
    *head = newNode;
}

void insertAtTail(DNode **head, int value) {
    DNode *newNode = createDNode(value);
    if (*head == NULL) {
        *head = newNode;
        return;
    }
    DNode *current = *head;
    while (current->next != NULL) {
        current = current->next;
    }
    current->next = newNode;
    newNode->prev = current;
}

void deleteNode(DNode **head, int value) {
    if (*head == NULL) return;
    
    DNode *current = *head;
    while (current != NULL && current->data != value) {
        current = current->next;
    }
    
    if (current == NULL) return;
    
    if (current->prev == NULL) {
        *head = current->next;
        if (*head) (*head)->prev = NULL;
    } else {
        current->prev->next = current->next;
        if (current->next) current->next->prev = current->prev;
    }
    free(current);
}

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

void printReverse(DNode *head) {
    if (head == NULL) return;
    DNode *current = head;
    while (current->next != NULL) current = current->next;
    while (current != NULL) {
        printf("%d", current->data);
        if (current->prev) printf(" <-> ");
        current = current->prev;
    }
    printf("\n");
}

void freeList(DNode **head) {
    DNode *current = *head;
    while (current != NULL) {
        DNode *temp = current;
        current = current->next;
        free(temp);
    }
    *head = NULL;
}

int main() {
    DNode *head = NULL;
    
    printf("===== 双链表演示 =====\n\n");
    
    insertAtTail(&head, 10);
    insertAtTail(&head, 20);
    insertAtTail(&head, 30);
    insertAtHead(&head, 5);
    
    printf("正向打印: ");
    printList(head);
    
    printf("反向打印: ");
    printReverse(head);
    
    printf("\n删除元素20后:\n");
    deleteNode(&head, 20);
    printf("正向打印: ");
    printList(head);
    
    freeList(&head);
    return 0;
}

运行结果:

复制代码
===== 双链表演示 =====

正向打印: 5 <-> 10 <-> 20 <-> 30
反向打印: 30 <-> 20 <-> 10 <-> 5

删除元素20后:
正向打印: 5 <-> 10 <-> 30

三、循环链表

3.1 什么是循环链表?

循环链表(Circular Linked List) 是一种首尾相接的链表,最后一个节点的next指向第一个节点,形成一个闭环!

就像一个旋转木马🎠:

  • 没有起点也没有终点
  • 可以一直循环下去
  • 适合处理约瑟夫问题、循环队列等

#mermaid-svg-7YVUCNWW8YuQZN5p{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7YVUCNWW8YuQZN5p .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7YVUCNWW8YuQZN5p .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7YVUCNWW8YuQZN5p .error-icon{fill:#552222;}#mermaid-svg-7YVUCNWW8YuQZN5p .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7YVUCNWW8YuQZN5p .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7YVUCNWW8YuQZN5p .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7YVUCNWW8YuQZN5p .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7YVUCNWW8YuQZN5p .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7YVUCNWW8YuQZN5p .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7YVUCNWW8YuQZN5p .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7YVUCNWW8YuQZN5p .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7YVUCNWW8YuQZN5p .marker.cross{stroke:#333333;}#mermaid-svg-7YVUCNWW8YuQZN5p svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7YVUCNWW8YuQZN5p p{margin:0;}#mermaid-svg-7YVUCNWW8YuQZN5p .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7YVUCNWW8YuQZN5p .cluster-label text{fill:#333;}#mermaid-svg-7YVUCNWW8YuQZN5p .cluster-label span{color:#333;}#mermaid-svg-7YVUCNWW8YuQZN5p .cluster-label span p{background-color:transparent;}#mermaid-svg-7YVUCNWW8YuQZN5p .label text,#mermaid-svg-7YVUCNWW8YuQZN5p span{fill:#333;color:#333;}#mermaid-svg-7YVUCNWW8YuQZN5p .node rect,#mermaid-svg-7YVUCNWW8YuQZN5p .node circle,#mermaid-svg-7YVUCNWW8YuQZN5p .node ellipse,#mermaid-svg-7YVUCNWW8YuQZN5p .node polygon,#mermaid-svg-7YVUCNWW8YuQZN5p .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7YVUCNWW8YuQZN5p .rough-node .label text,#mermaid-svg-7YVUCNWW8YuQZN5p .node .label text,#mermaid-svg-7YVUCNWW8YuQZN5p .image-shape .label,#mermaid-svg-7YVUCNWW8YuQZN5p .icon-shape .label{text-anchor:middle;}#mermaid-svg-7YVUCNWW8YuQZN5p .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7YVUCNWW8YuQZN5p .rough-node .label,#mermaid-svg-7YVUCNWW8YuQZN5p .node .label,#mermaid-svg-7YVUCNWW8YuQZN5p .image-shape .label,#mermaid-svg-7YVUCNWW8YuQZN5p .icon-shape .label{text-align:center;}#mermaid-svg-7YVUCNWW8YuQZN5p .node.clickable{cursor:pointer;}#mermaid-svg-7YVUCNWW8YuQZN5p .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7YVUCNWW8YuQZN5p .arrowheadPath{fill:#333333;}#mermaid-svg-7YVUCNWW8YuQZN5p .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7YVUCNWW8YuQZN5p .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7YVUCNWW8YuQZN5p .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7YVUCNWW8YuQZN5p .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7YVUCNWW8YuQZN5p .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7YVUCNWW8YuQZN5p .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7YVUCNWW8YuQZN5p .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7YVUCNWW8YuQZN5p .cluster text{fill:#333;}#mermaid-svg-7YVUCNWW8YuQZN5p .cluster span{color:#333;}#mermaid-svg-7YVUCNWW8YuQZN5p div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7YVUCNWW8YuQZN5p .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7YVUCNWW8YuQZN5p rect.text{fill:none;stroke-width:0;}#mermaid-svg-7YVUCNWW8YuQZN5p .icon-shape,#mermaid-svg-7YVUCNWW8YuQZN5p .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7YVUCNWW8YuQZN5p .icon-shape p,#mermaid-svg-7YVUCNWW8YuQZN5p .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7YVUCNWW8YuQZN5p .icon-shape .label rect,#mermaid-svg-7YVUCNWW8YuQZN5p .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7YVUCNWW8YuQZN5p .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7YVUCNWW8YuQZN5p .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7YVUCNWW8YuQZN5p :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 回到起点
节点1
节点2
节点3
节点4

3.2 循环链表 vs 普通链表

特性 普通链表 循环链表
尾节点next NULL 指向头节点
遍历结束条件 current == NULL current->next == head
形成结构 线性 环形

3.3 循环链表的基本操作

创建节点
c 复制代码
CLNode* createCLNode(int value) {
    CLNode *newNode = (CLNode*)malloc(sizeof(CLNode));
    newNode->data = value;
    newNode->next = NULL;
    return newNode;
}
尾插法

#mermaid-svg-HEb72lpz36VfYbqX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HEb72lpz36VfYbqX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HEb72lpz36VfYbqX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HEb72lpz36VfYbqX .error-icon{fill:#552222;}#mermaid-svg-HEb72lpz36VfYbqX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HEb72lpz36VfYbqX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HEb72lpz36VfYbqX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HEb72lpz36VfYbqX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HEb72lpz36VfYbqX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HEb72lpz36VfYbqX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HEb72lpz36VfYbqX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HEb72lpz36VfYbqX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HEb72lpz36VfYbqX .marker.cross{stroke:#333333;}#mermaid-svg-HEb72lpz36VfYbqX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HEb72lpz36VfYbqX p{margin:0;}#mermaid-svg-HEb72lpz36VfYbqX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HEb72lpz36VfYbqX .cluster-label text{fill:#333;}#mermaid-svg-HEb72lpz36VfYbqX .cluster-label span{color:#333;}#mermaid-svg-HEb72lpz36VfYbqX .cluster-label span p{background-color:transparent;}#mermaid-svg-HEb72lpz36VfYbqX .label text,#mermaid-svg-HEb72lpz36VfYbqX span{fill:#333;color:#333;}#mermaid-svg-HEb72lpz36VfYbqX .node rect,#mermaid-svg-HEb72lpz36VfYbqX .node circle,#mermaid-svg-HEb72lpz36VfYbqX .node ellipse,#mermaid-svg-HEb72lpz36VfYbqX .node polygon,#mermaid-svg-HEb72lpz36VfYbqX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HEb72lpz36VfYbqX .rough-node .label text,#mermaid-svg-HEb72lpz36VfYbqX .node .label text,#mermaid-svg-HEb72lpz36VfYbqX .image-shape .label,#mermaid-svg-HEb72lpz36VfYbqX .icon-shape .label{text-anchor:middle;}#mermaid-svg-HEb72lpz36VfYbqX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HEb72lpz36VfYbqX .rough-node .label,#mermaid-svg-HEb72lpz36VfYbqX .node .label,#mermaid-svg-HEb72lpz36VfYbqX .image-shape .label,#mermaid-svg-HEb72lpz36VfYbqX .icon-shape .label{text-align:center;}#mermaid-svg-HEb72lpz36VfYbqX .node.clickable{cursor:pointer;}#mermaid-svg-HEb72lpz36VfYbqX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HEb72lpz36VfYbqX .arrowheadPath{fill:#333333;}#mermaid-svg-HEb72lpz36VfYbqX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HEb72lpz36VfYbqX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HEb72lpz36VfYbqX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HEb72lpz36VfYbqX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HEb72lpz36VfYbqX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HEb72lpz36VfYbqX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HEb72lpz36VfYbqX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HEb72lpz36VfYbqX .cluster text{fill:#333;}#mermaid-svg-HEb72lpz36VfYbqX .cluster span{color:#333;}#mermaid-svg-HEb72lpz36VfYbqX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HEb72lpz36VfYbqX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HEb72lpz36VfYbqX rect.text{fill:none;stroke-width:0;}#mermaid-svg-HEb72lpz36VfYbqX .icon-shape,#mermaid-svg-HEb72lpz36VfYbqX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HEb72lpz36VfYbqX .icon-shape p,#mermaid-svg-HEb72lpz36VfYbqX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HEb72lpz36VfYbqX .icon-shape .label rect,#mermaid-svg-HEb72lpz36VfYbqX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HEb72lpz36VfYbqX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HEb72lpz36VfYbqX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HEb72lpz36VfYbqX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 插入
回到起点
节点1
节点2
节点3
新节点

c 复制代码
void insertAtTail(CLNode **head, int value) {
    CLNode *newNode = createCLNode(value);
    
    if (*head == NULL) {
        *head = newNode;
        newNode->next = *head;  // 指向自己
        return;
    }
    
    CLNode *current = *head;
    while (current->next != *head) {
        current = current->next;
    }
    
    current->next = newNode;
    newNode->next = *head;  // 新节点指向头节点
}
打印循环链表
c 复制代码
void printList(CLNode *head) {
    if (head == NULL) return;
    
    CLNode *current = head;
    do {
        printf("%d", current->data);
        if (current->next != head) printf(" -> ");
        current = current->next;
    } while (current != head);
    printf(" (回到%d)\n", head->data);
}
删除节点
c 复制代码
void deleteNode(CLNode **head, int value) {
    if (*head == NULL) return;
    
    CLNode *current = *head;
    CLNode *prev = NULL;
    
    // 找到要删除的节点
    do {
        if (current->data == value) break;
        prev = current;
        current = current->next;
    } while (current != *head);
    
    // 没找到
    if (current->data != value) {
        printf("未找到值为%d的节点!\n", value);
        return;
    }
    
    // 只有一个节点
    if (current->next == current) {
        *head = NULL;
    } else {
        if (current == *head) {
            *head = current->next;
        }
        prev->next = current->next;
    }
    
    free(current);
}

3.4 约瑟夫问题(经典应用)

💡 问题描述:n个人围成一圈,从第一个人开始报数,报到m的人出圈,求出圈顺序。
#mermaid-svg-VhhVG8QIE3gEEytp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VhhVG8QIE3gEEytp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VhhVG8QIE3gEEytp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VhhVG8QIE3gEEytp .error-icon{fill:#552222;}#mermaid-svg-VhhVG8QIE3gEEytp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VhhVG8QIE3gEEytp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VhhVG8QIE3gEEytp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VhhVG8QIE3gEEytp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VhhVG8QIE3gEEytp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VhhVG8QIE3gEEytp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VhhVG8QIE3gEEytp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VhhVG8QIE3gEEytp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VhhVG8QIE3gEEytp .marker.cross{stroke:#333333;}#mermaid-svg-VhhVG8QIE3gEEytp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VhhVG8QIE3gEEytp p{margin:0;}#mermaid-svg-VhhVG8QIE3gEEytp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VhhVG8QIE3gEEytp .cluster-label text{fill:#333;}#mermaid-svg-VhhVG8QIE3gEEytp .cluster-label span{color:#333;}#mermaid-svg-VhhVG8QIE3gEEytp .cluster-label span p{background-color:transparent;}#mermaid-svg-VhhVG8QIE3gEEytp .label text,#mermaid-svg-VhhVG8QIE3gEEytp span{fill:#333;color:#333;}#mermaid-svg-VhhVG8QIE3gEEytp .node rect,#mermaid-svg-VhhVG8QIE3gEEytp .node circle,#mermaid-svg-VhhVG8QIE3gEEytp .node ellipse,#mermaid-svg-VhhVG8QIE3gEEytp .node polygon,#mermaid-svg-VhhVG8QIE3gEEytp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VhhVG8QIE3gEEytp .rough-node .label text,#mermaid-svg-VhhVG8QIE3gEEytp .node .label text,#mermaid-svg-VhhVG8QIE3gEEytp .image-shape .label,#mermaid-svg-VhhVG8QIE3gEEytp .icon-shape .label{text-anchor:middle;}#mermaid-svg-VhhVG8QIE3gEEytp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VhhVG8QIE3gEEytp .rough-node .label,#mermaid-svg-VhhVG8QIE3gEEytp .node .label,#mermaid-svg-VhhVG8QIE3gEEytp .image-shape .label,#mermaid-svg-VhhVG8QIE3gEEytp .icon-shape .label{text-align:center;}#mermaid-svg-VhhVG8QIE3gEEytp .node.clickable{cursor:pointer;}#mermaid-svg-VhhVG8QIE3gEEytp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VhhVG8QIE3gEEytp .arrowheadPath{fill:#333333;}#mermaid-svg-VhhVG8QIE3gEEytp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VhhVG8QIE3gEEytp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VhhVG8QIE3gEEytp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VhhVG8QIE3gEEytp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VhhVG8QIE3gEEytp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VhhVG8QIE3gEEytp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VhhVG8QIE3gEEytp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VhhVG8QIE3gEEytp .cluster text{fill:#333;}#mermaid-svg-VhhVG8QIE3gEEytp .cluster span{color:#333;}#mermaid-svg-VhhVG8QIE3gEEytp div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VhhVG8QIE3gEEytp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VhhVG8QIE3gEEytp rect.text{fill:none;stroke-width:0;}#mermaid-svg-VhhVG8QIE3gEEytp .icon-shape,#mermaid-svg-VhhVG8QIE3gEEytp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VhhVG8QIE3gEEytp .icon-shape p,#mermaid-svg-VhhVG8QIE3gEEytp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VhhVG8QIE3gEEytp .icon-shape .label rect,#mermaid-svg-VhhVG8QIE3gEEytp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VhhVG8QIE3gEEytp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VhhVG8QIE3gEEytp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VhhVG8QIE3gEEytp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1
2
3
4
5

代码实现:

c 复制代码
void josephus(int n, int m) {
    // 创建循环链表
    CLNode *head = NULL;
    for (int i = n; i >= 1; i--) {
        insertAtTail(&head, i);
    }
    
    printf("出圈顺序: ");
    
    // 从第一个人开始,报数m-1次后删除
    CLNode *current = head;
    CLNode *prev = NULL;
    
    while (current->next != current) {
        // 报数m-1次
        for (int i = 1; i < m; i++) {
            prev = current;
            current = current->next;
        }
        
        printf("%d ", current->data);
        
        // 删除当前节点
        prev->next = current->next;
        free(current);
        current = prev->next;
    }
    
    printf("%d\n", current->data);  // 最后一个
    free(current);
}

测试:

c 复制代码
int main() {
    printf("===== 约瑟夫问题 =====\n");
    printf("n=5, m=3\n");
    josephus(5, 3);
    return 0;
}

运行结果:

复制代码
===== 约瑟夫问题 =====
n=5, m=3
出圈顺序: 3 1 5 2 4

四、双向循环链表

💡 双向循环链表:结合双链表和循环链表的优点,既可以双向遍历,又可以循环!

4.1 节点结构

c 复制代码
typedef struct DCNode {
    int data;
    struct DCNode *prev;
    struct DCNode *next;
} DCNode;

4.2 创建和插入

c 复制代码
DCNode* createDCNode(int value) {
    DCNode *newNode = (DCNode*)malloc(sizeof(DCNode));
    newNode->data = value;
    newNode->prev = NULL;
    newNode->next = NULL;
    return newNode;
}

void insertAtTail(DCNode **head, int value) {
    DCNode *newNode = createDCNode(value);
    
    if (*head == NULL) {
        *head = newNode;
        newNode->prev = newNode;
        newNode->next = newNode;
        return;
    }
    
    DCNode *tail = (*head)->prev;
    
    tail->next = newNode;
    newNode->prev = tail;
    newNode->next = *head;
    (*head)->prev = newNode;
}

4.3 打印

c 复制代码
void printList(DCNode *head) {
    if (head == NULL) return;
    
    DCNode *current = head;
    do {
        printf("%d", current->data);
        if (current->next != head) printf(" <-> ");
        current = current->next;
    } while (current != head);
    printf("\n");
}

五、复杂度对比

操作 单链表 双链表 循环链表
头插法 O(1) O(1) O(1)
尾插法 O(n) O(n) O(1)*
删除已知节点 O(n) O(1) O(n)
查找前驱 O(n) O(1) O(n)
向前遍历

*已知尾指针的情况下


六、应用场景

双链表的应用

  1. 浏览器的前进/后退 🔙

    • 每个页面是一个节点,prev指向前一页,next指向后一页
  2. 音乐播放器 🎵

    • 上一首/下一首功能
  3. LRU缓存 💾

    • 最近最少使用算法

循环链表的应用

  1. 约瑟夫问题 👥

    • 多人围成一圈报数
  2. 轮询调度

    • 操作系统的时间片轮转
  3. 循环队列 🔄

    • 打印任务队列
  4. 音乐播放循环 🔁

    • 单曲循环、列表循环

七、练习题 🎯

🏠 基础题

题目1:合并两个有序双链表

将两个有序双链表合并为一个新的有序双链表。


题目2:判断回文链表

判断一个链表是否是回文链表(正读反读都一样)。

c 复制代码
// 示例
1 -> 2 -> 2 -> 1  // 是回文
1 -> 2 -> 3 -> 4  // 不是回文

提示:可以使用快慢指针找到中间,然后反转后半部分比较。


💪 进阶题

题目3:LRU缓存实现

使用双向链表实现一个LRU缓存。

题目4:约瑟夫变体

n个人围成一圈,每个人有不同的权重,权重大的可以跳过多次。


八、总结

今天我们学习了两种链表的变体:

双链表

  • 每个节点有prev和next两个指针
  • 可以双向遍历
  • 删除已知节点O(1)
  • 空间开销稍大

循环链表

  • 尾节点指向头节点,形成闭环
  • 适合处理循环问题
  • 约瑟夫问题的经典解法

双向循环链表

  • 结合两者优点
  • Linux内核链表就是这种结构

选择建议:

  • 需要双向遍历 → 双链表
  • 需要循环操作 → 循环链表
  • 需要两者兼备 → 双向循环链表

九、下篇预告

下一篇我们将学习 栈(Stack)------后进先出的数据结构!
#mermaid-svg-LyhqlaLdf5VNLWr9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LyhqlaLdf5VNLWr9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LyhqlaLdf5VNLWr9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LyhqlaLdf5VNLWr9 .error-icon{fill:#552222;}#mermaid-svg-LyhqlaLdf5VNLWr9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LyhqlaLdf5VNLWr9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LyhqlaLdf5VNLWr9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LyhqlaLdf5VNLWr9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LyhqlaLdf5VNLWr9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LyhqlaLdf5VNLWr9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LyhqlaLdf5VNLWr9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LyhqlaLdf5VNLWr9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LyhqlaLdf5VNLWr9 .marker.cross{stroke:#333333;}#mermaid-svg-LyhqlaLdf5VNLWr9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LyhqlaLdf5VNLWr9 p{margin:0;}#mermaid-svg-LyhqlaLdf5VNLWr9 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-LyhqlaLdf5VNLWr9 .cluster-label text{fill:#333;}#mermaid-svg-LyhqlaLdf5VNLWr9 .cluster-label span{color:#333;}#mermaid-svg-LyhqlaLdf5VNLWr9 .cluster-label span p{background-color:transparent;}#mermaid-svg-LyhqlaLdf5VNLWr9 .label text,#mermaid-svg-LyhqlaLdf5VNLWr9 span{fill:#333;color:#333;}#mermaid-svg-LyhqlaLdf5VNLWr9 .node rect,#mermaid-svg-LyhqlaLdf5VNLWr9 .node circle,#mermaid-svg-LyhqlaLdf5VNLWr9 .node ellipse,#mermaid-svg-LyhqlaLdf5VNLWr9 .node polygon,#mermaid-svg-LyhqlaLdf5VNLWr9 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LyhqlaLdf5VNLWr9 .rough-node .label text,#mermaid-svg-LyhqlaLdf5VNLWr9 .node .label text,#mermaid-svg-LyhqlaLdf5VNLWr9 .image-shape .label,#mermaid-svg-LyhqlaLdf5VNLWr9 .icon-shape .label{text-anchor:middle;}#mermaid-svg-LyhqlaLdf5VNLWr9 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LyhqlaLdf5VNLWr9 .rough-node .label,#mermaid-svg-LyhqlaLdf5VNLWr9 .node .label,#mermaid-svg-LyhqlaLdf5VNLWr9 .image-shape .label,#mermaid-svg-LyhqlaLdf5VNLWr9 .icon-shape .label{text-align:center;}#mermaid-svg-LyhqlaLdf5VNLWr9 .node.clickable{cursor:pointer;}#mermaid-svg-LyhqlaLdf5VNLWr9 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LyhqlaLdf5VNLWr9 .arrowheadPath{fill:#333333;}#mermaid-svg-LyhqlaLdf5VNLWr9 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LyhqlaLdf5VNLWr9 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LyhqlaLdf5VNLWr9 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LyhqlaLdf5VNLWr9 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LyhqlaLdf5VNLWr9 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LyhqlaLdf5VNLWr9 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LyhqlaLdf5VNLWr9 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LyhqlaLdf5VNLWr9 .cluster text{fill:#333;}#mermaid-svg-LyhqlaLdf5VNLWr9 .cluster span{color:#333;}#mermaid-svg-LyhqlaLdf5VNLWr9 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LyhqlaLdf5VNLWr9 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LyhqlaLdf5VNLWr9 rect.text{fill:none;stroke-width:0;}#mermaid-svg-LyhqlaLdf5VNLWr9 .icon-shape,#mermaid-svg-LyhqlaLdf5VNLWr9 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LyhqlaLdf5VNLWr9 .icon-shape p,#mermaid-svg-LyhqlaLdf5VNLWr9 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LyhqlaLdf5VNLWr9 .icon-shape .label rect,#mermaid-svg-LyhqlaLdf5VNLWr9 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LyhqlaLdf5VNLWr9 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LyhqlaLdf5VNLWr9 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LyhqlaLdf5VNLWr9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 入栈 push
栈顶
出栈 pop

栈可以用数组或链表实现,让我们拭目以待! 🚀


💡 学习建议:双链表操作时要特别注意指针的指向,建议画图理解!

觉得有帮助就点赞+收藏+关注吧! 👍

有问题欢迎在评论区留言~ 💬

相关推荐
是隼人2 小时前
buuctf-pwn pwnable_start(ret2shellcode)题解(学习过程持续更新)
c语言·学习·安全·pwn入门·ctf入门
wabs6662 小时前
关于二叉树【力扣107.二叉树的层序遍历II的思考】
数据结构·c++·算法·leetcode·二叉树·层序遍历
小七在进步3 小时前
数据结构:非比较排序:计数排序
数据结构
一木 之林3 小时前
四、STL 容器与数据结构(进阶)(二)
数据结构·c++·哈希算法
ysu_03143 小时前
02-单链表完全指南
c语言·数据结构·算法·leetcode
ltqshs3 小时前
C语言-链表例程
c语言·开发语言·链表
Lyyaoo.3 小时前
【链表】【中等】两数相加/倒N删除/两个交换/排序链表/LRU缓存
数据结构·链表·缓存
YYYing.3 小时前
【C++进阶系列 (一)】关于线程堆栈的那些事 (上篇)
c语言·开发语言·c++·线程堆栈
Discipline~Hai3 小时前
Linux网络编程05-sqlite3数据库
linux·c语言·网络·数据库·sqlite·linux应用软件编程