我们先引入表的自然连接

知乎回答:

自然连接是在广义笛卡尔积R×S中选出同名属性上符合相等条件元组,再进行投影,去掉重复的同名属性,组成新的关系。自然连接是一种特殊的等值链接,它要求两个关系中进行比较的分量必须是相同的属性组,并且在结果中把重复的属性列去掉,所以根据T关系中的有序组可知R和S进行的是自然连接。我是这么通俗地想的这道题:你看R和S,两者第一行,都有个B,那么第一行可以很自然地接上,根据定义,把重复的B留一个就行;再第二行,两者共有一个1,也同理衔接;再看第三行,R与S二者无相同项,无法自然连接。最后连接在一起就是前两行的了。

作者:不错
链接:https://www.zhihu.com/question/317633206/answer/1707523986
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

按照我的理解:

自然连接:是一种特殊的等值连接,它要求两个关系进行比较的分量必须是相同的属性组,并且在结果集中将重复属性列去掉。

我们看一个问题:

●有表A , m1 行 , n1 列

●有表B , m2行 , n2 列

●求自然连接的结果

连接的条件 :表A 的第 i 列与 表 第 j 列相等

C = A ▷◁ B

  i=j

例如 :


表A 的第1 列的元素,如果有元素和 表 B第 1 列的元素有重合的话,就把两个表 那一列的那一行 连起来,

例如: 2 1 2  2 1

2 1 2 2 4

1 1 2 1 2

5 2 1 5 3

这样就找到了两个表有共同属性的数据


我们现在举一个现实中的例子,

假如 A 是餐厅的顾客数据 ,   B是隔壁按摩店的顾客数据, 这两家时常会有顾客,一条龙服务, 吃完饭后,去按摩,

所以现在饭店老板就想收购这家按摩店 , 想来搞一个数据分析,调查同时去 A 和 B 的顾客的信息,然后再根据信息调查,


所以, 现在我们就要找的就是 同时去 A 餐厅和 去 B 按摩店的人

人们一次只吃一顿饭 , 所以 A 列表里没有重复的人名 ,而吃完饭去按摩店的人可以选择不同类型的服务 , 所以同一个人可以记录很多条不同的服务

现在我们找到A 和 B 的账本,如上图 ,所以我们要做得事情就是把两家共同的顾客的信息列出来

现在开始我们的人脑模拟 :

先去A的第一行的第一个节点 ,取得 3, 然后拿着3 ,去B账单每一行的第一个数据进行对比

遍历完发现 3 没有去过按摩店 , 那这个顾客就不记录了

然后再去A 的第二行的第一个节点, 取得顾客2 , 然后去遍历B账单的每一行第一个数据

发现顾客 2 有过两次服务 就记录两次

2 1 2 2 1

2 1 2 2 4

以此类推, 取到顾客 4 的名字, 然后去便利 B,发现没有就不记录

顾客1 ,然后去B遍历所有记录

1 1 2 1 2

顾客 5 ,再去遍历B记录

5 2 1 5 3

这样,我们记录的数据就是

2 1 2 2 1

2 1 2 2 4

1 1 2 1 2

5 2 1 5 3

用人脑模拟 , 这的确很快, 但是如果有很多按摩店的数据呢? 我们就需要利用计算机了, 我们的思路就是先取到 列表A 的第一行的第一个节点 ,然后拿着这一个节点去循环遍历 B 每行的第一个元素 ,

如果和B名字有重合的话, 就把顾客的 饭店数据和 按摩店数据 连起来 ,

那结束的条件是什么呢? 我们拿到 A中一个顾客的姓名, 然后去遍历 B的所有行的顾客姓名,

然后拿到A 的第二个顾客的名字, 再去遍历B的所有顾客姓名 ,

直到 A 遍历完为止 ,

有名字重合的话,就把饭店信息和按摩店信息连接起来 ,当成列表的一行

把信息全再传到列表里就可以了.

构造承载数据类型的列表:

