05 双向链表

news2024/10/4 23:47:58

目录

1.双向链表
2.实现
3.OJ题
4.链表和顺序表对比


1. 双向链表

前面写了单向链表,复习一下
无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多作为其他数据结构的子结构,如哈希桶、图的邻接等。另外这种结构在笔试面试中出现多

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

2. 实现

头文件

#pragma once

//数据类型
typedef int DataType;
//结构
typedef struct _SListNode
{
	DataType data;
	struct _SListNode* pNext;
}SListNode;

//插入
void PushFront(SListNode** pHead, DataType data);
void PushBack(SListNode** pHead, DataType data);
//pos之前插入
void Insert(SListNode** pHead, SListNode* pPos, DataType data);
//pos之后插入
void InsertAfter(SListNode** pHead, SListNode* pPos, DataType data);
//查找
SListNode* Find(SListNode* pHead, DataType data);
//删除
void PopFront(SListNode** pHead);
void PopBack(SListNode** pHead);
void Erase(SListNode** pHead, SListNode* pos);
// 删除pos位置后面的值
void EraseAfter(SListNode* pos);

//打印
void PrintList(SListNode* pHead);
//销毁
void Destory(SListNode** pHead);

插入只需要修改新节点的前后节点,新节点前后节点的链接,注意顺序,不能覆盖后面需要的值

插入和删除可以复用insert和erase的函数,所以也可以只写这两个,然后来实现头插尾插这些

#include "List.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

List* BuyNode(DATATYPE data)
{
	List* newnode = (List*)malloc(sizeof(List));
	if (newnode == NULL)
	{
		perror("mallco");
		return NULL;
	}

	//初始化数据
	newnode->data = data;
	newnode->pre = NULL;
	newnode->next = NULL;
	return newnode;
}

List* Init()
{
	List* head = BuyNode(-1);
	if (head == NULL)
	{
		perror("mallco");
	}

	head->pre = head;
	head->next = head;
	return head;
}

void PrintList(List* head)
{
	List* cur = head->next;
	while (cur != head)
	{
		printf("->%d ", cur->data);
		cur = cur->next;
	}
	printf("\r\n");
}


void PushFront(List* head, DATATYPE data)
{
	assert(head);
	//创建节点
	List* newnode = BuyNode(data);

	newnode->pre = head;
	newnode->next = head->next;

	head->next->pre = newnode;
	head->next = newnode;

	/*
	List* first = head->next;
	
	head->next = newnode;
	newnode->pre = head;

	newnode->next = first;
	first->pre = newnode;
	*/

	//Insert(head->next, data);
}

void PushBack(List* head, DATATYPE data)
{
	assert(head);
	//创建节点
	List* newnode = BuyNode(data);
	List* tail = head->pre;

	newnode->pre = tail;
	newnode->next = head;

	tail->next = newnode;
	head->pre = newnode;

	//Insert(head, data);
}

void Insert(List* pos, DATATYPE data)
{
	assert(pos);

	//创建节点
	List* newnode = BuyNode(data);
	List* prev = pos->pre;

	newnode->pre = pos->pre;
	newnode->next = pos;

	prev->next = newnode;
	pos->pre = newnode;
}

void PopFront(List* head)
{
	assert(head);
	assert(!Empety(head));

	List* del = head->next;

	head->next = del->next;
	del->next->pre = head;
	free(del);
	/*
	List* first = head->next;
	List* second = first->next;

	head->next = second;
	second->pre = head;

	free(first);
	*/

	//erase(head->next)
}

void PopBack(List* head)
{
	assert(head);
	assert(!Empety(head));

	List* del = head->pre;
	//保留尾节点前一个
	List* tailpre = del->pre;

	tailpre->next = head;
	head->pre = tailpre;
	free(del);

	//erase(head->pre)
}

void Erase(List* pos)
{
	assert(pos);
	List* posPre = pos->pre;
	List* posNext = pos->next;

	posPre->next = posNext;
	posNext->pre = posPre;
	free(pos);
}

