25考研数据结构复习·2.3链表.3

  • 双链表

    可进可退,存储密度更低一丢丢

    • 初始化(带头结点)

      typedef struct DNode{  //定义双链表结点类型
      		ElemType data;  //数据域
      		strucct DNode *prior,*next;  //前驱和后继指针
      }DNode,*DLinklist;
      
      //初始化双链表
      bool InitDLinkList(DLinklist &L){
      		L = (DNode *) malloc(sizeof(DNode));  //分配一个头结点
      		if (L == NULL)  //内存不足,分配失败
      				return false;
      		L -> prior = NULL;  //头结点的prior永远指向NULL
      		L -> next =NULL;  //头结点之后暂时还没有结点
      		return true; 
      }
      
      void testDLinkList(){
      		//初始化双链表
      		DLinklist L;
      		InitDLinkList(L);
      		//后续代码...
      }
      
      //判断双链表是否为空(带头结点)
      bool Empty(DLinklist L){
      		if(L -> next == NULL)
      				return true;
      		else
      				return false;
      }
      

      DLinklist ⇔等价 DNode *

  • 插入

    //在p结点之后插入s结点
    bool InsertNextDNode(DNode *p,DNode *s){
    		if(p == NULL || s == NULL)  //非法参数
    				return false;
    		//修改指针时要注意顺序
    		s -> next = p ->next;  //将结点*s插入到结点 *p之后
    		if(p -> next = p -> next)  //如果p结点有后继节点
    				p -> next -> prior =s; 
    		s ->prior =p;
    		p ->next =s;
    }
    
  • 删除

    //删除p结点的后继结点
    bool DeleteNextDNode(DNode *p){
    		if(p == NULL) 
    				return false;
    		DNode *q = p -> next;  //找到p的后继节点q
    		if(q == NULL) 
    				return false;  //p没有后继
    		p -> next = q ->next;  
    		if(q -> next != NULL)  //q结点不是最后一个结点
    				q -> next -> prior = p; 
    		free(q);  //释放结点空间
    		return true;
    }
    
    void DestoryList(DLinklist &L){
    		//循环释放各个数据节点
    		while (L -> next != NULL)
    				DeleteNextDNode(L);
    		free(L);  //释放头结点
    		L = NULL;  //头指针指向NULL
    }
    
  • 遍历

    • 后向遍历

      while (p != NULL){
      		//对结点p做相应处理,如打印
      		p = p -> next;
      }
      
    • 前向遍历

      while (p != NULL){
      		//对结点p做相应处理
      		p = p -> prior;
      }
      
    • 前向遍历(跳过头结点)

      while (p -> prior != NULL){
      		//对结点p做相应处理
      		p = p -> prior;
      }
      
    • 双链表不可随机存取

    • 按位查找、按值查找操作都只能用遍历的方式实现。

    • 时间复杂度:O(n)

总结 

 

 

  • 循环链表

循环单链表 

 

typedef struct LNode{  //定义单链表结点类型
		ElemType data;  //每个结点村南方一个数据元素
		struct LNode *next;  //指针指向下一个结点
}LNode, *LinkList;

//初始化一个单链表
bool InitList(LinkList &L){
		L = (LNode *) malloc(sizeif(LNode));  //分配一个头节点
		if (L == NULL)  //内存不足,分配失败
				return false;
		L -> next = L;  //头节点next指向头结点
		return true;
}

//判断单链表是否为空
bool Empty(LinkList L){
		if (L -> next == L)
				return true;
		else
				return false;
}

//判断结点p是否为循环单链表的表尾结点
bool isTail(LinkList L,LNode *p){
		if(p -> next == L)
				return true;
		else
				return false;
}

循环双链表

 

表头结点的prior指向表尾结点;

表尾结点的next指向头结点;

typedef struct DNode{  
		ElemType data;  
		strucct DNode *prior,*next;  
}DNode,*DLinklist;

//初始化空的循环双链表
bool InitDLinkList(DLinklist &L){
		L = (DNode *) malloc(sizeof(DNode));  //分配一个头结点
		if (L == NULL)  //内存不足,分配失败
				return false;
		L -> prior = L;  //头结点的prior指向头结点
		L -> next = L;  //头结点的next 指向头结点
		return true; 
}