我们首先想到的就是数组了,但是我们一行的几个数组成一个数组的话, 我们有很多行,如何链接起来呢?

那当然是用链表了 ,这样也方便插入每个顾客的信息了,


表 A 数据我们用 链表 h1 存储 ,表 B 数据我们用 h2存储 , 头结点是代表这个线性表有几行几列 , 每行的数据用数据存储, 然后再指引下一行就可以了

操作流程就是:

先构建链表节点的数据结构 ,然后构建头结点 ,构建两个链表 ,

然后遍历 h1 的第二个节点的第一个数,取到后,去遍历h2的每个节点的第一个数 , 如果和h1节点中的那个数,相等的话, 我们就把这两个节点的数据连接起来,存在数组里面,然后构建一个新节点,

形成 h 链表 ,这个 h 链表里面就是我们所需要的数据.

h里面存放的是我们所遍历B和 A ,以A为主,遍历 B中和 A有相同性质的数据 ,然后把数据联系起来.

我们这个找的是,名字相同的人,在两家,进行的服务的信息整理.

构造存储信息的节点的数据结构:

定义节点数据类型

typedef int ElemType;

定义数据节点的数据结构

typedef struct Node1        //定义存储数据的节点
{
    ElemType data[MaxCol];    //定义存储数据的节点数组
    struct Node1 *next;        //定义节点的前驱指针
}DList;

typedef struct Node2        //定义头结点结构类型
{
    int    Row,Col;            //头结点数据存储列表行数和列数
    DList *next;            //头结点的后继指针
}HList;

创建数据链表

我们已经定义了,节点的数据结构类型,头结点定义列表的行列数, 后面的数据节点数组存储每行的数据,每个节点存储一行数据


下面,我们开始构建 创建如图链表的成员方法:

我们利用尾插法 , 插入节点,所以需要定义尾指针

传入要构建的链表地址指针

