数据结构与算法基础(王卓)(8):线性表的应用(并集和有序表合并)

news2024/11/28 8:31:59

PPT:第二章P173;


并集集合:线性表的合并(无需有序,不能重复)

线性表:

Status Union(Sqlist& A, Sqlist& B)//并集
{
    int len_A = A.length;
    int len_B = B.length;
    for (int i = 1; i <= len_B; i++)
    {
        Poly e=*A.elem;
        //这里只是给我们设定的元素e赋一个任意初值
        //只要保证e在初始化时由初值不为空即可
        //至于该e元素的内容是什么其实并没有什么所谓
        //因为后面我们总归是会改的
        GetElem(B, i, e);
        if (LocateElem(A, e))
            return ERROR;
        else
            ListInsert(A, ++len_A, e);
        //注意插入函数中输入的是位序,不是数组下标
    }
    return true;
}

该算法的时间复杂度:O(ListLenth(La) * ListLengrh(Lb))

最后A表为合并以后的新表


链表:

Status Union(Lnode& A, Lnode& B)
{
    for (int i = 1; i <= 求表长(&B); i++)
    {
        int len_A = 求表长(&A);
        Elemtype e;
        取第i个元素(&A, i, e);
        if (!LocateELem(&B, e))
            Listlnsert(&A, ++len_A, e);
    }
    return true;
}

结果:(默认在(7):小结:关于链表和线性表的定义及操作当中预设的前置语句之下运行) 

而产生该结果的原因,和前置语句中,定义插入函数的语句有关:

Status Listlnsert(LinkList& L, int i, Elemtype e)

更准确的来说:问题源于我们给出的 “&A”和定义中的“LinkList& L”类型匹配不上

想要程序能够正常运行,改动的方法有两种:


1:

修改<插入>函数的函数体:(此时<合并>函数无需修改)

把合并函数的“声明”(函数体之外)部分改为:

Status Listlnsert(LinkList L, int i, Elemtype e)

此时,该合并函数所需要的前置条件(最简单版本)为:

//链表的定义及其基础操作
#include<iostream>
using namespace std;
#include<stdlib.h>//存放exit

#define TRUE        1
#define FALSE       0
#define OK          1
#define ERROR       0
#define INFEASIBLE  -1
#define OVERFLOW   -2   

#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 Lnode
    //node:结; 结点;
{
    Elemtype data;
    Lnode* next;
};
typedef Lnode* LinkList;

Status 链表是否为空(LinkList L)
{
    if (L->next)
        return true;
    else
        return false;
}
Status 求表长(LinkList L)
{
    if (链表是否为空(L))
        cerr << "链表为空" << endl;
    LinkList p = L->next;
    //特别注意:因为这里从首元结点开始算起(计算)
    //所以:L->next;
    int i = 0;
    while (p)//不要写成if
    {
        p = p->next;
        i++;
    }
    //cout << "表长为:  " << i << endl;
    return i;
}

Status 取第i个元素(LinkList L, int i, Elemtype e)
{// GetElem“i”
    LinkList p;
    p = L->next;
    int j = 1;
    while (p && i > j)
    {
        p = p->next;
        j++;
    }
    if (i < 0 || i < j || !p)
        return false;
    e = p->data;
    return true;
}

Status LocateELem(LinkList 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;
        }
        p = p->next;
    }
    if (p == NULL)
        return NULL;
    return true;
}

Status Listlnsert(LinkList L, int i, Elemtype e)
{//插入(把元素e插到第i个位置结点上)
    auto p = L; int j = 0;
    while (p && j < i - 1)
    {
        p = p->next; ++j;
    }
    if (!p || j > i - 1)
        return false;
    auto s = new Lnode;
    s->data = e;
    s->next = p->next;
    p->next = s;
    return true;
}//Listlnsert_L


2:
修改<合并>函数的函数体:(在前置语句保持不变,不必改动的情况下)

