IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> [数据结构] 四.循环链表 -> 正文阅读

[数据结构与算法][数据结构] 四.循环链表

目录

循环链表的基本概念

算法时间复杂度

C语言实现

循环单链表

循环双链表


循环链表的基本概念

循环链表是一种特殊的链表, 和普通链表的不同是它的尾节点的next指向了头结点

这样做的好处是从任何一个节点开始都能找到其他所有节点, 当然,不能保证时间复杂度较优

循环链表一般也有两种: 单循环链表双循环链表, 其实也就是有没有prior前驱指针的区别

这里就仅以单循环链表为例了:

注意: 空的单循环链表头结点的next指向头结点(他自己)

题外话: 记得王道有一个很坑的题:

问一个单循环链表的头结点为head, 如果head->next->next=head, 那么这个链表的长度是多少?

答案是0或者1, 原因一想只知道,但是很容易认为长度就是1[哭]

算法时间复杂度

和普通单链表几乎没有区别

  • 找到某个索引的节点: O(n), (不能随机访问,只能从头开始找)
  • 在头部/尾部插入节点: O(1)
  • 在中间某位置插入节点: O(n) (因为需要找到节点的位置)
  • 删除第一个结点(如果有头指针或者尾指针): O(1)
  • 删除最后一个节点 (不管是否有尾指针): O(n)
  • 删除中间某位置的节点: O(n) (也是因为需要找到节点位置)

这里有一个值的注意的点:

因为是循环链表, 所以尾指针比头指针有用, 两者可以不用同时有, 只要有尾指针就行了

但是如果是要删除最后一个节点, 两个指针都不好使了(需要的是指向倒数第二个节点的指针)

但是这时可以使用循环双链表(操作性能性能最优的链表)

C语言实现

循环单链表

/**
 * @file circular_linked_list.c
 * @author xiaoxiao (you@domain.com)
 * @brief 循环单链表
 * @version 0.1
 * @date 2022-03-04
 * 
 * @copyright Copyright (c) 2022
 * 
 */

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

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

typedef Node* PNode;

/**
 * @brief 初始化表头(无数据)
 * 
 * @return PNode 表头
 */
PNode initalList(){
    // 表头不存储数据
    // 最后一个节点的指针指向头结点,表示为空 
    PNode pHead = (PNode) malloc(sizeof(Node));
    pHead -> next = pHead;
    return pHead;
}

/**
 * @brief 在尾部插入一个元素
 * 
 * @param pHead 链表头
 * @param data 数据值
 */
void insertInTail(PNode pHead, int data){
    
    PNode pCurrent = pHead;
    while(pCurrent -> next != pHead){
        pCurrent = pCurrent -> next;
    }
    pCurrent -> next = (PNode)(malloc(sizeof(Node)));
    pCurrent -> next -> next = pHead;
    pCurrent -> next -> data = data;
}

/**
 * @brief 在任意索引插入一个元素
 * index=1是在表头后面(第一个元素)插入
 * 
 * @param pHead 链表头
 * @param index 索引
 * @param data 数据值
 */
bool insertByIndex(PNode pHead, int index, int data){
    PNode pCurrent = pHead;
    int i = 1;
    do{
        if(i == index){
            PNode pNewNode = (PNode) malloc (sizeof(Node)); // 新节点
            pNewNode -> data = data;
            pNewNode -> next = pCurrent -> next;
            pCurrent -> next = pNewNode;
            return true;
        }
        pCurrent = pCurrent -> next;
        i ++;
    }while(pCurrent != pHead);
    printf("索引大于链表长度+1,无法插入\n");
    return false;
}

/**
 * @brief 删除尾部元素
 * 
 * @param pHead 链表头
 */
bool deleteTail(PNode pHead){
    if(pHead -> next == pHead){
        printf("链表为空, 不能删除\n");
        return false;
    }
    PNode pCurrent = pHead;
    PNode pPrev = NULL;
    while(pCurrent -> next != pHead){
        pPrev = pCurrent;
        pCurrent = pCurrent -> next;
    }
    pPrev -> next = pHead;
    free(pCurrent);
    return true;
}

bool deleteByIndex(PNode pHead, int index){
    PNode pCurrent = pHead;
    for(int i = 1; i < index; i++){
        if(pCurrent -> next != pHead){
            pCurrent = pCurrent -> next;
        }
        else{
            printf("索引大于链表长度,无法删除\n");
            return false;
        }
        // pCurrent是要删除的前一个节点
    }
    if(pCurrent -> next == pHead){ // 这就是最后一个节点
        printf("索引大于链表长度, 无法插入\n");
        return false;
    }
    PNode pNext = pCurrent -> next -> next;
    free(pCurrent -> next);
    pCurrent -> next = pNext;
    return true;
}

/**
 * @brief 打印链表
 * 
 * @param pHead 链表头
 */
