• [C/C++]数据结构 链表(单向链表,双向链表)


    前言:

            上一文中我们介绍了顺序表的特点及实现,但是顺序表由于每次扩容都是呈二倍增长(扩容大小是自己定义的),可能会造成空间的大量浪费,但是链表却可以解决这个问题.

    概念及结构:

             链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的

    注意:

    1. 链式结构在逻辑上是连续的,但是在物理上不一定连续
    2. 结点一般都是从堆上申请出来的
    3. 从堆上申请的空间是按照一定的策略来分配的,两次申请的空间可能连续也可能不连续

        分类:

    1. 单向或双向
    2. 带头或不带头
    3. 循环或不循环

        虽然链表有这么多种结构,但是在实际中最常用的还是两种结构:

    • 无头单向非循环链表:

              结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈       希桶、图的邻接表等等。       

    •  带头双向循环链表:

           结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单

    无头单向非循环链表

    接口:

    1. typedef int SLTDateType;
    2. typedef struct SListNode
    3. {
    4. SLTDateType data;
    5. struct SListNode* next;
    6. }SListNode;
    7. // 动态申请一个节点
    8. SListNode* BuySListNode(SLTDateType x);
    9. // 单链表打印
    10. void SListPrint(SListNode* plist);
    11. // 单链表尾插
    12. void SListPushBack(SListNode** pplist, SLTDateType x);
    13. // 单链表的头插
    14. void SListPushFront(SListNode** pplist, SLTDateType x);
    15. // 单链表的尾删
    16. void SListPopBack(SListNode** pplist);
    17. // 单链表头删
    18. void SListPopFront(SListNode** pplist);
    19. // 单链表查找
    20. SListNode* SListFind(SListNode* plist, SLTDateType x);
    21. // 单链表在pos位置之后插入x
    22. void SListInsertAfter(SListNode** pphead,SListNode* pos, SLTDateType x);
    23. // 单链表删除pos位置之后的值
    24. void SListEraseAfter(SListNode** pphead,SListNode* pos);
    25. // 在pos的前面插入
    26. SListNode* SListInsertFront(SListNode** pphead, SListNode* pos, SLTDateType x);
    27. // 删除pos位置
    28. void SLTErase(SListNode** pphead, SListNode* pos);
    29. //单链表摧毁
    30. void SLTDestroy(SLNode** pphead);

    1.动态申请结点

    1. SListNode* BuySListNode(SLTDateType x)
    2. {
    3. SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
    4. if (newnode == NULL)
    5. {
    6. perror("malloc");
    7. return;
    8. }
    9. newnode->data = x;
    10. newnode->next = NULL;
    11. }

    2.单链表打印

    1. void SListPrint(SListNode* plist)
    2. {
    3. SListNode* cur = plist;
    4. while (cur != NULL)
    5. {
    6. printf("%d->", cur->data);
    7. cur = cur->next;
    8. }
    9. printf("NULL\n");
    10. }

    3.单链表尾插

    1. void SListPushBack(SListNode** pplist, SLTDateType x)
    2. {
    3. SListNode* newnode = BuySListNode(x);
    4. if (*pplist == NULL)
    5. {
    6. *pplist = newnode;
    7. }
    8. else
    9. {
    10. SListNode* tail = *pplist;
    11. while (tail->next)
    12. {
    13. tail = tail->next;
    14. }
    15. tail->next = newnode;
    16. }
    17. }

    4.单链表头插

    1. void SListPushFront(SListNode** pplist, SLTDateType x)
    2. {
    3. SListNode* newnode = BuySListNode(x);
    4. newnode->next = *pplist;
    5. *pplist = newnode;
    6. }

    5.单链表尾删

    1. void SListPopBack(SListNode** pplist)
    2. {
    3. assert(*pplist);
    4. SListNode* pre = NULL;
    5. SListNode* tail = *pplist;
    6. if ((*pplist)->next == NULL)
    7. {
    8. *pplist = NULL;
    9. }
    10. else
    11. {
    12. /* while (tail->next)
    13. {
    14. pre = tail;
    15. tail = tail->next;
    16. }
    17. free(tail);
    18. tail = NULL;
    19. pre->next = NULL;*/
    20. SListNode* tail = *pplist;
    21. while (tail->next->next)
    22. {
    23. tail = tail->next;
    24. }
    25. free(tail->next);
    26. tail->next = NULL;
    27. }
    28. }

    6.单链表头删

    1. void SListPopFront(SListNode** pplist)
    2. {
    3. assert(pplist);
    4. if ((*pplist)->next == NULL)
    5. {
    6. *pplist = NULL;
    7. }
    8. else
    9. {
    10. SListNode* ret = ((*pplist)->next);
    11. free(*pplist);
    12. *pplist = ret;
    13. }
    14. }

    7.单链表查找

    1. SListNode* SListFind(SListNode* plist, SLTDateType x)
    2. {
    3. assert(plist);
    4. SListNode* ret = plist;
    5. while (ret->data != x&&ret->next)
    6. {
    7. ret=ret->next;
    8. }
    9. if (ret->next == NULL && ret->data != x)
    10. return NULL;
    11. else
    12. return ret;
    13. }

    8.摧毁单链表

    1. void SLTDestroy(SListNode** pphead)
    2. {
    3. SListNode* cur = *pphead;
    4. SListNode* ret = NULL;
    5. while (cur)
    6. {
    7. ret = cur->next;
    8. free(cur);
    9. cur = ret;
    10. }
    11. *pphead = NULL;
    12. }

    9.在pos位置之后插入x

    1. // 单链表在pos位置之后插入x
    2. void SListInsertAfter(SListNode** pphead,SListNode* pos, SLTDateType x)
    3. {
    4. assert(pphead);
    5. //检测插入位置是否存在
    6. assert(pos);
    7. assert(*pphead);
    8. SListNode* newnode=BuySListNode(x);
    9. newnode->next = pos->next;
    10. pos->next = newnode;
    11. }

    10.删除pos位置之后的值

    1. // 单链表删除pos位置之后的值
    2. void SListEraseAfter(SListNode** pphead,SListNode* pos)
    3. {
    4. assert(pphead);
    5. assert(pos);
    6. assert(pos->next);
    7. assert(*pphead);
    8. SListNode* tmp = pos->next;
    9. pos->next = pos->next->next;
    10. free(tmp);
    11. tmp = NULL;
    12. }

    11.在pos位置之前插入x

    1. SListNode* SListInsertFront(SListNode** pphead, SListNode* pos, SLTDateType x)
    2. {
    3. assert(pphead);
    4. assert(pos);
    5. assert(*pphead);
    6. SListNode* newnode = BuySListNode(x);
    7. SListNode* pre = *pphead;
    8. if (*pphead == pos)
    9. {
    10. SListPushFront(pphead,x);
    11. }
    12. else
    13. {
    14. while (pre->next != pos)
    15. {
    16. pre = pre->next;
    17. }
    18. newnode->next = pos;
    19. pre->next = newnode;
    20. }
    21. }

    12.删除pos位置

    1. void SLTErase(SListNode** pphead, SListNode* pos)
    2. {
    3. assert(pphead);
    4. assert(pos);
    5. assert(*pphead);
    6. if (*pphead == pos)
    7. {
    8. SListPopFront(pphead);
    9. }
    10. else
    11. {
    12. SListNode* pre = *pphead;
    13. while (pre->next != pos)
    14. {
    15. pre = pre->next;
    16. }
    17. pre->next = pos->next;
    18. free(pos);
    19. pos = NULL;
    20. }
    21. }

     

    带头双向循环链表

    接口:

    1. // 带头+双向+循环链表增删查改实现
    2. typedef int LTDataType;
    3. typedef struct ListNode
    4. {
    5. LTDataType _data;
    6. struct ListNode* _next;
    7. struct ListNode* _prev;
    8. }ListNode;
    9. // 创建返回链表的头结点.
    10. ListNode* ListCreate();
    11. // 双向链表销毁
    12. void ListDestory(ListNode* pHead);
    13. // 双向链表打印
    14. void ListPrint(ListNode* pHead);
    15. // 双向链表尾插
    16. void ListPushBack(ListNode* pHead, LTDataType x);
    17. // 双向链表尾删
    18. void ListPopBack(ListNode* pHead);
    19. // 双向链表头插
    20. void ListPushFront(ListNode* pHead, LTDataType x);
    21. // 双向链表头删
    22. void ListPopFront(ListNode* pHead);
    23. // 双向链表查找
    24. ListNode* ListFind(ListNode* pHead, LTDataType x);
    25. // 双向链表在pos的前面进行插入
    26. void ListInsert(ListNode* pos, LTDataType x);
    27. // 双向链表删除pos位置的节点
    28. void ListErase(ListNode* pos);

    1.创建返回链表的头节点

    1. ListNode* ListCreate(LTDataType x)
    2. {
    3. ListNode* node = (ListNode*)malloc(sizeof(ListNode));
    4. if (node == NULL)
    5. {
    6. perror("malloc");
    7. exit(-1);
    8. }
    9. node->_data = x;
    10. node->_next = NULL;
    11. node->_prev = NULL;
    12. return node;
    13. }

    2.双向链表销毁

    1. void ListDestory(ListNode* pHead)
    2. {
    3. assert(pHead);
    4. ListNode* cur = pHead->_next;
    5. while (cur != pHead)
    6. {
    7. ListNode* next = cur->_next;
    8. free(cur);
    9. cur = next;
    10. }
    11. free(pHead);
    12. }

    3.双向链表打印

    1. void ListPrint(ListNode* pHead)
    2. {
    3. assert(pHead);
    4. ListNode* cur = pHead->_next;
    5. while (cur != pHead)
    6. {
    7. printf("%d <-> ", cur->_data);
    8. cur = cur->_next;
    9. }
    10. }

    4.双向链表尾插

    1. void ListPushBack(ListNode* pHead, LTDataType x)
    2. {
    3. ListNode* newnode = ListCreate(x);
    4. ListNode* tail = pHead->_prev; //尾指针
    5. tail->_next = newnode;
    6. newnode->_next = pHead;
    7. newnode->_prev = tail;
    8. pHead->_prev = newnode;
    9. }

    5.双向链表尾删

    1. void ListPopBack(ListNode* pHead)
    2. {
    3. assert(pHead);
    4. assert(pHead->_next!=pHead);
    5. ListNode* tail = pHead->_prev;
    6. pHead->_prev = tail->_prev;
    7. tail->_prev->_next = pHead;
    8. free(tail);
    9. tail = NULL;
    10. }

    6.双向链表头插

    1. void ListPushFront(ListNode* pHead, LTDataType x)
    2. {
    3. assert(pHead);
    4. ListNode* newnode = ListCreate(x);
    5. newnode->_next = pHead->_next;
    6. pHead->_next->_prev = newnode;
    7. pHead->_next = newnode;
    8. newnode->_prev = pHead;
    9. }

    7.双向链表头删

    1. void ListPopFront(ListNode* pHead)
    2. {
    3. ListNode* pHeadNext = pHead->_next;
    4. pHeadNext->_next->_prev = pHead;
    5. pHead->_next = pHeadNext->_next;
    6. free(pHeadNext);
    7. pHeadNext = NULL;
    8. }

    8.双向链表查找

    1. ListNode* ListFind(ListNode* pHead, LTDataType x)
    2. {
    3. assert(pHead);
    4. ListNode* cur = pHead->_next;
    5. while (cur != pHead)
    6. {
    7. if (cur->_data == x)
    8. return cur;
    9. cur = cur->_next;
    10. }
    11. return NULL;
    12. }

    9.双向链表在pos的前面进行插入

    1. void ListInsert(ListNode* pos, LTDataType x)
    2. {
    3. assert(pos);
    4. ListNode* posprev = pos->_prev;
    5. ListNode* newnode = ListCreate(x);
    6. newnode->_next = pos;
    7. pos->_prev = newnode;
    8. posprev->_next = newnode;
    9. newnode->_prev = pos->_prev;
    10. }

    10.双向链表删除pos位置的节点

    1. void ListErase(ListNode* pos)
    2. {
    3. assert(pos);
    4. ListNode* posprev = pos->_prev;
    5. ListNode* posnext = pos->_next;
    6. posprev->_next = posnext;
    7. posnext->_prev = posprev;
    8. free(pos);
    9. }

  • 相关阅读:
    window.addEventListener相关参数介绍说明
    llama笔记:官方示例解析 example_chat_completion.py
    [BJDCTF 2020]Easy
    项目立项管理
    Promise的Catch报错总结
    开源在线课堂知识付费源码系统 PHP+MySQL组合开发 支持视频、音频、图文章节 带完整的搭建教程
    设计模式浅析(一) ·策略模式
    进制转换(二进制、八进制、十进制、十六进制)
    纯前端读写文件?
    docker安装及使用-Linux
  • 原文地址:https://blog.csdn.net/m0_74910646/article/details/134474797