Status Union(Lnode& A, Lnode& B)
{
    for (int i = 1; i <= 求表长(&B); i++)
    {
        int len_A = 求表长(&A);
        Elemtype e;
        LinkList p = &A;
        取第i个元素(&A, i, e);
        if (!LocateELem(&B, e))
            Listlnsert(p, ++len_A, e);
    }
    return true;
}


那么此时,一个让我觉得很有意思(很奇怪)的现象(情况)就发生了

问题:

为什么在前面“&A”和定义中的“LinkList& L”类型匹配不上

但是只要我们把“&A”放到一个新的该类型的变量当中,让该信息以变量的形式在程序中执行

放进去的明明都是同一个东西(&A),凭什么(怎么)原来的时候程序就可以运行了呢???


合并为一个新的整体:有序表的合并(有序,可重复)

线性表:

对于该操作的具体实现的流程设计:(各模块)

  1. 创建一个空表C
  2. 依次从A或B(中)“摘取”元素值较小的结点插入到C表的最后,直至其

    中一表变空

  3. 继续将A或B其中一表的剩余结点插入C表的最后


模块一:

对于这里的模块一,我们需要进行的:

建一个新表来返回两表合并后的结果(最终合并后的表)的操作的整个学习过程与问题,详见:

数据结构与算法基础(王卓)(8)附:关于new的使用方法详解part 2;

而在本程序中,我们使用的语句,即:

    C.elem = new Elemtype[100]; 

模块二:

其中,模块二的流程实现,又具体细分为:

  1.  摘取两表中元素值较小的结点
  2. 将结点插入到C表的结尾
  3. 重复循环“1”、“2”步操作,直至其中一表变为空为止

