数据结构与算法基础(王卓)(6):带尾指针(单向)循环链表的合并;双链表及其插入和删除的详细操作

news2025/3/1 6:51:34

带尾指针(单向)循环链表的合并

PPT(157):

操作前初始设定预设的条件​​​​

我们要做的,即:

让A表尾节点指向B表首结点,让B表尾节点指向A表首结点:

Project 1:

Status 合并链表(Lnode A,Lnode B)
{
    //前置条件声明:
    LinkList Ta, Tb;//两表的尾指针

    LinkList temp = Ta;//
    Ta->next = Tb->next;//&B;
    Tb->next = temp;
    return true;
}

问题:

临时变量temp应该指向的,是尾结点指向的地址,而不是尾结点本身的地址;

两表合并以后不应存在原B表的头结点(理论上说,应该删除)

另外:

Ta和Tb是两表本身的名称,只是两表都采用尾指针而已,不存在什么指向表B的说法

Project 2:

Status 合并链表(LinkList Ta, LinkList Tb)
{
    LinkList temp = Ta->next;
    //
    Ta->next = Tb->next->next;//指向b1
    delete Tb->next;
    Tb->next = temp;
    //
    return true;
}

前提:两表非空

当我们试图去简化程序,看看能不能不用temp,结果发现:(根本不可能实现)

尾指针Ta指向表Tb的首元结点时,Tb肯定不能被销毁,且Tb->next也不能变动

要不然就找不到表Tb的首元结点了

此后,想要执行让“尾指针Tb指向表Ta的头结点”的操作,就必须再搞出(设置)一个变量来储存Ta的头结点的地址,要不然根本就无法实现程序的基本功能

双向循环链表

简而言之,双向循环链表的前置定义如下:

struct DLnode
{
    Elemtype data;
    DLnode* next,* prior;
};
typedef DLnode* LinkList;

双向链表的插入

在链表的第i位插入新元素e:

Project 1:

前置条件:

#include<iostream>
using namespace std;
#include<stdlib.h>//存放exit  
#include<math.h>//OVERFLOW,exit

#define MAXlength 100  //初始大小为100,可按需修改

typedef int Status;         //函数调用状态

struct K
{
    float a;
    int b;
    string c;
    bool operator==(K& t)
    {
        return t.a == a && t.b == b;
        //&& t.c = c;
    }
    bool operator!=(K& t)
    {
        return t.a != a || t.b != b;
        //|| t.c = c;
    }
};
typedef K Elemtype;         //函数调用状态

struct DLnode
{
    Elemtype data;
    DLnode* next, * prior;
};
typedef DLnode* DLinkList;

DLinkList p;
//为了后面双链表插入函数里方便使用,我们把p写为全局函数
Status 取第i个元素地址(DLinkList L, int i)//, DLinkList e)
{
    
    p = L->next;
    int j = 1;
    while (p && i > j)
    {
        p = p->next;
        j++;
    }
    if (i < 0 || i < j || !p)
        return false;
    //e = p;
    return true;
}

算法函数实现: 

Status 双链表插入(DLinkList L, int i, Elemtype e)
{
    取第i个元素地址(L, i);
    auto s = new DLnode;
    //auto s = new DLinkList;
    s->data = e;//别忘了写
    s->prior = p->prior;
    s->next = p;
    p->prior->next = s;
    p->prior = s;
    return true;
}

问题:

(1):关于插入算法(的)四个操作(的)前后语序问题

在程序设计时,本来出于要保留(插入结点的)前驱结点的next指针和后继结点的prior指针

于是就先给新结点的prior和next指针赋值

但是这样就会导致在执行语句    p->prior->next = s;时,由于前面有对新结点next指针赋值的操作

此时p->prior有两个地址(新节点和前驱节点都指向后继节点)容易让程序产生错乱

所以,对于该(此)处的程序,语句执行限制如下:

    s->data = e;别忘了写
    s->prior = p->prior; 执行之前,p->prior(前驱节点的)地址必须保留不能改变(丢失)
    p->prior->next = s;执行之前,不宜对p->prior进行赋值

至于语句    s->next = p; 和    p->prior = s;,地址s和p本身在这几个语句里又不会发生改变,所以不必在意

所以,综上所述,题目对这四句语句的限制要求:

    s->prior = p->prior;   :前面不能有p->prior = s;
    p->prior->next = s;   :前面不能有p->prior = s;
    s->next = p;   :任意位置
    p->prior = s;   :不能在那两句的前面

