表的自然连接(数据结构链表链接)
我们先引入表的自然连接
知乎回答:
自然连接是在广义笛卡尔积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;
}
表的自然连接(数据结构链表链接)相关推荐
- mysql 自然连接、内连接、外连接的区别
数据库中的连接join分为内连接.自然连接.外连接,外连接又分为左外连接.右外连接.全外连接(注意:mysql不支持全外连接) 首先,我们先来建两张表,第一张表命名为kemu,第二张表命名为score ...
- 9.3.2 自然连接(NATURAL JOIN)
9.3.2 自然连接(NATURAL JOIN) 自然连接(NATURAL JOIN)是一种特殊的等价连接,它将表中具有相同名称的列自动进行记录匹配.自然连接不必指定任何同等连接条件.图9.9给出了 ...
- 数据库中的内连接、自然连接和外连接的区别
数据中的连接join分为内连接.自然连接.外连接,外连接又分为左外连接.右外连接.全外连接 当然,这些分类都是在连接的基础上,是从两个表中记录的笛卡尔积中选取满足连接的记录.笛卡尔积简单的说就是一个表 ...
- mysql存在外键的连接_MySQL(外连接、自然连接、新增-追加-删除外键、外键条件-约束)...
外连接(outer join) left join:左外连接(左连接),以左表为主表 right join:右外连接(右连接),以右表为主表 基本语法:左表 left/right join 右表 on ...
- 数据库——自然连接、内连接、外连接(左外连接、右外连接、全外连接)、交叉连接
1. 自然连接(*natural join)* 自然连接不用指定连接列,也不能使用ON语句,它默认比较两张表里相同的列, `SELECT*FROM student NATURAL JOIN score ...
- Mysql表连接:内连接、外连接、交叉连接、自然连接真的都不一样吗
文章目录 前言 测试环境 创建测试数据 对比测试 内连接 交叉连接 外连接 左外连接 右外连接 自然连接 一般自然连接 自然左外连接 自然右外连接 STRAIGHT_JOIN 逗号分隔连接表 各种连接 ...
- 内连接、外链接、自然连接、全连接
oracle sql的连接类型有: 内连接(inner join也叫join) 外链接:分为 左连接left join . 右连接right join 自然连接:natrue join.特点:连接后的 ...
- 数据库表连接总结:等值连接, 自然连接,左外连接,右外连接,内连接,全外连接;
[1]等值连接 1)连接:凡是查询涉及到两个以上的表,就需要将表连接: 2)就是用where子句做的连接查询:连接查询的列名可以不同: [2]自然连接: select * from a_tbl na ...
- 数据结构链表之符号表,Python3实现——8
数据结构链表之符号表 符号表的介绍 之前章节介绍的顺序表和链表都是一个节点储存一个元素的表,但在日常生活中我们还有很多一次需要储存成对或多个值的情况,例如: 符号表最主要的目的将一对元素,用一个键和一 ...
最新文章
- 重磅嘉宾公布,第四范式AI新品发布会进入报名倒计时
- 块语法Block在MVC思维的妙用之多重M层代理传值
- Jlink接口的Jtag和SWD接口定义
- X210串口配置与stdio移植
- P0INP = 0Xfd;P1DIR |= 0X01;
- 49个Python学习资源:从初学者到高级玩家都有了
- 极大似然估计(MLE)、最大后验估计(MAE)
- 学习【新版的】韦东山嵌入式_day01
- GPU架构和Compute Shader线程规划
- 企业网络安全最常遇到的安全问题是什么,主要面临哪些威胁?
- 右键一直转圈圈(右键桌面和右键打开文件夹)的两种解决办法小总结
- 壳的机制以及脱壳技术
- 【LeetCode】517. 超级洗衣机 解题报告 (python)
- 数据科学家和人工智能职业生涯之外软件公司之外的热门市场
- 蚂蚁金服bPaaS究竟是什么?
- Windows 通过CMD指令导出的文件夹/文件的目录树
- 树莓派更改WiFi信息
- 深度解析Linux通过日志反查入侵
- 2017面向对象程序设计(Java)第十六周学习总结
- NOIP2018·赛道修建
热门文章
- C++的名字空间(很重要)
- jquery的$().each(function(i){})和和$.each(,function(i,n){})的区别和用法
- Linux笔记——软件包管理
- Java培训学习之Java开源软件的汇总
- 连不上微软服务器 dns,无法联系主 DNS 服务器 | Microsoft Docs
- Dubbo系列(二)源码分析之SPI机制
- vue所有页面刷新一次mounted(以及所有生命周期函数)执行两次的解决方法
- 用c 语言循环抓取网页,C语言获取网页源代码
- 基于 MySQL Binlog 的 Elasticsearch 数据同步实践 原
- REW声学测试(三):生成测试信号