【数据结构】双向链表

1.双向链表

1.1 概念与结构

1.2 实现双向链表

c 复制代码
//List.h 
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int LTDataType; 
typedef struct ListNode 
{ 
    struct ListNode* next; //指针保存下⼀个结点的地址 
    struct ListNode* prev; //指针保存前⼀个结点的地址 
    LTDataType data; 
}LTNode; 

//第一种方法:要修改头结点,要用LTNode** pphead接收
//void LTInit(LTNode** pphead); 
//第二种方法:不需要二级指针
LTNode* LTInit(); 

//第一种销毁方法:
//void LTDestroy(LTNode** pphead);
//第二种方法:
void LTDestroy(LTNode* phead);

//创建
LTNode* LTBuyNode(LTDataType x);

//打印
void LTPrint(LTNode* phead);
//判断链表是否为空
bool LTEmpty(LTNode* phead); 

//在双向链表中,增删改查都不会改变哨兵位结点,所以用LTNode* phead
void LTPushBack(LTNode* phead, LTDataType x); 
//尾删
void LTPopBack(LTNode* phead); 

//头插
void LTPushFront(LTNode* phead, LTDataType x); 
//头删
void LTPopFront(LTNode* phead); 

//在pos位置之后插⼊数据
void LTInsert(LTNode* pos, LTDataType x); 
//删除pos结点
void LTErase(LTNode* pos); 
//查找
LTNode *LTFind(LTNode* phead,LTDataType x);
c 复制代码
//List.c
#include"List.h"

LTNode* LTBuyNode(LTDataType x)
{
    LTNode* newnode=(LTNode*)malloc(sizeof(LTNode));
    if(newnode==NULL)
    {
        perror("malloc");
        exit(1);
    }
    newnode->data=x;
    newnode->next=newnode->prev=newnode;//空指针也可以用这句代码
    return newnode;
}

//void LTInit(LTNode** pphead)
//{
//    *pphead=LTBuyNode(-1);
//}
LTNode* LTInit()
{
    LTNode* phead=LTBuyNode(-1);
    return phead;
}

//void LTDestroy(LTNode** pphead)
//{
    //先从第一个有效结点开始删除
//   LTNode* pcur=(*pphead)->next;
//    while(pcur!=*pphead)
//    {
//        LTNode* next=pcur->next;//先把下一个结点存起来
//        free(pcur);
//        pcur=next;
//    }
    //销毁头结点
//    free(*pphead);
//    *pphead=NULL;
//}
void LTDestroy(LTNode* phead)
{
    LTNode* pcur=(phead)->next;
    while(pcur!=phead)
    {
        LTNode* next=pcur->next;//先把下一个结点存起来
        free(pcur);
        pcur=next;
    }
    free(phead);
    phead=NULL;
}



void LTPushBack(LTNode* phead, LTDataType x)
{
    assert(phead);
    LTNode* newnode=LTBuyNode(x);
    //phead->prev是原本的尾结点
    //先修改newnode,不会改变原链表
    newnode->prev=phead->prev;
    newnode->next=phead;
    //先连后断
    phead->prev->next=newnode;
    phead->prev=newnode;
}

//插入在第一个有效的结点的前面
void LTPushFront(LTNode* phead, LTDataType x)
{
    LTNode* newnode=LTBuyNode(x);
    //先修改newnode,不会改变原链表
    newnode->next=phead->next;
    newnode->prev=phead;
    phead->next->prev=newnode;
    phead->next=newnode;
}

bool LTEmpty(LTNode* phead)
{
    assert(phead);
    return phead->next==phead;//链表的next指向自己就返回
}

//尾删
//头删和尾删要确保链表不为空
void LTPopBack(LTNode* phead)
{
  	assert(!LTEmpty(phead));
    LTNode* del=phead->prev;
    del->prev->next=phead;
    prev->prev=del->prev;
    free(del);
    del=NULL;
}
//打印
void LTPrint(LTNode* phead)
{
    LTNode* pcur=phead->next;
    //只要pcur->next!=phead
    while(pcur->next!=phead)
    {
        printf("%d->",pcur->data);
        pcur=pcur->next;
    }
    printf("\n");
}
//删除第一个有效结点
void LTPopFront(LTNode* phead)
{
    assert(!LTEmpty(phead));
    LTNode*del=phead->next;
    del->next->prev=phead;
    phead->next=del->next;
    free(del);
    del=NULL;
}
//查找
LTNode *LTFind(LTNode* phead,LTDataType x)
{
    assert(phead);
    LTNode* pcur=phead->next;
    while(pcur!=head)
    {
        if(pcur->data==x)
            return pcur;
        pcur=pcur->next;
    }
    //未找到
    return NULL;
}