若将上述语句分别标号为1,2,3,4的话,根据排列组合的知识,可以正确运行的程序,无非就是下列几种情况:

1234

2134

3124

3214

1324

2314

 然而,即使研究到了这一步,程序依然还是有问题

实际上第i个元素的地址没有传进程序,修改如下:

(2):缺少确定p指向后继节点真正的语句

Project 2:(1234)

DLinkList p,a;//改动1
Status 取第i个元素地址(DLinkList L, int i, DLinkList a)//改动2
{

    p = L->next;
    int j = 1;
    while (p && i > j)
    {
        p = p->next;
        j++;
    }
    if (i < 0 || i < j || !p)
        return false;
    a = p;//改动3
    return true;
}


Status 双链表插入(DLinkList L, int i, Elemtype e)
{
    取第i个元素地址(L, i, a);
    if (a != p)//改动5
        return false;

    auto s = new DLnode;

    s->data = e;//别忘了写

    s->prior = p->prior;
    p->prior->next = s;
    s->next = p;// 
    p->prior = s;
    return true;
}

确定p指向后继节点真正的语句:改动5

(3):真正实现用辅助函数返回后继节点地址

另外,如果在前面写函数“取第i个元素地址”时,想要完全保证函数的封装性,也可以把函数改为:

DLinkList 取第i个元素地址(DLinkList L, int i, DLinkList a)
{
    DLinkList p;
    p = L->next;
    int j = 1;
    while (p && i > j)
    {
        p = p->next;
        j++;
    }
    if (i < 0 || i < j || !p) 
        return 0;
    a = p;
}

此时,插入函数内引用函数语句为:

