数据结构之双向链表-2

简介: 数据结构之双向链表

查找与修改

双向链表的查找,找到返回地址,找不到返回NULL。要注意的是从哨兵位的下一位开始找,因为哨兵位是不存储有效数据的,直到重新找到哨兵位


74470344150d403d8bba1b747ac1dc76.png


运行结果


查找到地址后,就可以对其解引用访问,进行修改


a248eac6911e4ba0b7aab8dce6398781.png


指定插入

在pos前插入


在双向链表,找到pos的上一个节点的地址prev太简单了


f51aa208f5074fb5bf0fed81aaf96640.png


0cd6675547ad4b809550bd0204c452c6.png


运行结果


4f1e164c0db24757b46631ef0f8869d8.png


在这里可以复用指定插入函数,快速实现头插和尾插


头插


d0c1ab7d74ef4dd8bffce00b51a5dfdc.png


79c93f5260f4457ca2c5fa59a7862d7f.png


尾插


9f330551b9ae4169881d3ed5f52a3254.png


8698e5dbde14431b9169f3237582cf1f.png


指定删除

创建posPrev和posNext两个指针,释放掉pos的节点空间,再相互链接


在pos删除


610a31c8d35e4bddbd96d343b7ebfb51.png


de5e0cdc26d64e21ad9ff56ba26af9c9.png


运行结果


8acc4278077c4fd2be6d2b8683e83512.png


在这里也可以复用指定删除函数,快速实现头删和尾删  


头删


86646583a3c84fee99fd9bc2a383ee47.png


尾删


89e7253b834f34d1ae29aa917f16cae2_3717274bbe4b4c799a8bf8b5fe876e29.png


大家有没有发现,因为双向链表存在哨兵位,链表不为空,省去了很多关于空链表和空指针的讨论,一路下来浑身舒畅


销毁

双向链表的销毁,值得注意的是最后哨兵位也要释放掉,因为为了保持用一级指针的连贯性,所以我们选择最后手动在外部将链表指针置为NULL,实现半自动(和free函数的逻辑一致)


fb3ec5bfc419e5e33fd2f94770c2b18d_7a6f97da8f5843d19176495643210081.png


运行结果


625b7c82c2d6ba4b0aea6f4cf3885d7a_b0cd48585a7a4b1290202ffec42dc0c8.png


这样我们就完成了对双向链表的增删查改等功能的实现


顺序表和双向链表的优缺点分析

我们看到,双向链表的好处是如此巨大的,那是否就代表前面学的顺序表就很low呢?


不同点 顺序表 链表
存储空间上 物理上一定连续 逻辑上连续,但物理上不一定连续
随机访问 支持O(1) 不支持:O(N)
任意位置插入或者删除元素 可能需要搬移元素,效率低O(N) 只需修改指针指向
插入 动态顺序表,空间不够时需要扩容 没有容量的概念
应用场景 元素高效存储+频繁访问 任意位置插入和删除频繁


双向链表oj题

仅仅了解双向链表的知识是不够的,让我们来刷刷题吧!


141.环形链表(LeetCode)-CSDN博客

142.环形链表 II(LeetCode)-CSDN博客

138.随机链表的复制(LeetCode)-CSDN博客


源代码

dlist.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int DLDataType;
typedef struct DListNode
{
  struct DListNode* prev;
  struct DListNode* next;
  DLDataType data;
}DLNode;
//初始化
DLNode* DLInit();
//打印
void DLPrint(DLNode* phead);
//销毁
void DLDestroy(DLNode* phead);
//检测链表是否为空
bool DLEmpty(DLNode* phead);
//尾插
void DLPushBack(DLNode* phead, DLDataType x);
//尾删
void DLPopBack(DLNode* phead);
//头插
void DLPushFront(DLNode* phead, DLDataType x);
//头删
void DLPopFront(DLNode* phead);
//查找
DLNode* DLFind(DLNode* phead, DLDataType x);
//在pos前指定插入
void DLInsert(DLNode* pos, DLDataType x);
//在pos指定删除
void DLErase(DLNode* pos);