//在pos之后插入一个值为x的结点
void LTInsert(LTNode* pos, LTDataType x)
{
    assert(pos);
    LTNode* newnode=LTBuyNode(x);
    //先修改newnode,不会改变原链表
    newnode->prev=pos;
    newnode->next=pos->next;
    //先连再断
    pos->next->prev=newnode;
    pos->next=newnode;
}

void LTErase(LTNode* pos)
{
    assert(pos);
    pos->prev->next=pos->next;
    pos->next->prev=pos->prev; 
    free(pos);
    pos=NULL;
}
c 复制代码
//test.c
#include"List.h"
void test01()
{
    //LTNode* plist=NULL;
    //LTInit(&plist);
    LTNode* plist=LTInit();
    
    //尾插
    LTPushBack(plist,1);
    LTPushBack(plist,2);
    LTPushBack(plist,3);
    LTPushBack(plist,4);
    
    //头插
    LTPushFront(plist,1);
    LTPushFront(plist,2);
    LTPushFront(plist,3);
    LTPushFront(plist,4);
    
    //尾删
    LTPopBack(plist);
    LTPrint(plist);
    LTPopBack(plist);
    LTPrint(plist);
    LTPopBack(plist);
    LTPrint(plist);
    
    //头删
    LTPopFront(plist);
    LTPrint(plist);
    LTPopFront(plist);
    LTPrint(plist);
    LTPopFront(plist);
    LTPrint(plist);
    
    //查找
    LTNode* pos=LTFind(plist,2);
    if(pos)
        printf("找到了\n");
    else
        printf("未找到\n");
    
    //在pos之后插入100
    LTInsert(pos,100);
    LTPrint(plist);
    //删除pos结点
    LTErase(pos);
    LTPrint(plist);
    
    //销毁
    //LTDesTroy(&plist);
    LTDesTroy(plist);
    plist=NULL;
}
int main()
{
    test01();
    return 0;
}

2.顺序表与链表的分析

​

不同点 顺序表 链表(单链表)
存储空间上 物理上⼀定连续 逻辑上连续,但物理上不⼀定连续
随机访问 ⽀持O(1) 不⽀持:O(N)
任意位置插⼊或者删除元素 可能需要搬移元素,效率低O(N) 在指定位置之后插入或删除元素只需修改指针指向
插⼊ 动态顺序表,空间不够时需要扩容和空间浪费 没有容量的概念,按需申请释放,不存在空间浪费
应⽤场景 元素⾼效存储+频繁访问 任意位置⾼效插⼊和删除
相关推荐
岑梓铭7 小时前
《考研408数据结构》第八章(排序)复习笔记
数据结构·笔记
nice_lcj5201 天前
LRU 缓存入门:它是什么、怎么用、面试怎么手写
java·数据结构·缓存·面试·lru
꧁细听勿语情꧂1 天前
直接选择排序,堆排序,冒泡排序,递归快速排序、hoare版本、挖坑法、lomuto前后指针,非递归快速排序
c语言·开发语言·数据结构·算法
Pniubi2 天前
关于算法用现成数据结构和手撕
数据结构
老师我太想进步了20262 天前
【数据结构-6】算法美学之单链表(1)
数据结构·算法
希望我万事顺遂2 天前
力扣hot100-滑动窗口最大值
数据结构·算法·leetcode
꧁细听勿语情꧂2 天前
二叉树余题,排序、直接插入排序、希尔排序
c语言·开发语言·数据结构·算法
是main不是漫2 天前
【双指针|LeetCode1089 复写零】巧用逆向思路,破解原地覆盖陷阱
数据结构·算法·leetcode
饮水桶_T3 天前
JAVA IO流笔记
开发语言·数据结构·算法
wo_jack3 天前
C++ 入门笔记(十七):list 入门 —— 为什么 list 不能 sort?
数据结构·c++·面试·stl·list