Status 双链表插入(DLinkList L, int i, Elemtype e)
{
    DLinkList p,a;
    取第i个元素地址(L, i, a);
    if ( a!= p)
        return false;

    auto s = new DLnode;
...

或者写得更简单简洁一点:

DLinkList 取第i个元素地址(DLinkList L, int i)
{
    DLinkList p;
    p = L->next;
    int j = 1;
    while (p && i > j)
    {
        p = p->next;
        j++;
    }
    if (i < 0 || i < j || !p)
        return 0;
    return p;
}

此时,插入函数内引用函数语句为:

Status 双链表插入(DLinkList L, int i, Elemtype e)
{
    DLinkList p;
    if ( 取第i个元素地址(L, i)!= p)
        return false;

    auto s = new DLnode;

    s->data = e;//别忘了写

    s->prior = p->prior;
    p->prior->next = s;
    s->next = p;
    p->prior = s;
    return true;
}

(4):最后

关于新建的节点s,我们可以像前面所示例的程序一样:

直接创建一个新节点s,节点名直接作为指向结点的指针(这样更加方便快捷)

也可以像上课说的那样:

创立一个新的节点,创建指针指向该节点:(这也就是我们先前在学C++时介绍的最常见的用法)

    DLnode *s = new DLnode;

双向链表的删除

删除链表第i位元素(已知第i位结点)

Status 双链表删除(DLinkList L, int i, Elemtype e)
{
    DLinkList p = 取第i个元素地址(L, i);
    if (p->data != e)
        return false;

    p->next->prior = p->prior;
    //后继节点的前驱设置为前驱结点地址
    p->prior->next = p->next;
    delete p;
    return true;
}

 前置条件同(3)第二个程序版本

这里,我们设计的程序认为:e用来表示结点信息本身(已知结点信息)

而PPT想表达的意思为:e用来返回结点信息:

Status 双链表删除(DLinkList L, int i, Elemtype &e)
{
    DLinkList p;
    if (p != 取第i个元素地址(L, i))
        return false;

    e = p->data;

    p->next->prior = p->prior;
    //后继节点的前驱设置为前驱结点地址
    p->prior->next = p->next;
    delete p;
    return true;
}

 时间复杂度:O(1)

删除链表第i位元素(未给出结点位序)

既然你没有给出我们结点位序,那你至少需要给出我们结点信息data

要不然你啥也不给,我们肯定是找不到这个结点在哪的

DLinkList LocateELem(DLinkList L, Elemtype e)
{
    //在线性表L中查找值为e的数据元素
    //找到,则返回L中值为e的数据元素的地址,查找失败返回NULL
    auto p = L->next; int i = 1;
    while (p && p->data != e)
    {
        i++;
        if (e == p->data)
        {
            //cout << "地址为:  " << p << ";" << endl;
            //cout << "位置序号为:  " << i << ";" << endl;
            return p;
        }
        p = p->next;
    }
    if (p == NULL)
        return NULL;
    //return 1;
}
Status 双链表删除(DLinkList L, Elemtype &e)
{
    DLinkList p;
    if (p != LocateELem(L, e))
        return false;

    e = p->data;

    p->next->prior = p->prior;
    //后继节点的前驱设置为前驱结点地址
    p->prior->next = p->next;
    delete p;
    return true;
}

 时间复杂度:O(n)

因为查找算法本身需要的时间复杂度为O(n)

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

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

相关文章

NVIDIA NCCL 源码学习(九)- 单机内ncclSend和ncclRecv的过程

上节介绍了通信链路的建立过程&#xff0c;本节介绍下单机内部ncclSend和ncclRecv的运行过程。 单机内的通信都是通过kernel来进行的&#xff0c;所以整个通信的过程可以分为两步&#xff0c;第一步是准备kernel相关的参数&#xff0c;第二步是实际执行kernel的过程。 为方便…

闯关(贪心)

某综艺频道推出了一个闯关活动。 活动一共包含 n 个关卡&#xff08;编号 1∼n&#xff09;&#xff0c;其中 m 个关卡为特殊关卡。 每个关卡都有一个通关分数&#xff0c;其中第 i 个关卡的通关分数为 ai。 挑战者可以自由决定所有关卡的具体挑战顺序&#xff0c;并且每通过…

【工具类】Elasticsearch的HTTP客户端(Java)

一、介绍 1. 原理 Java基于Http请求操作ES&#xff0c;与Kibana上的操作一致。 Kibana上的dsl与Http的关系&#xff1a; GET、POST等同于HTTP的POSTPUT 等同于HTTP的PUTDELETE 等同于HTTP的DELETE 如图 该DSL可转化为HTTP请求 POST ip:port/docwrite/_search 请求体为&…

Mysql online DDL工具:gh-ost

gh-ost特点&#xff1a; 1、不使用触发器。 在gh-ost出现之前第三方MySQL DDL工具均采用触发器的方式进行实现&#xff0c;包括前面percona的pt-osc&#xff0c;Facebook的OSC等等。而gh-ost采用的机制和他们完全不同&#xff1a;它通过MySQL binlog来同步数据。 gh-ost会伪装…

PowerToys 微软效率工具包 使用指南

PowerToys 微软效率工具包 使用教程 Microsoft PowerToys 是一组实用程序&#xff0c;供高级用户调整和简化其 Windows 10 和 11 体验以提高工作效率。 下载 PowerToys⇲ 安装教程 1.双击文件运行 点击我同意 2.等待下载安装完成 3.安装完成 使用指南 Always on Top 通…

FOHEART H1数据手套驱动Optitrack光学动捕双手运动(Unity3D)

本教程将在Unity3D中混合Optitrack与数据手套的数据流&#xff0c;在人体运动的基础上&#xff0c;添加双手手指部分的运动。 双手手背的角度仍由Optitrack提供&#xff0c;数据手套提供双手手指的角度。 01 客户端软件 分别安装MotiveBody与MotionVenus并校准人体与数据…

Kafka-Topic创建源码分析

Kafka-Topic创建源码分析 在kafka中,创建topic通过使用kafka-topics.sh脚本或者直接调用AdminClient对外提供的adminApi来进行创建. 即使是使用kafka-topics.sh&#xff0c;其最终会通过生成并调用AdminClient来进行处理. 0,创建topic流程图 1,创建topic示例代码 通过引入A…

工作两年半,终于学会了Jenkins部署Maven项目

上期我们讲了Linux部署Jenkins Linux安装Jenkins&#xff08;Java11最新版&#xff09; 这期我们来讲的是使用Jenkins部署一个maven项目 文章目录&#x1f46e;所需要的环境&#xff08;必须要有&#xff0c;否则不能进行下一步&#xff09;&#x1f64b;第一步&#xff0c;安装…

02 运算符

目录 第一章&#xff1a;概述 第二章&#xff1a;算术运算符 2.1 概述 2.2 应用示例 2.3 号的两种用法 2.4 自增自减运算 2.4.1 概述 2.4.2 单独使用 2.4.3 复合使用 第三章&#xff1a;赋值运算符 3.1 概述 3.2 应用示例 第四章&#xff1a;关系运算符&#xff0…

vue3.0找不到模块“./App.vue”或其相应的类型声明

vue3报错提示 找不到模块“/App.vue”或其相应的类型声明 情况一、vue3.0js 报错原因&#xff1a;javascript只能理解.js文件&#xff0c;无法理解.vue文件。 解决方案&#xff1a;根目录新建jsconfig.json {"compilerOptions": {"baseUrl": "./&qu…

被取消的AP考试到底是什么嘞?

最近&#xff0c;好多考试都延期或取消了&#xff0c;美国大学理事会&#xff08;College Board&#xff09;也发布公告&#xff0c;宣布受疫情影响&#xff0c;上海、北京等地的AP考试正式取消&#xff0c;不提供线上考试的机会&#xff0c;而且不会安排后续补考。 这条消息惊…

芯片漫游指南(5)-- UVM寄存器

目录1.寄存器模型概览1.1 概述1.2 uvm_reg相关概念1.3 MCDF寄存器模型1.4 寄存器建模1.5 模型使用流程2.寄存器模型集成2.1 总线UVC的实现2.2 纵向UVC的示例3.寄存器模型的常规方法3.1 mirror、desired和actual value3.2 prediction的分类3.3 uvm_reg的访问方法3.4 mem与reg的联…

python扩展实现方法--python与c混和编程

大部分的Python的扩展都是用C语言写的&#xff0c;但也很容易移植到C中。 一般来说&#xff0c;所有能被整合或者导入到其它python脚本的代码&#xff0c;都可以称为扩展。 扩展可以用纯Python来写&#xff0c;也可以用C或者C之类的编译型的语言来扩展。 就算是相同的架构的两…

Spring Boot学习篇(三)之通用mapper的使用(oracle版)

Spring Boot学习篇(三)之通用mapper的使用(oracle版) 1 配置pom.xml <parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version>2.7.2</version></parent> <de…

概念辨析|电子文件单轨制与电子档案单套制

电子文件单轨制和电子档案单套制是档案业务中的重要概念&#xff0c;在建设数字中国的时代背景下&#xff0c;厘清“套”与“轨”的区别和联系是革新档案工作的基础 产生背景 随着信息技术的不断发展和政务信息化的持续推进&#xff0c;电子文件逐渐在业务工作中大量出现&…

从工具到实践:如何在GitHub上保障开源项目安全?

1998年&#xff0c;Christine Peterson创造了 “开源软件”这个词。她解释道&#xff1a;“这是刻意为之&#xff0c;为了让其他人更容易理解这个领域”。同年&#xff0c;O’Reilly组织了首届“开源峰会”。 开源软件受到更多人青睐原因在于&#xff0c;用户对软件拥有更多的…

【圣诞节】简单代码实现圣诞树|圣诞贺卡 | 快来为心爱的她送上专属的圣诞礼物叭~

圣诞节马上就要到了&#xff0c;不知道给自己喜欢的人准备什么样的惊喜吗&#xff1f;作为一名程序员&#xff0c;当然是用编程制作专属于她or他的圣诞树&#xff01; 目录 &#x1f384;圣诞树 ✨3D圣诞树 代码块 打开方式 修改位置 效果展示 ✨音乐律动圣诞树 代码块…

详解 Vue 过渡 transition 动画 animation 并结合第三方库 animation.css 和 gsap

transition vue过渡组件 标签自带类名 触发时机默认类名 自定义类名 <transition name"xxx"> 自定义行内式类名 方便结合第三方库 transition 钩子 接收参数el enter 和leave 第二个参数 done 可以 决定 after-enter after-leave 的 周期内的执行时机 v-…

YonBuilder移动开发平台 AVM框架 封装虚拟数字键盘组件

AVM&#xff08;Application-View-Model&#xff09;前端组件化开发模式基于标准Web Components组件化思想&#xff0c;提供包含虚拟DOM和Runtime的编程框架avm.js以及多端统一编译工具&#xff0c;完全兼容Web Components标准&#xff0c;同时兼容Vue和React语法糖编写代码&am…

Codeforces Round #697 (Div. 3) E. Advertising Agency

翻译&#xff1a; 玛莎在一家广告公司工作。为了推广新品牌&#xff0c;她想和一些博主签约。玛莎总共有&#x1d45b;个不同的博主。编号为&#x1d456;的博主拥有&#x1d44e;&#x1d456;名粉丝。 由于玛莎的预算有限&#xff0c;她只能与&#x1d458;不同的博主签约。…