void testDLinkList(){
		//初始化双链表
		DLinklist L;
		InitDLinkList(L);
		//后续代码...
}

//判断双链表是否为空
bool Empty(DLinklist L){
		if(L -> next == L)
				return true;
		else
				return false;
}

//判断结点p是否为循环双链表的表尾结点
bool isTail(DLinklist L,DNode *p){
		if(p -> next ==L)
				return true;
		else
				return false;
}

  • 插入

    //在p结点之后插入s结点
    bool InsertNextDNode(DNode *p,DNode *s){
    		s -> next = p ->next;  //将结点*s插入到结点 *p之后
    		p -> next -> prior =s; 
    		s -> prior =p;
    		p -> next =s;
    }
    
  • 删除

    //删除p结点的后继结点
    bool DeleteNextDNode(DNode *p){
    		p -> next = q ->next;  
    		q -> next -> prior = p; 
    		free(q);  //释放结点空间
    		return true;
    }
    
  • 👍 代码问题

    如何判空

    如何判断结点p是否是表尾/表头结点 → 后向/前向遍历的实现核心

    如何在表头、表中、表尾插入/删除一个结点

  • 静态链表

    • 什么是静态链表

      数组的方式实现的链表

      分配一整片连续的内存空间,各个结点集中安置。

      每个数据元素4B,每个游标4B(每个结点共8B)设起始地址为addr

      a₁的存放地址为addr + 8*2

 

  • 如何定义一个静态链表

    //方法1
    #define MaxSize 10 //静态链表的最大长度
    struct Node{  //静态链表结构类型的定义
    		ElemType data;  //存储数据元素
    		int next;  //下一个元素的数组下标
    }
    typedef struct Node SLinkList[MaxSize];  //可用SLinkList定义“一个长度为MaxSize的Node型数组”
    
    void testSlinkList(){
    		struct Node a[MazSize];  //数组a作为静态链表
    		//...后续代码
    }
    

    等价于👇

    //方法2
    #define MaxSize 10 //静态链表的最大长度
    struct Node{  //静态链表结构类型的定义
    		ElemType data;  //存储数据元素
    		int next;  //下一个元素的数组下标
    }SLinkList[MaxSize];  
    
    void testSlinkList(){
    		SLinkList a;  //a是一个静态链表
    		//...后续代码
    }
    

    • 证明方法2

      #define MaxSize 10 //静态链表的最大长度
      struct Node{  //静态链表结构类型的定义
      		int data;  //存储数据元素
      		int next;  //下一个元素的数组下标
      };
      typedef struct{  //静态链表结构类型的定义
      		int data;  //存储数据元素
      		int next;  //下一个元素的数组下标
      }SLinkList[MaxSize];  
      
      void testSlinkList(){
      		struct Node x;
      		printf("sizeX = %d\\n",sizeof(x));
      		//sizeX = 8
      		struct Node a[MaxSize];
      		printf("sizeA = %d\\n",sizeof(a));
      		//sizeA = 80
      		SLinkList b;
      		printf("sizeB = %d\\n",sizeof(b));
      		//sizeB =  80
      }
      

      结论: SLinkList b ——相当于定义了一个长度为MaxSize的Node型数组

  • 简述基本操作的实现

    • 初始化

      把 a[0]的next设为-1

    • 查找

      从头结点出发挨个往后遍历结点 时间复杂度O(n)

    • 插入位序为i的结点

      1.找到一个空的结点,存入数据元素

    • Q:如何判断结点是否为空。

      A:可让next为某个特殊值,如-2

      2.从头结点出发找到位序为i - 1的结点

      3.修改新结点的next

      4.修改i - 1号结点的next

    • 删除某个结点

      1.从头结点出发找到前驱结点

      2.修改前驱结点的游标

      3.被删除结点next设为-2

  • 总结

    优点:增删操作不需要大量移动元素

    缺点:不能随机存取,只能从头结点开始依次往后查找;容量固定不可变

    使用场景:

    1.不支持指针的低级语言

    2.数据元素数量固定不变的场景(如操作系统的文件分配表FAT

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Annabelle.02

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值