void printList(PNode pHead){
    if(pHead -> next == pHead){
        printf("空链表无法打印");
        return;
    }
    PNode pCurrent = pHead -> next;
    printf("打印链表: ");
    while(pCurrent != pHead){
        printf("%d ", pCurrent -> data);
        pCurrent = pCurrent -> next;
    }   
}

int main(){
    printf("----------------\n");
    PNode pHead = initalList();
    insertInTail(pHead, 2);
    deleteByIndex(pHead, 1);
    insertByIndex(pHead, 1, 3);
    deleteTail(pHead);
    printList(pHead);
    printf("\n----------------\n");
    return 0;
}

循环双链表

/**
 * @file double_circular_linked_list.c
 * @author xiaoxiao (you@domain.com)
 * @brief 循环双链表
 * @version 0.1
 * @date 2022-03-04
 * 
 * @copyright Copyright (c) 2022
 * 
 */

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

typedef struct Node {
    struct Node * prior; // 前驱结点
    struct Node * next; // 后继节点
    int data;
}Node;

typedef Node* PNode;

/**
 * @brief 初始化表头(无数据)
 * 
 * @return PNode 表头
 */
PNode initalList(){
    // 表头不存储数据
    // 最后一个节点的两个指针都指向头结点,表示为空链表 
    PNode pHead = (PNode) malloc(sizeof(Node));
    pHead -> next = pHead;
    pHead -> prior = pHead;
    return pHead;
}

/**
 * @brief 在尾部插入一个元素
 * 
 * @param pHead 链表头
 * @param data 数据值
 */
void insertInTail(PNode pHead, int data){
    
    PNode pCurrent = pHead;
    while(pCurrent -> next != pHead){
        pCurrent = pCurrent -> next;
    }
    // ppCurrent是原链表最后一个节点
    pCurrent -> next = (PNode)(malloc(sizeof(Node)));
    pCurrent -> next -> next = pHead;
    pCurrent -> next -> prior = pCurrent;
    pHead -> prior = pCurrent -> next; // 头结点的前驱指针指向最后一个节点
    pCurrent -> next -> data = data;
}

/**
 * @brief 在任意索引插入一个元素
 * index=1是在表头后面(第一个元素)插入
 * 
 * @param pHead 链表头
 * @param index 索引
 * @param data 数据值
 */
bool insertByIndex(PNode pHead, int index, int data){
    PNode pCurrent = pHead;
    int i = 1;
    do{
        if(i == index){
            PNode pNewNode = (PNode) malloc (sizeof(Node)); // 新节点
            pNewNode -> prior = pCurrent;
            pNewNode -> next = pCurrent -> next;
            pCurrent -> next -> prior = pNewNode;
            pCurrent -> next = pNewNode;
            pNewNode -> data = data;
            return true;
        }
        pCurrent = pCurrent -> next;
        i ++;
    }while(pCurrent != pHead);
    printf("索引大于链表长度+1,无法插入\n");
    return false;
}

/**
 * @brief 删除尾部元素
 * 
 * @param pHead 链表头
 */
bool deleteTail(PNode pHead){
    if(pHead -> next == pHead){
        printf("链表为空, 不能删除\n");
        return false;
    }
    PNode pCurrent = pHead;
    while(pCurrent -> next != pHead){
        pCurrent = pCurrent -> next;
    }
    pCurrent -> prior -> next = pHead; // 尾结点的上一个节点(新的尾结点)后驱指向头结点
    pHead -> prior = pCurrent -> prior; // 头结点的前驱指向尾结点的上一个节点
    free(pCurrent);
    return true;
}

bool deleteByIndex(PNode pHead, int index){
    if(pHead -> next = pHead){
        printf("空链表无法删除\n");
        return false;
    }
    PNode pCurrent = pHead -> next;
    for(int i = 1; i < index; i++){
        if(pCurrent -> next != pHead){
            pCurrent = pCurrent -> next;
        }
        else{
            printf("索引大于链表长度,无法删除\n");
            return false;
        }
    }
    // pCurrent是要删除的节点
    pCurrent -> next -> prior = pCurrent -> prior;
    pCurrent -> prior -> next = pCurrent -> next;
    free(pCurrent);
    return true;
}

/**
 * @brief 打印链表
 * 
 * @param pHead 链表头
 */
void printList(PNode pHead){
    if(pHead -> next == pHead){
        printf("空链表无法打印\n");
        return;
    }
    PNode pCurrent = pHead -> next;
    printf("打印链表: ");
    while(pCurrent != pHead){
        printf("%d ", pCurrent -> data);
        pCurrent = pCurrent -> next;
    }
    printf("\n");   
}

int main(){
    printf("----------------\n");
    PNode pHead = initalList();
    insertInTail(pHead, 2);
    insertByIndex(pHead, 1, 3);
    deleteTail(pHead);
    printList(pHead);
    printf("----------------\n");
    return 0;
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-03-06 13:22:01  更:2022-03-06 13:24:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 13:51:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码