void CreateTable(HList *&h){

然后,需要创建头结点,先为头结点分配空间

 h=(HList *)malloc(sizeof(HList));       //创建头结点

这是我们刚才创建的头结点的类型 HList


接着,头结点置空

 h->next=NULL;

我们利用尾插法 , 插入节点,所以需要定义尾指针

DList *r;

我们插入的新数据节点,也需要定义指针

DList *s;

下面开始构建数据链表

注意:头节点数据表示列表的行数和列数 ,后面节点的数据区里存放的是数组,每个数组里面存放按照列的排序的数据, 每行通过指针来链接

所以,我们对应的去修改头结点的数据就行了

printf("请输入要创建表的行数和列数");

scanf("%d%d",&h->Row,&h->Col);

接着遍历每一行的每一列的每一个元素,进行对应赋值就行了

for(int i=0; i<h->Row;i++)
{printf("第%d行:",i+1);        //物理地址加1,就是逻辑地址 s=(DLink *)malloc(sizeof(DList)); //每一行算一个节点,每一行的数据存放在数组里面for(int j=0; j<h->Col; j++){scanf("%d",&s->data[j]);   //对节点数组进行按顺序赋值,一行的数据,按列正序输入}//因为我们第一次插入,头结点h的后继指针是空,所以我们让头结点的后继指针指向新节点s if(h->next == NULL){h->next=s;}else{r->next =s;}r=s;   //这里分析一下,为什么不让指向尾结点的指针r,直接指向头结点呢?因为头结点和数据节//点的数据结构不一样,我们定义的*r 是DList *r;//最后的时候,将尾结点置空就可以了r->next =NULL;
}

这样,我们构建两个数据链表的操作就完成了

接下来,就是遍历 h1 链表的每一个节点的数组的特定位置和 h2 的每一行的特定位置作对比了,

如果相等,那么就把这两个列表的那一行链接成一串,构成一个新节点

这样就把两个表 , 具有相同属性的数据链接存储起来了,

下面开始我们的代码实操:

我们首先传入创建好的链表 h1 ,链表 h2 ,和 需要创建的链表 h

void LinkTable(HList *h1,HList *h2,HList *&h){

注:我们现在是在成员方法里面, h1 和 h2 都是指针链表 ,已经创建好了,但是 h 链表还需要我们重新从头结点开始创建, 然后把符合我们上述属性的节点连接起来,当成数据节点就可以了.

我们需要首先指定需要把哪些相同属性的数据 , 通过遍历连接起来

表 A :我们需要遍历列表每行的第一个元素(姓名)

表B: 我们需要遍历列表每行的第一个元素(姓名)

映射到链表里面,就是每个节点的数组坐标


当然,我们也不局限于只链接这几列 , 可以指定 属性列,进行遍历链接,这是后话

通过输入属性的列表列序 ,来指定自然连接的位置

printf("连接字段是 : 第 1 个表位序 ,第二个表位序:");

int f1 , f2;

scanf("%d%d",&f1,&f2);        //定义要链接的列

//创建头结点

h=(HList *)malloc(sizeof(HList));

//头结点的数据区,初始的时候还没有插入节点, 置为0

h->Row=0;

//头结点的列,如果存在的话,那就是两个链表的列相加

h->Col = h1->Col + h2->Col;

//头结点的后继节点置为空

h->next = NULL;

现在存储数据的链表有了,就可以进行遍历,链接节点,创建节点,插入链表了

先确定表 A 的第一个节点 ,然后遍历 B的每个节点的数组的第一个元素,

如果相等,就把A 和 B 这两行连接起来,组成链表 h 的新节点

我们先定义,遍历 A 和 B 的指针 *p 和 *q

DList *p;

DList *q;

p  = h1->next;        //p初始指向 h1 头结点的后继节点


while (p!=NULL)
    {
        q=h2->next;                //初始的时候, q 指向 h2头结点的后继节点
        while (q!=NULL)        //当 h2的遍历节点不为空,就是没遍历到结尾的话
        {
            if (p->data[f1-1]==q->data[f2-1])       //如果找到相同属性的字段,就是对应字段值相等
            {
                s=(DList *)malloc(sizeof(DList));   //创建一个数据结点
                for (i=0; i<h1->Col; i++)               //复制表1的当前行
                 {

s->data[i]=p->data[i];            //节点数据复制到新节点

}           
                for (i=0; i<h2->Col; i++)
                {    s->data[h1->Col+i]=q->data[i];  //复制表2的当前行,数组的构建,按顺序构建
             //h的节点数据已经赋值 ,下面把新节点用尾插法插入到头节点 h 后面就行了

}

if (h->next==NULL)              //插入第一个数据结点
                 {

h->next=s;

}
                else                            //插入其他数据结点
                 {

r->next=s;               //为什么不让 r直接指向头结点,然后就少一步呢,因为r和头结                    }                                //  点的数据结构类型不一样
                r=s;                            //r始终指向最后数据结点
                h->Row++;                       //表行数增1
            }
            q=q->next;                          //表2下移一个记录
        }
        p=p->next;                              //表1下移一个记录,先内层循环,然后 外层循环
    }
    r->next=NULL;                               //表尾结点next域置空
}

这样,我们就可以构建一个 h 链表把我们所需要数据都存在里面了

下面怎么输出呢? 这不是很简单吗?

遍历输出一下就行了,先遍历节点,节点内部套一个 while循环遍历数组

void DispTable(HList *h)    //传入要输出的链表
{int j;DList *p=h->next;    //遍历输出的节点,初始的情况下,就是头结点的后继节点,头结点不用输出while (p!=NULL)        //循环遍历节点{for (j=0; j<h->Col; j++)    //我们传入了 h ,就知道了 h的属性 Colprintf("%4d",p->data[j]);    //输出节点数组的元素printf("\n");            //为了美观换行p=p->next;            //指向下一个节点,直到为空节点}
}

接下来,当然是主函数调用了:

int main()
{HList *h1,*h2,*h;printf("表1:\n");CreateTable(h1);            //创建表1printf("表2:\n");CreateTable(h2);            //创建表2LinkTable(h1,h2,h);         //连接两个表printf("连接结果表:\n");DispTable(h);               //输出连接结果return 0;
}

完整代码如下:

#include <stdio.h>
#include <malloc.h>
#define MaxCol  10          //最大列数
typedef int ElemType;
typedef struct Node1        //定义数据结点类型
{ElemType data[MaxCol];struct Node1 *next;     //指向后继数据结点
} DList;
typedef struct Node2        //定义头结点类型
{int Row,Col;            //行数和列数DList *next;            //指向第一个数据结点
} HList;
void CreateTable(HList *&h)
{int i,j;DList *r,*s;h=(HList *)malloc(sizeof(HList));       //创建头结点h->next=NULL;printf("表的行数,列数:");scanf("%d%d",&h->Row,&h->Col);for (i=0; i<h->Row; i++){printf("  第%d行:",i+1);s=(DList *)malloc(sizeof(DList));   //创建数据结点for (j=0; j<h->Col; j++)                //输入一行的数据初步统计scanf("%d",&s->data[j]);if (h->next==NULL)                  //插入第一个数据结点h->next=s;else                                //插入其他数据结点r->next=s;                      //将*s插入到*r结点之后r=s;                                //r始终指向最后一个数据结点}r->next=NULL;                           //表尾结点next域置空
}
void DispTable(HList *h)
{int j;DList *p=h->next;while (p!=NULL){for (j=0; j<h->Col; j++)printf("%4d",p->data[j]);printf("\n");p=p->next;}
}
void LinkTable(HList *h1,HList *h2,HList *&h)
{int f1,f2,i;DList *p=h1->next,*q,*s,*r;printf("连接字段是:第1个表位序,第2个表位序:");scanf("%d%d",&f1,&f2);h=(HList *)malloc(sizeof(HList));h->Row=0;h->Col=h1->Col+h2->Col;h->next=NULL;while (p!=NULL){q=h2->next;while (q!=NULL){if (p->data[f1-1]==q->data[f2-1])       //对应字段值相等{s=(DList *)malloc(sizeof(DList));   //创建一个数据结点for (i=0; i<h1->Col; i++)               //复制表1的当前行s->data[i]=p->data[i];for (i=0; i<h2->Col; i++)s->data[h1->Col+i]=q->data[i];  //复制表2的当前行if (h->next==NULL)              //插入第一个数据结点h->next=s;else                            //插入其他数据结点r->next=s;r=s;                            //r始终指向最后数据结点h->Row++;                       //表行数增1}q=q->next;                          //表2下移一个记录}p=p->next;                              //表1下移一个记录}r->next=NULL;                               //表尾结点next域置空
}
int main()
{HList *h1,*h2,*h;printf("表1:\n");CreateTable(h1);            //创建表1printf("表2:\n");CreateTable(h2);            //创建表2LinkTable(h1,h2,h);         //连接两个表printf("连接结果表:\n");DispTable(h);               //输出连接结果return 0;
}

表的自然连接(数据结构链表链接)相关推荐

  1. mysql 自然连接、内连接、外连接的区别

    数据库中的连接join分为内连接.自然连接.外连接,外连接又分为左外连接.右外连接.全外连接(注意:mysql不支持全外连接) 首先,我们先来建两张表,第一张表命名为kemu,第二张表命名为score ...

  2. 9.3.2 自然连接(NATURAL JOIN)

    9.3.2  自然连接(NATURAL JOIN) 自然连接(NATURAL JOIN)是一种特殊的等价连接,它将表中具有相同名称的列自动进行记录匹配.自然连接不必指定任何同等连接条件.图9.9给出了 ...

  3. 数据库中的内连接、自然连接和外连接的区别

    数据中的连接join分为内连接.自然连接.外连接,外连接又分为左外连接.右外连接.全外连接 当然,这些分类都是在连接的基础上,是从两个表中记录的笛卡尔积中选取满足连接的记录.笛卡尔积简单的说就是一个表 ...

  4. mysql存在外键的连接_MySQL(外连接、自然连接、新增-追加-删除外键、外键条件-约束)...

    外连接(outer join) left join:左外连接(左连接),以左表为主表 right join:右外连接(右连接),以右表为主表 基本语法:左表 left/right join 右表 on ...

  5. 数据库——自然连接、内连接、外连接(左外连接、右外连接、全外连接)、交叉连接

    1. 自然连接(*natural join)* 自然连接不用指定连接列,也不能使用ON语句,它默认比较两张表里相同的列, `SELECT*FROM student NATURAL JOIN score ...

  6. Mysql表连接:内连接、外连接、交叉连接、自然连接真的都不一样吗

    文章目录 前言 测试环境 创建测试数据 对比测试 内连接 交叉连接 外连接 左外连接 右外连接 自然连接 一般自然连接 自然左外连接 自然右外连接 STRAIGHT_JOIN 逗号分隔连接表 各种连接 ...

  7. 内连接、外链接、自然连接、全连接

    oracle sql的连接类型有: 内连接(inner join也叫join) 外链接:分为 左连接left join . 右连接right join 自然连接:natrue join.特点:连接后的 ...

  8. 数据库表连接总结:等值连接, 自然连接,左外连接,右外连接,内连接,全外连接;

    [1]等值连接 1)连接:凡是查询涉及到两个以上的表,就需要将表连接: 2)就是用where子句做的连接查询:连接查询的列名可以不同: [2]自然连接: select * from  a_tbl na ...

  9. 数据结构链表之符号表,Python3实现——8

    数据结构链表之符号表 符号表的介绍 之前章节介绍的顺序表和链表都是一个节点储存一个元素的表,但在日常生活中我们还有很多一次需要储存成对或多个值的情况,例如: 符号表最主要的目的将一对元素,用一个键和一 ...

最新文章

  1. 重磅嘉宾公布,第四范式AI新品发布会进入报名倒计时
  2. 块语法Block在MVC思维的妙用之多重M层代理传值
  3. Jlink接口的Jtag和SWD接口定义
  4. X210串口配置与stdio移植
  5. P0INP = 0Xfd;P1DIR |= 0X01;
  6. 49个Python学习资源:从初学者到高级玩家都有了
  7. 极大似然估计(MLE)、最大后验估计(MAE)
  8. 学习【新版的】韦东山嵌入式_day01
  9. GPU架构和Compute Shader线程规划
  10. 企业网络安全最常遇到的安全问题是什么,主要面临哪些威胁?
  11. 右键一直转圈圈(右键桌面和右键打开文件夹)的两种解决办法小总结
  12. 壳的机制以及脱壳技术
  13. 【LeetCode】517. 超级洗衣机 解题报告 (python)
  14. 数据科学家和人工智能职业生涯之外软件公司之外的热门市场
  15. 蚂蚁金服bPaaS究竟是什么?
  16. Windows 通过CMD指令导出的文件夹/文件的目录树
  17. 树莓派更改WiFi信息
  18. 深度解析Linux通过日志反查入侵
  19. 2017面向对象程序设计(Java)第十六周学习总结
  20. NOIP2018·赛道修建

热门文章

  1. C++的名字空间(很重要)
  2. jquery的$().each(function(i){})和和$.each(,function(i,n){})的区别和用法
  3. Linux笔记——软件包管理
  4. Java培训学习之Java开源软件的汇总
  5. 连不上微软服务器 dns,无法联系主 DNS 服务器 | Microsoft Docs
  6. Dubbo系列(二)源码分析之SPI机制
  7. vue所有页面刷新一次mounted(以及所有生命周期函数)执行两次的解决方法
  8. 用c 语言循环抓取网页,C语言获取网页源代码
  9. 基于 MySQL Binlog 的 Elasticsearch 数据同步实践 原
  10. REW声学测试(三):生成测试信号