数据结构之双向链表-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;
}
相关文章
|
2月前
|
存储 编译器 C语言
【数据结构】C语言实现带头双向循环链表万字详解(附完整运行代码)
【数据结构】C语言实现带头双向循环链表万字详解(附完整运行代码)
11 0
|
9天前
|
存储
数据结构第二课 -----线性表之单向链表
数据结构第二课 -----线性表之单向链表
|
9天前
|
存储
数据结构第三课 -----线性表之双向链表
数据结构第三课 -----线性表之双向链表
|
10天前
|
存储 Java
数据结构奇妙旅程之顺序表和链表
数据结构奇妙旅程之顺序表和链表
|
14天前
|
存储 C语言
数据结构基础:双链表结构、实现
数据结构基础:双链表结构、实现
|
14天前
|
存储
数据结构基础:一篇文章教你单链表(头插,尾插,查找,头删等的解析和代码)
数据结构基础:一篇文章教你单链表(头插,尾插,查找,头删等的解析和代码)
|
17天前
|
C语言
数据结构:5、链表之双向链表
数据结构:5、链表之双向链表
25 0
|
17天前
|
存储
数据结构:4、链表之单链表
数据结构:4、链表之单链表
11 0
|
24天前
|
存储 C语言
数据结构期末复习(2)链表
数据结构期末复习(2)链表
12 0
|
24天前
数据结构—链表(超详细)(山东大学)(数据结构实验三)
数据结构—链表(超详细)(山东大学)(数据结构实验三)