List* FindNode(List* head, DATATYPE data)
{
	assert(head);
	List* cur = head->next;

	while (cur != head)
	{
		if (cur->data == data)
			return cur;
		cur = cur->next;
	}
	return NULL;
}

bool Empety(List* head)
{
	assert(head);

	return head->next == head;

}

void Destory(List* head)
{
	assert(head);

	List* cur = head->next;
	while (cur != head)
	{
		List* next = cur->next;
		free(cur);
		cur = next;
	}

	free(head);
}

主文件

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "List.h"

int main()
{
	List* list = Init();
	PushFront(list, 3);
	PushFront(list, 2);
	PushFront(list, 1);
	PushBack(list, 4);
	PushBack(list, 5);
	PrintList(list);
	PopFront(list);
	PopBack(list);
	PrintList(list);
	List* pos = FindNode(list, 3);
	if (pos != NULL)
	{
		Insert(pos, 1);
		PrintList(list);
	}
	Erase(pos);
	PrintList(list);
	Destory(list);

	return 0;
}

3. OJ题

链表的复制

https://leetcode.cn/problems/copy-list-with-random-pointer/description/

在这里插入图片描述
思路

直接思路,拷贝一个一模一样的链表,关键是复制random对应的节点值,要遍历看原链表指向的random是在第几个,将新链表的random链接到对应位置,这种方法时间复杂度较高

另一种思路。在原链表每个节点后面插入一个拷贝出来的原链表值。重点在random的链接,这样新链表的random就是原链表对应的rand节点的下一个位置。然后再创建一个新链表,将每个拷贝节点尾插并还原原链表

在这里插入图片描述

如图,原链表指向是1->2->3->null,插入蓝色的新拷贝链表,1->蓝1->2->蓝2->3->蓝3->null,原1的random指向3,那么拷贝链表的random指向就应该是3的next,就是拷贝链表的3

解绑过程如下图

在这里插入图片描述

cur是当前节点,它的next是copy节点,首先链表头和尾本来是空,第一次置为第一个拷贝节点。将copy节点尾插到copytail链表,cur的next节点就是copy节点的下一个,然后将cur更新到copy的next,如下图

在这里插入图片描述这样一直循环

/**
 * Definition for a Node.
 * struct Node {
 *     int val;
 *     struct Node *next;
 *     struct Node *random;
 * };
 */

struct Node* copyRandomList(struct Node* head) {
	
    struct Node* cur = head;
    //拷贝节点插入在原节点后面
    while(cur != NULL)
    {
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));
        copy->val = cur->val;

        struct Node* next = cur->next;
        //插入
        cur->next = copy;
        copy->next = next;

        cur = next;
    }

    //控制拷贝节点的random
    cur = head;
    while(cur != NULL)
    {
        struct Node* copy = cur->next;
        if(cur->random == NULL)
        {
            copy->random = NULL;
        }
        else{
            //指向对应的拷贝链表
            copy->random = cur->random->next;
        }
        cur = copy->next;
    }

    //尾插拷贝链表,还原原链表
    struct Node* copyhead = NULL;
    struct Node* copytail = NULL;
    
    cur = head;
    while(cur != NULL)
    {
        struct Node* copy = cur->next;
        struct Node* next = copy->next;

        //尾插
        if(copyhead == NULL)
        {
            copyhead = copytail = copy;
        }
        else{
            copytail->next = copy;
            copytail = copytail->next;
        }
        //恢复原链表
        cur->next = copy->next;
        cur = next;
    }
    return copyhead;
}

4. 顺序表和链表对比

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

链表
优点:
1.任意位置插入删除O(1)
2.按需申请释放空间
缺点:
1.不支持下标随机访问
2.CPU告诉缓存命中率更低
顺序表
优点:
1.尾插尾删效率不错
2.下标的随机访问
3.CPU告诉缓存命中率更高
缺点:
1.除过尾插尾删,效率低O(N),需要挪动元素
2.空间不够,需要扩容
3.扩容需要代价,一般伴随空间浪费

