采用线性表求解笛卡尔积

文章描述了如何使用单链表存储具有不确定行数和列数的表格数据,包括创建表、销毁表、显示表以及执行两个表之间的简单自然连接的算法。创建表采用尾插法,销毁表时区分头结点和数据节点,显示表按行输出所有数据,连接运算则遍历两个表并比较指定列的值来构建连接结果。
摘要由CSDN通过智能技术生成

1.题目:假设有两个表A和B,分别是m1行、n1列和m2行、n2列。它们的简单自然连接结果C=A▷◁B,其中i表示表A中的列号,j表示表B中的列号,C为A和B的笛卡尔积中满足指定连接条件的所有记录组,该连接条件为表A的第i列与表B的第j列相等。

2.数据组织:

由于每个表的行数不确定,所以采用单链表作为表的存储结构,每行作为一个数据节点,也称为行节点。另外,每行中元素的个数也是不确定的,但由于需要提供随机查找行中的数据元素,所以每行的数据元素采用顺序存储结构,这里用长度为MaxCol的数组data存储每行的数据。因此该单链表中数据节点的类型声明如下:

#define MaxCol 10         //最大列数
typedef struct Node1      //定义数据节点类型
{
   ElemType data[MaxCol]; //存放一行的数据
   struct Node1 *next;    //指向后继数据节点
}DList;                   //行节点类型

  另外,需要指定每个表的行数和列数,为此将单链表的头结点类型声明如下:

typedef struct Node2
{
   int Row,Col;       //行数和列数
   DList *next;       //指向第一个数据节点
}HList;               //头结点类型

注意:在这里,头结点和数据节点两者的类型是不同的。

3.设计运算算法:

通过对本求解问题进行分析,发现需要设计以下4个基本运算算法。

·CreateTable(&h):采用交互方式建立单链表h。

·DestroyTable(&h):销毁单链表h。

·DispTable(h):输出单链表h。

·LinkTable(h1,h2,&h):由h1和h2连接产生结果单链表h。

1)采用交互方式建立单链表的算法

   采用尾插法建表的方法创建存储一个表的单链表,用户先输入表的行数和列数,然后输入各行的数据。在采用尾插法建表时需要设置一个尾节点指针r,一般尾插法是先让r指向头结点,但这里头结点和数据节点的类型不同,且头结点只要一个,而数据节点有若干个,所以只让r指向数据节点。对应的建表算法如下:

void CreateTable(HList *&h)
{
   int i,j;
   DList *r,*s;
   h=(HList *)malloc(sizeof(HList));    //创建头结点
   printf("表的行数,列数:");
   scanf("%d%d",&h->Row,&h->Col);       //输入表的行数和列数
   for(i=0;i<h->Row;i++)                //输入所有行的数据
   {
      printf("第%d行:",i+1);
      s=(DList *)malloc(sizeof(DList)); //创建数据节点s
      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域置空
}

    显然该算法的时间复杂度为O(m×n),其中m为表的行数,n为表的列数

2)销毁单链表的算法

      该算法和前面销毁单链表的算法类似,只是要针对头结点和数据节点类型不相同的情况进行相应修改。对应的算法如下:

void DestroyTable(HList *&h)
{
   DList *pre=h->next,*p=pre->next;    //将pre指向第一个数据节点,p指向第二个数据节点
   while(p!=NULL)                      //while循环条件为第二个数据节点非空
   {
       free(pre);                      //释放第一个数据节点
       pre=p;                          //将指针p,pre分别往后移一个数据节点
       p=p->next;
   } 
   free(pre);                          //不论是否执行循环while,均释放pre指向的数据节点
   free(h);                            //释放头结点
}

      分为两种情况:

①若只有一个行节点,则直接销毁行节点和头结点

②若有多个行节点,则先销毁第一个行节点,再将p和pre指针均向后移一个单位,不断销毁pre指向的节点,将所有行节点销毁后,再销毁头结点。

    该算法的时间复杂度为O(m),其中m为表的行数。

3)输出单链表的算法

   对应的输出单链表的算法如下:

void DispTable(HList *h)
{
    int j;
    DList *p=h->next;                 //p指向开始行节点
    while(p!=NULL)                    //遍历所有行
    {
        for(j=0;j<h->Col;j++)         //输出一行的数据
          printf("%4d",p->data[j]);
       printf("\n");
       p=p->next;                     //p指向下一个行节点
    }
}

    该算法的时间复杂度为O(m×n),其中m为表的行数,n为表的列数。

4)表连接运算算法

     为了实现两个表h1和h2的简单自然连接,先要输入两个表连接的列序号i和j,然后用p指针遍历单链表h1,对于h1的每个数据节点,都用q指针从头至尾遍历单链表h2的所有数据节点,若自然连接条件成立,即h1的p所指节点和h2的q所指节点满足连接条件p->data[i-1]=q->data[j-1],则建立一个连接节点s并添加到结果单链表h中。结果单链表h也是采用尾插法建表方法创建的。实现两个表h1和h2的简单自然连接并生成结果单链表h的算法如下:

void LinkTable(HList *h1,HList *h2,HList *&h)
{
    int i,j,k;
    DList *p=h->next,*q,*s,*r;
    printf("连接字段是:第1个表序号,第2个表序号:");
    scanf("%d%d",&i,&j);
    h=(HList *)malloc(sizeof(HList));              //创建结果表头结点
    h->Row=0;                                      //置行数为0
    h->Col=h1->Col+h2->Col;                        //置列数为表1和表2的列数和
    h->next=NULL;                                  //置next域为NULL
    while(p!=NULL)                                 //遍历表1
    {
        q=h2->next;                                //q指向表2的首节点
        while(q!=NULL)                             //遍历表2
        {
         if(p->data[i-1]==q->data[j-1])            //对应字段值相等
         {
            s=(DList *)malloc(sizeof(DList));      //创建一个数据节点s
            for(k=0;k<h1->Col;k++)                 //复制表1的当前行
             s->data[k]=p->data[k];
            for(k=0;k<h2->Col;k++)                 //复制表2的当前行
             s->data[h1->Col+k]=q->data[k];
            if(h->next==NULL)    
                 h->next=s;        //若插入的是第一个数据节点,将s节点插入头结点之后
            else r->next=s;        //将s节点插入节点r之后
            r=s;                   //r始终指向尾节点
            h->Row++;              //表的行数增1
          }
          q=q->next;               //表2后移一个节点
         }
        p=p->next;                 //表1后移一个节点
    }
    r->next=NULL;                                  //表尾节点的next
}

4.设计求解程序

     在设计好4个基本运算算法以后,设计以下主函数调用这些算法完成求解任务:

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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风雪心

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值