dlist.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"dlist.h"
DLNode* BuyDLNode(DLDataType x)
{
  DLNode* newnode = (DLNode*)malloc(sizeof(DLNode));
  if (newnode == NULL)
  {
  perror("malloc fail");
  return NULL;
  }
  newnode->data = x;
  newnode->prev = NULL;
  newnode->next = NULL;
  return newnode;
}
DLNode* DLInit()
{
  DLNode* phead = BuyDLNode(-1);
  phead->next = phead;
  phead->prev = phead;
  return phead;
}
void DLPrint(DLNode* phead)
{
  assert(phead);
  DLNode* cur = phead;
  printf("Guard");
  while (cur->next != phead)
  {
  cur = cur->next;
  printf("<==>%d", cur->data);
  }
  printf("\n");
}
bool DLEmpty(DLNode* phead)
{
  assert(phead);
  return phead == phead->next;
}
void DLPushBack(DLNode* phead, DLDataType x)
{
  assert(phead);
  DLInsert(phead, x);
  //DLNode* newnode = BuyDLNode(x);
  //DLNode* tail = phead->prev;
  //
  //tail->next = newnode;
  //newnode->prev = tail;
  //phead->prev = newnode;
  //newnode->next = phead;
}
void DLPopBack(DLNode* phead)
{
  assert(phead);
  assert(!DLEmpty(phead));
  DLErase(phead->prev);
  //DLNode* tail = phead->prev;
  //DLNode* tailPrev = tail->prev;
  //
  //free(tail);
  //tailPrev->next = phead;
  //phead->prev = tailPrev;
}
void DLPushFront(DLNode* phead, DLDataType x)
{
  assert(phead);
  DLInsert(phead->next, x);
  //DLNode* newnode = BuyDLNode(x);
  //DLNode* first = phead->next;
  //newnode->next = first;
  //first->prev = newnode;
  //phead->next = newnode;
  //newnode->prev = phead;
}
void DLPopFront(DLNode* phead)
{
  assert(phead);
  assert(!DLEmpty(phead));
  DLErase(phead->next);
  //DLNode* first = phead->next;
  //DLNode* second = first->next;
  //second->prev = phead;
  //phead->next = second;
  //free(first);
}
DLNode* DLFind(DLNode* phead, DLDataType x)
{
  assert(phead);
  DLNode* cur = phead->next;
  while (cur != phead)
  {
  if (cur->data == x)
  {
    return cur;
  }
  cur = cur->next;
  }
  return NULL;
}
void DLInsert(DLNode* pos, DLDataType x)
{
  assert(pos);
  DLNode* newnode = BuyDLNode(x);
  DLNode* prev = pos->prev;
  prev->next = newnode;
  newnode->prev = prev;
  newnode->next = pos;
  pos->prev = newnode;
}
void DLErase(DLNode* pos)
{
  assert(pos);
  DLNode* posPrev = pos->prev;
  DLNode* posNext = pos->next;
  posPrev->next = posNext;
  posNext->prev = posPrev;
  free(pos);
}
void DLDestroy(DLNode* phead)
{
  assert(phead);
  DLNode* cur = phead->next;
  while (cur != phead)
  {
  DLNode* next = cur->next;
  free(cur);
  cur = next;
  }
  free(phead);
}


test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"dlist.h"
TestDList1()
{
  DLNode* plist = DLInit();
  //尾插
  DLPushBack(plist, 1);
  DLPushBack(plist, 2);
  DLPushBack(plist, 3);
  DLPushBack(plist, 4);
  DLPushBack(plist, 5);
  //头插
  DLPushFront(plist, 1);
  DLPushFront(plist, 2);
  DLPushFront(plist, 3);
  DLPushFront(plist, 4);
  DLPushFront(plist, 5);
  //打印
  DLPrint(plist);
}
TestDList2()
{
  DLNode* plist = DLInit();
  //尾插
  DLPushBack(plist, 1);
  DLPushBack(plist, 2);
  DLPushBack(plist, 3);
  DLPushBack(plist, 4);
  DLPushBack(plist, 5);
  //头删
  DLPopFront(plist);
  DLPopFront(plist);
  DLPopFront(plist);
  //打印
  DLPrint(plist);
}
TestDList3()
{
  DLNode* plist = DLInit();
  //头插
  DLPushFront(plist, 1);
  DLPushFront(plist, 2);
  DLPushFront(plist, 3);
  DLPushFront(plist, 4);
  DLPushFront(plist, 5);
  //尾删
  DLPopBack(plist);
  DLPopBack(plist);
  DLPopBack(plist);
  //打印
  DLPrint(plist);
}
TestDList4()
{
  DLNode* plist = DLInit();
  //头插
  DLPushFront(plist, 1);
  DLPushFront(plist, 2);
  DLPushFront(plist, 3);
  DLPushFront(plist, 4);
  DLPushFront(plist, 5);
  //查找与修改
  DLNode* pos = DLFind(plist, 4);
  if (pos != NULL)
  {
  pos->data = 40;
  //在pos前指定插入
  DLInsert(pos, 100);
  }
  //打印
  DLPrint(plist);
}
TestDList5()
{
  DLNode* plist = DLInit();
  //头插
  DLPushFront(plist, 1);
  DLPushFront(plist, 2);
  DLPushFront(plist, 3);
  DLPushFront(plist, 4);
  DLPushFront(plist, 5);
  //查找与修改
  DLNode* pos = DLFind(plist, 4);
  if (pos != NULL)
  {
  pos->data = 40;
  //在pos指定删除
  DLErase(pos);
  }
  //打印
  DLPrint(plist);
}
TestDList6()
{
  DLNode* plist = DLInit();
  //尾插
  DLPushBack(plist, 1);
  DLPushBack(plist, 2);
  //头插
  DLPushFront(plist, 1);
  DLPushFront(plist, 2);
  //打印
  DLPrint(plist);
  //销毁
  DLDestroy(plist);
  plist = NULL;
}
int main()
{
  TestDList6();
  return 0;
}
相关文章
|
4天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
26 4
|
6天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
6天前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
26天前
|
存储 Java
数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】
23 7
|
26天前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
21 3
|
25天前
|
算法 Java
数据结构与算法学习五:双链表的增、删、改、查
双链表的增、删、改、查操作及其Java实现,并通过实例演示了双向链表的优势和应用。
15 0
数据结构与算法学习五:双链表的增、删、改、查
|
4天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
18 0
【数据结构】——双向链表详细理解和实现
【数据结构】——双向链表详细理解和实现
|
30天前
|
存储 Java
【数据结构】链表
【数据结构】链表
16 1
|
30天前
|
存储 缓存
数据结构3——双向链表
数据结构3——双向链表
107 1