project1:

    //不用指针,直接硬钢判断语句
    int i = 0,//对应A表
        j = 0,//对应B表
        k = 0;//对应C表
    while (i < A.length || j < B.length)
    {
        if (A.elem[i] > B.elem[j])
        {
            C.elem[k] = B.elem[i];
            i++;
            k++;
        }

        if (A.elem[i] == B.elem[j])
        {
            C.elem[k] = A.elem[i];
            C.elem[++k] = B.elem[j];
            i++;
            j++;
            k++;
        }

        else//        if (A.elem[i] < B.elem[j])

        {
            C.elem[k] = A.elem[i];
            i++;
            k++;
        }
//当然,也可以先大于小于再等于

说明:

(1):

在模块二中,两表相比较的两结点元素值都相等的语句,也可以写为:

        if (A.elem[i] == B.elem[j])
        {
            C.elem[k] = A.elem[i];
            C.elem[++k] = B.elem[j];
            i++;
            j++;
            k++;
        }

(2):

需要注意(记住),本来(一开始),对于循环执行(的)判断语句,我们本来想写为

    while(A.elem[i] != 0 || B.elem[j] != 0)

然而,结果显示:

首先,第一点确定无疑的事情(结论)就是:

在这里,我们的程序不能完成该语句中的“!=”判断

在这里,想要程序能够成功实现执行该判断,我们可以有如下两种解决办法:

  • 手撸一个关于<Poly类型> != <int类型>的判断定义表达式
  • 定义结点为空时,该空节点的内容;即:定义一个这样的空结点

当然,要真这么写,可以是也可以,但是太过麻烦,我们这里就不选择这种方法了


project 2:(利用线性表地址线性排列存储的性质)

    //利用指针
    Poly* pa, * pb, * pc;
    pa = A.elem;
    pb = B.elem;
    pc = C.elem;

    //*pa = A.elem[0];
    //*pb = B.elem[0];

    while (pa <= &A.elem[A.length - 1] || pb < &B.elem[B.length - 1])
    {
        if (*pa > *pb)
        {
            *pc = *pb;
            pa++;
            pc++;
        }

        if (*pa == *pb)
        {
            *pc = *pa;
            *(++pc) = *pb;
            pa++;
            pb++;
            pc++;
        }

        else
        {
            *pc = *pa;
            pa++;
            pc++;
        }

模块三:

    if (A.length > B.length)
    {
        //while (i < A.length)  同理,后面不再赘述
        while (pa <= &A.elem[A.length - 1])    
        {
            *pc = *pa;
            pc++;
            pa++;
        }
    }
    else
    {
        while (pb <= &B.elem[B.length - 1])
        {
            *pc = *pb;
            pc++;
            pb++;
        }
    }

最终修改打磨:

根据(参考)PPT(178)中的标准答案,我们发现以下地方仍有修改的空间:


模块一:

一方面:

我们没有给新建的C表的length元素赋值

另一方面:

C表的长度是A表和B表两表长度的总和

如果还是只是固定的,像开辟和A表B表一样的固定为100的大小的空间,未免有些不妥:

  C.length = A.length + B.length; 
  C.elem = new Elemtype[C.length];

另外,模块二这里我们写的限定条件:

    while (pa <= &A.elem[A.length - 1] || pb < &B.elem[B.length - 1])

写成:

    while (pa <= A.elem + A.length - 1 || pb < B.elem + B.length - 1)

也同理(一样);(标准答案就是按后者这么写的,但我感觉这么写倒也没有什么特别过人之处)


但是另一个问题就严重了:

在C++中:

与:&&

或:||

非:!

所以应该改为:

    while (pa <= A.elem + A.length - 1 && pb < B.elem + B.length - 1)

 或者:

    while (pa <= &A.elem[A.length - 1] && pb < &B.elem[B.length - 1])

另外:

在模块二中关于这个循环的循环体,我写的倒是也没有什么大的错误

但是太过累赘,还是标准答案上写的更加简洁和方便

当然他其实没有我写的那么严谨:具体写出两节点元素值相等时的操作流程,可以让循环次数减少

但是从大的时间复杂度的角度来说,其实n次循环和(n-5)次循环本质上没有太大区别

所以这里我们还是选择标准答案上的写法:

    while (pa <= &A.elem[A.length - 1] && pb < &B.elem[B.length - 1])
    {
        if (*pa < *pb)
            *pc++ = *pa++;
        else
            *pc++ = *pb++;
    }

该写法,即:

先(给C表中最后的结点(*pc))赋值,再自增;一个语句实现


最后,关于模块三:

其实我们不用在去设置看A和B哪个表更长的判断语句

因为其实我们即使直接写两个循环语句,依然不影响程序的运行

因为一个表的指针已经到达该表的尾结点以后,自然就不符合该循环的循环判断条件:

        while (pa <= &A.elem[A.length - 1])    

        while (pb <= &B.elem[B.length - 1])

了,另外,这里的函数体,我们也可以写为像上面一样的“先(给C表中最后的结点(*pc))赋值,再自增;”的形式:

        while (pa <= &A.elem[A.length - 1])    
        {
            *pc++ = *pa++;
        }
        while (pb <= &B.elem[B.length - 1])
        {
            *pc++ = *pb++;
        }

综上:

关于线性表的:有序表的合并(有序,可重复)操作如下:

int Merge(Sqlist A, Sqlist B, Sqlist& C)
{//合并; 融入; (使)结合; 并入; 相融;

    typedef Poly Elemtype;

    C.length = A.length + B.length;
    C.elem = new Elemtype[C.length];

    //利用指针
    Poly* pa, * pb, * pc;
    pa = A.elem;
    pb = B.elem;
    pc = C.elem;
    //*pa = A.elem[0];
    //*pb = B.elem[0];

    while (pa <= &A.elem[A.length - 1] && pb < &B.elem[B.length - 1])
    {
        if (*pa < *pb)
            *pc++ = *pa++;
        else
            *pc++ = *pb++;
    }
    //
    while (pa <= &A.elem[A.length - 1])
    {
        *pc++ = *pa++;
    }
    while (pb <= &B.elem[B.length - 1])
    {
        *pc++ = *pb++;
    }
    //
    return true;
}

链表:

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

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

相关文章

研究生如何能(较快)找出某领域(去噪)已有算法的创新点或者引入其他领域的新算法?

广义上说&#xff0c;滤波就是给不同的信号分量分配不同的权重&#xff0c;较为复杂的维纳滤波, 是根据信号的统计量设计权重。狭义上说&#xff0c;降噪/去噪&#xff0c;可以看成滤波的一种。降噪的目的在于突出信号本身而抑制噪声影响。从这个角度&#xff0c;降噪就是给信号…

C/C++ 调用规则

平栈&#xff1a;清理参数对调用栈的操作步骤&#xff1a;参数传递三种调用约定&#xff1a;cdecl &#xff08;C调用约定&#xff09;:从右往左传参&#xff0c;参数通过栈传递&#xff0c;调用方(caller)负责平参&#xff08;支持类似printf的不定参&#xff09;stdcall (标准…

hadoop简介

文章目录1&#xff1a;hadoop简介2&#xff1a;Hadoop系统2.1&#xff1a;hadoop架构1&#xff1a;MapReduce2&#xff1a;YARN架构3&#xff1a;HDFS2.2&#xff1a;HDFS、YARN、MapReduce三者关系1&#xff1a;hadoop简介 Hadoop是一个由Apache基金会所开发的分布式系统基础…

如何快速删除CSV、Excel、Markdown表格的重复行?

如果你正在使用 CSV、Excel 或 Markdown 表格&#xff0c;你可能会遇到重复行的问题。这可能是因为你手动输入了重复的数据&#xff0c;或者是因为你从其他源导入了重复的数据。无论原因是什么&#xff0c;删除重复行是一项重要的数据清理任务。本文将向你展示如何使用几种不同…

RESTful的风格提倡 URL 地址使用统一的风格设计

RESTful概念实现REST&#xff1a;Representational State Transfer&#xff0c;表现层资源状态转移。资源&#xff1a;资源是一种看待服务器的方式&#xff0c;即&#xff0c;将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。资源的表述资源的表…

nnUNet 训练 AMOS22数据集 Task216(抽丝剥茧指令+原理篇)

环境准备篇 安装hiddenlayer&#xff08;用来生成什么网络拓扑图&#xff1f;管他呢&#xff0c;装吧&#xff09; pip install --upgrade githttps://github.com/nanohanno/hiddenlayer.gitbugfix/get_trace_graph#egghiddenlayer 安装环境&#xff0c;由于服务器已经装好py…

网络安全日益严峻下计算机主机加固的意义

​ 近年来&#xff0c;计算机以及互联网应用在中国得到普及和发展&#xff0c;已经深入到社会每个角落&#xff0c;政府&#xff0c;经济&#xff0c;军事&#xff0c;社会&#xff0c;文化和人们生活等各方面都越来越依赖于计算机和网络&#xff0c;电子政务&#xff0c;无纸办…

【计算机体系结构】指令集体系结构、微体系结构简介

1. “虚拟” to “现实” 首先可以看这张图片&#xff0c;下面的 Physics 所指的是我们的物理世界中看得见摸得到或者是客观存在的事物&#xff0c;而人类希望将自己的工作内容或者需求以某种方式映射到物理层面上&#xff0c;用物理变化带来的影响来完成人类工作内容。例如早期…

《Linux Shell脚本攻略》学习笔记-第二章

2.1 简介 本章将为你介绍一些最值得关注同时也是最实用的命令。 2.2 用cat进行拼接 cat命令能够显示或者拼接文件内容。cat能够将标准输入数据与文件数据组合在一起。 通常的做法是将stdin重定向到一个文件&#xff0c;然后再合并两个文件。而cat命令一次就能搞定这些操作。 1&…

【Linux】基础常见指令

目录​​​​​​​ 前言 一、Linux的环境搭建与远程控制 Linux 环境的搭建方式主要有三种 使用 XShell 远程登陆到 Linux 二、常见指令 1. ls 指令 2. pwd命令 3. cd 指令 4. touch指令 5. mkdir指令 6. rmdir指令 && rm 指令 7. man指令 8. cp指令 9. mv指令 10.…

【微信小程序】实用教程02-添加全局页面配置、页面、底部导航

开始前&#xff0c;请先完成项目创建&#xff0c;详见 【微信小程序】实用教程01-注册登录账号&#xff0c;获取 AppID、下载安装开发工具、创建项目、上传体验 前期准备 因我们的项目是根据模板创建的&#xff0c;需先清理掉无效的页面代码&#xff0c;具体操作方式如下&…

LeetCode 64. 最小路径和

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 64. 最小路径和&#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 一、题目名称 LeetCode 64…

【Redis】使用阻塞队列+Redis优化秒杀业务

【Redis】使用阻塞队列Redis优化秒杀业务 文章目录【Redis】使用阻塞队列Redis优化秒杀业务1. 为什么要优化2. 怎么优化2.1 查询优惠卷2.2 判断秒杀库存2.3 校验一人一单2.4 减库存2.5 创建订单2.6 保证redis操作的原子性3. 确认优化方案4. 实现优化方案4.1 编写lua脚本4.2 定义…

jvm系列(3)--运行时数据区Runtime

目录运行时数据区概述及线程前言运行时数据区结构运行时数据区与内存线程的内存空间Runtime类线程JVM 线程JVM 系统线程程序计数器(PC寄存器)PC寄存器介绍PC寄存器的作用举例两个面试题CPU 时间片本地方法接口本地方法举例为什么要使用 Native Method&#xff1f;与Java环境外交…

Tomcat打破双亲委派模型

tomcat的类加载器结构tomcat的类加载&#xff08;loadClass&#xff09;过程和原本的双亲委派模型思路差不多&#xff0c;先看有没有加载过。先在本地 Cache 查找该类是否已经加载过&#xff0c;也就是说 Tomcat 的类加载器是否已经加载过这个类。如果 Tomcat 类加载器没有加载…

【学习笔记】【Pytorch】张量(Tensor)的基础操作

【学习笔记】【Pytorch】张量&#xff08;Tensor&#xff09;的基础操作一、创建张量1.使用数据创建张量2.无需数据的创建选项3.torch.Tensor与torch.tensor的区别4.PyTorch中张量的创建方法的选择二、张量的属性1.张量的 torch.dtype2.张量的 torch.device3.张量的 torch.layo…

阿维塔冲击年10万台订单,第二款车型Q2发布

1月13日&#xff0c;阿维塔科技在重庆总部召开渠道合作伙伴大会。今年&#xff0c;阿维塔计划推出&#xff1a; •阿维塔11后驱版本 •并发布第二款产品&#xff0c;代号E12&#xff0c;定位中大型轿车。阿维塔今年计划冲击10万辆订单目标。在当前CHN平台的基础上&#xff0c;阿…

Openresty记录笔记

最近由于项目需要学习了安全代理的相关知识&#xff0c;其实刚开始的时候是非常需要一个入门的介绍&#xff0c;大概说明下这个到底是个什么东西&#xff0c;能干啥&#xff0c;简单的原理是什么&#xff0c;为此我记录下我看完用完的心得&#xff0c;记录成笔记。 一般我们代码…

Redis 持久化详解

目录一、简介二、RDB持久化2.1、SAVE2.2、BGSAVE2.3、SAVE选项2.4、RDB文件结构2.5、RDB文件载入三、AOF持久化3.1、开启AOF功能3.2、配置AOF文件的冲洗频率3.3、AOF重写3.3.1、BGREWRITEAOF命令&#xff08;手动&#xff09;3.3.2、AOF重写配置选项&#xff08;自动&#xff0…

Android | Service

Android Service Service 概念 实现程序后台运行的解决方案&#xff0c;一种可在后台执行长时间运行操作而不提供界面的应用组件。Service 的运行不依赖于任何用户界面&#xff0c;即使程序被切换到后台&#xff0c;或者用户打开了另外一个应用程序&#xff0c;Service 仍然能…