cpu存储分类在这里插入图片描述数据的存储从服务器到硬盘,再到内存。其中内存还有寄存器和告诉缓存部分,访问速度越网上越快,但代价也越高,空间也越小。寄存器中拿数据是最快的,但一般寄存器只有几十字节

内存读取数据并不是需要多少读多少,它会将需要读取的数据后面的一部分也读入缓存中,因为这部分极有可能还会读取,这就是命中缓存,访问速度更快。所以顺序表式连续存储的,命中缓存的几率更高,速度也就更快

每个数据类型都各有优势,有不同的应用场景,不存在优劣

相关参考:
CPU缓存知识

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1406574.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

你知道Mysql的架构吗?

msyql分为server曾和存储引擎层 server层包括了连接器(管理连接&#xff0c;权限验证)、查询缓存&#xff08;命中直接返回结果&#xff09;、分析器&#xff08;词法分析&#xff0c;语法分析&#xff09;、优化器&#xff08;执行计划生成&#xff0c;索引选择&#xff09;、…

浪花 - 查询队伍列表

一、接口设计 1. 请求参数&#xff1a;封装 TeamQuery package com.example.usercenter.model.dto;import com.example.usercenter.common.PageRequest; import lombok.Data;/*** author 乐小鑫* version 1.0* Date 2024-01-22-20:14*/ Data public class TeamQuery extends …

使用Unity创建VisionPro应用

1、下载特定Unity版本 Unity账号需要是Pro账号,普通账号不行,目前只支持这1个Unity版本,不要下载任何其它版本:unityhub://2022.3.11f1/d00248457e15) 其它条件:使用Mac电脑M系列芯片,XCode15 Beta2及以上 参考资料: 苹果官网:苹果官网 Unity官网:Unity官网 官方教程…

C#,生成图片的指定尺寸缩略图的源代码

编程的时候经常用到图像的缩略图。 本文发布一个用于生成指定尺寸的缩略图的简单方法。 1 文本格式 private void button1_Click(object sender, EventArgs e) { CreateThumbnail("demo.jpg", "demo_thumb.jpg", 128, 128); } private void CreateTh…

MySQL函数—日期函数

MySQL函数—日期函数 函数功能CURDATE()返回当前日期&#xff0c;只有年月日CURTIME()返回当前时间&#xff0c;只有时分秒NOW()返回当前日期和时间 年月日时分秒YEAR(date)获取指定date的年份MONTH(date)获取指定date的月份DAY(date)获取指定date的日期DATE_ADD(date,INTERVAL…

项目解决方案: 视频融合(实时监控视频和三维建模进行融合)设计方案

目 录 一、需求描述 1、视频接入和控制要求 2、视频播放需求 3、提供其他应用的调用 二、方案设计 &#xff08;一&#xff09;系统设计图 &#xff08;二&#xff09;产品实现方案 三、产品和功能描述 &#xff08;一&#xff09;总体描述 &#xf…

2024问题汇总

2024问题汇总 Linux1.df-h / df -i 命令2.为多网卡Linux云服务器配置策略路由 Windows1.快速进入控制面板 网络连接指令 Linux 1.df-h / df -i 命令 df -h / df -i 都表示查看磁盘空间使用信息 如果遇到磁盘快满的情况&#xff0c;用这两个命令区别如下 df -h 是去删除比较大 …

Java的异常 Exception

从继承关系可知:Throwable 是异常体系的根&#xff0c;它继承自Object 。Throwable 有两个体系: Error 和Exception. Error表示严重的错误&#xff0c;程序对此一般无能为力,例如: OutOfMemoryError :内存耗尽NoClassDefFoundError :无法加载某个ClassStackOverflowError :虚…

web安全学习笔记【05】——反弹Shell、正反向连接

思维导图 #知识点&#xff1a; 1、Web常规-系统&中间件&数据库&源码等 2、Web其他-前后端&软件&Docker&分配站等 3、Web拓展-CDN&WAF&OSS&反向&负载均衡等 ----------------------------------- 1、APP架构-封装&原生态&H5&am…

软件安全测试的重要性简析,专业安全测试报告如何申请?

在当今数字化时代&#xff0c;软件在我们的日常生活中扮演着至关重要的角色&#xff0c;但也带来了各种潜在的安全威胁。为了保障用户的信息安全和维护软件的可靠性&#xff0c;软件安全测试显得尤为重要。 软件安全测试是指通过一系列的方法和技术&#xff0c;对软件系统中的…

BACnet转Modbus协议转换网关BA111

随着通讯技术和控制技术的发展&#xff0c;为了实现楼宇的高效、智能化管理&#xff0c;集中监控管理已成为楼宇智能管理发展的必然趋势。在此背景下&#xff0c;高性能的楼宇暖通数据传输解决方案——协议转换网关应运而生&#xff0c;广泛应用于楼宇自控和暖通空调系统应用中…

Webpack5 基本使用 - 2

常用 loader loader 是辅助打包工具。webpack 默认只能打包 js 文件&#xff0c;打包其它模块就需要配置 loader 来告诉 webpack 该怎么去打包其它文件。loader 可以将文件从不同的语言转换为 JavaScript。一类文件如果需要多个 loader 处理&#xff0c;loader 的执行顺序是从…

数据采集与预处理01: 项目1 数据采集与预处理准备

数据采集与预处理01&#xff1a; 项目1 数据采集与预处理准备 任务1 认识数据采集技术&#xff0c;熟悉数据采集平台 数据采集&#xff1a;足够的数据量是企业大数据战略建设的基础&#xff0c;因此数据采集成为大数据分析的前站。数据采集是大数据价值挖掘中重要的一环&#…

《WebKit 技术内幕》学习之十二(2):安全机制

2 沙箱模型 2.1 原理 一般而言&#xff0c;对于网络上的网页中的JavaScript代码和插件是不受信的&#xff08;除非是经过认证的网站&#xff09;&#xff0c;特别是一些故意设计侵入浏览器运行的主机代码更是非常危险&#xff0c;通过一些手段或者浏览器中的漏洞&#xff0c…

在SpringBoot中基于CanvasLabel的地震基础信息展示实践

目录 前言 一、数据库设计 1、数据库设计 2、sql脚本 3、数据记录 二、SpringBoot后台设计与实现 1、Mapper访问层及实体定义 2、Service层实现 3、控制层实现 三、地震信息展示 1、展示数据接入 2、最终效果 总结 前言 在上一篇博客中&#xff0c;对于在Leafle…

自然语言处理--基于HMM+维特比算法的词性标注

自然语言处理作业2--基于HMM维特比算法的词性标注 一、理论描述 词性标注是一种自然语言处理技术&#xff0c;用于识别文本中每个词的词性&#xff0c;例如名词、动词、形容词等&#xff1b; 词性标注也被称为语法标注或词类消疑&#xff0c;是语料库语言学中将语料库内单词…

mockjs使用(2)

mockjs使用&#xff08;1&#xff09; 4、Mock 4.1 Mock.mock() 根据数据模版生成模拟数据 Mock.mock( rurl?, rtype?, template|function(options) )问号代表该参数不必填 4.1.1 各参数及其默认值 rurl: 不必填。表示需要拦截的URL&#xff0c;可以使URL字符串或URL正…

解决方案 | 基于SFTP协议的文件传输断点续传Java实现方案

背景 因项目需要&#xff0c;我们服务每天都需要通过SFTP协议来对接上下游进行文件传输&#xff0c;但是对于一些大文件&#xff0c;在与第三方公司的服务器对接过程中很可能会因为网络问题或上下游服务器性能问题导致文件上传或者下载被中断&#xff0c;每次重试都需要重新对…

【Python进阶编程】python编程高手常用的设计模式(持续更新中)

Python编程高手通常熟练运用各种设计模式&#xff0c;这些设计模式有助于提高代码的可维护性、可扩展性和重用性。 以下是一些Python编程高手常用的设计模式&#xff1a; 1.单例模式&#xff08;Singleton Pattern&#xff09; 确保一个类只有一个实例&#xff0c;并提供全局…