热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

3.2.6多重表

算法是编程的灵魂,一直想研究一下算法,选了一本入门书《数据结构与算法分析--c语言描述》,空闲的时候翻一番,写一写。在3.2.6小节中有个多重表,百度了一下,可能比较简单,较少人实现--!,百度到的

算法是编程的灵魂,一直想研究一下算法,选了一本入门书《数据结构与算法分析--c语言描述》,空闲的时候翻一番,写一写。在3.2.6小节中有个多重表,百度了一下,可能比较简单,较少人实现- -!,百度到的一些博主的实现方法,定义的节点包含的信息较多且比较浪费空间,没有书上描述的那么简洁,所以自己实现了一下,建议先看下书里的内容:

情景:在一所有40000名学生和2500门课程的大学生需要生成两种类型的报告。第一个报告列出每个班的注册者,第二个报告列出每个学生注册的班级。

分析:常用的实现方法是使用二维数组。这样一个数组将有1亿项。平均大约一个学生注册三门课程,因此实际上有意义的数据只有120000项,大约占0.1%

解决办法:现在需要的是列出每个班级及每个班所包含的学生的表,也需要每个学生及其所注册的班级的表。如下图,我们把两个表合成一个表,使用多重表来实现

 

比如,为了列出C3班的所有学生,我们从C3开始通过向右行进而遍历其表。第一个单元属于学生S1。虽然不存在明显的信息,但是可以通过跟踪该生链表直达到该表表头,从而确定该生的信息。一旦找到该生信息,我们就转回到C3的表(在遍历该生的表之前,我们存储了我们在课程表中的位置)并找到可以确定属于S3的另外一个单元,我们继续并发现S4和S3也在班上。对任意一名学生,我们也可以用类似的方法确定该生注册的所有课程。

代码实现:

 
 

struct node;
typedef struct node * node_t;

//节点
struct node
{
    node_t next_student;
    node_t next_course;
};

//学生表头
struct student
{
    char name_student[20];
    node_t first_student;
};

//课程表头
struct course
{
    char name_course[20];
    node_t first_course;
};

说明:

1. 学生表头和课程表头包含对应的学生和课程信息,每个学生注册一门课程就产生一个节点,该节点即指向该学生注册的下一个节点,也指向该课程的下一个节点。

2. 如何获取学生表头/课程表头的信息,以下以打印s1学生(如下图)注册的课程为例来说明:

1).现在已知s1表头,通过表头可找到节点1

2).找到节点1,向右推进,可以找到课程的表头c1(可以通过判断next_student的参数判断节点是否为表头)

3).获取c1的信息:如上图,最后一个课程节点的next_course指向的只是课程表头c1中node_t结构体的起始位置,需要减去name_course的大小才能得到课程表头c1的起始位置(不知道为什么,name_course数组的大小应该是20字节,直接用课程最后一个节点next_course-20,得到的却不是c1的位置),在本来代码中,是利用一个struct course类型的变量,然后算出一个偏移量offset = next_student - name_course[0],然后用最后一个节点的next_course-offset就可以算出c1的位置了。

4).转回节点1,获取学生选的下一个课程的节点,然后依照上面的逻辑,继续获取该节点对应的课程信息,直到遍历完s1的链表。

 

#ifndef _MULTITABLE_H
#define _MULTITABLE_H
//struct node;
//typedef struct node node_t;
struct node;
typedef struct node * node_t;
typedef struct student * student_t;
typedef struct course *course_t;

//节点
struct node
{
    node_t next_student;
    node_t next_course;
};

//学生表头
struct student
{
    char name_student[20];
    node_t first_student;
};

//课程表头
struct course
{
    char name_course[20];
    node_t first_course;
};

#define COUNT_STUDENT 6
#define COUNT_COURSE 5
student_t g_student[COUNT_STUDENT];
course_t  g_course[COUNT_COURSE];

char g_student_name[COUNT_STUDENT][20] = {"学生1", "学生2", "学生3", "学生4", "学生5", "学生6"};
char g_course_name[COUNT_COURSE][20] = {"课程1", "课程2", "课程3", "课程4", "课程5"};


/**
 *@brief 初始化一个空的学生头
 *
 */
student_t make_empty_head_student(student_t L);

/**
 *@brief 初始化一个空的课程头
 *
 */
course_t make_empty_head_course(course_t L);

/**
 *初始化表头信息
 */
void init_linktable();

/**
 *@brief 插入学生节点
 *
 */
void insert_student_behind(node_t head, node_t inset_node);

/**
 *@brief 插入课程节点
 *
 */
void insert_course_behind(node_t head, node_t inset_node);

/**
 *@brief 注册课程
 *@param head_student:相应学生的头
 *@param head_course: 相应课程的头
 */
bool register_course(student_t head_student, course_t head_course);

/**
 *@brief 根据一个node节点查找对应的课程信息
 *
 *@param p:要查询的节点
 */
node_t find_course_head(node_t p);

/**
 *@brief 打印学生的课程信息
 *
 *@param student_t:传入学生的表头
 */
void printf_student_choose_course(student_t head_student);

student_t malloc_student_node();
#endif
#include "multitable.h"
#include 
#include 
#include <string.h>



/**
 *@brief 初始化一个空的学生头
 *
 */
student_t make_empty_head_student(student_t L)
{
    //if(L != NULL)
        //delete_link(L);
    
    L = malloc_student_node();
    if(L == NULL)
    {
        return NULL;
    }

    memset(L->name_student, 0, 20);
    L->first_student = (node_t)malloc(sizeof(struct node));
    L->first_student->next_student = L->first_student;
       L->first_student->next_course   = NULL;
    if(L->first_student == NULL)
    {
        free(L);
        return NULL;
    }

    return L;
}

/**
 *@brief 初始化一个空的课程头
 *
 */
course_t make_empty_head_course(course_t L)
{
    //if(L != NULL)
        //delete_link(L);

    L = (course_t)malloc(sizeof(struct course));
    if(L == NULL)
    {
        return NULL;
    }

    memset(L->name_course, 0, 20);
    L->first_course = (node_t)malloc(sizeof(struct node));
    L->first_course->next_student = NULL;
    L->first_course->next_course  = L->first_course;
    if(L->first_course == NULL)
    {
        free(L);
        return NULL;
    }

    return L;
}

/**
 *初始化表头信息
 */
void init_linktable()
{
    int i = 0;
    for(i = 0; i i)
    {
        g_student[i] = make_empty_head_student(g_student[i]); 
        strcpy(g_student[i]->name_student, g_student_name[i]);
    }

    for(i = 0; i i)
    {
        g_course[i] = make_empty_head_course(g_course[i]);
        strcpy(g_course[i]->name_course, g_course_name[i]);
    }

}

/**
 *@brief 插入学生节点
 *
 */
void insert_student_behind(node_t head, node_t insert_node)
{
    if(head == NULL || insert_node == NULL)
    {
        return;
    }

    node_t p = head;
    
    while(p->next_student != head)
    {
        p = p->next_student;
    }

    insert_node = p->next_student;
    p->next_student = insert_node;    
}

/**
 *@brief 插入课程节点
 *
 */
void insert_course_behind(node_t head, node_t insert_node)
{
    if(head == NULL || insert_node == NULL)
    {
        return;
    }

    node_t p = head;
    
    while(p->next_course != head)
    {
        p = p->next_course;
    }

    insert_node = p->next_course;
    p->next_course = insert_node;    
}

/**
 *@brief 注册课程
 *@param head_student:相应学生的头
 *@param head_course: 相应课程的头
 */
bool register_course(student_t head_student, course_t head_course)
{
    //if(head_student == NULL || head_course == NULL)
    //    return false;
    node_t temp_node = (node_t)malloc(sizeof(struct node));
    if(temp_node == NULL)
    {
        return false;
    }

    node_t p = head_student->first_student;
    while(p->next_student != head_student->first_student)
    {
        p = p->next_student;
    }
    
    temp_node->next_student = p->next_student;
    p->next_student = temp_node;
    p = head_course->first_course;
    while(p->next_course != head_course->first_course)
    {
        p = p->next_course;
    }
    temp_node->next_course = p->next_course;
    p->next_course = temp_node;

    return true;
}

/**
 *@brief 根据一个node节点查找对应的头节点的课程信息
 *
 *@param p:要查询的节点
 */
node_t find_course_head(node_t p)
{
    if(p == NULL)
    {
        return NULL;
    }

    while(p->next_student != NULL)
    {
        p = p->next_course;
    }

    return p;
}

/**
 *@brief 打印学生的课程信息
 *
 *@param student_t:传入学生的表头
 */
void printf_student_choose_course(student_t head_student)
{
    if(head_student == NULL)
    {
        return;
    }

    node_t p = head_student->first_student;
    node_t temp_node = NULL;

    course_t temp_course_head = NULL;
    while(p->next_student != head_student->first_student)
    {
        temp_node = find_course_head(p->next_student);
        //找到课程链表头,并计算头节点的开始位置,才能取到头节点的信息
        long offest = (long)(g_course[0]->first_course) - (long)(g_course[0]->name_course);
        temp_course_head = (course_t)(long(temp_node) - offest);
        printf("###########course name:%s\n",temp_course_head->name_course);
        p = p->next_student;
    }
}



student_t malloc_student_node()
{
    student_t p = (student_t)malloc(sizeof(struct student));
    return p;
}


int main(int argc, char *argv[])
{

    init_linktable();
    register_course(g_student[0], g_course[0] );
    register_course(g_student[0], g_course[2] );
    register_course(g_student[0], g_course[3] );
    
    register_course(g_student[1], g_course[0] );
    register_course(g_student[1], g_course[1] );
    register_course(g_student[1], g_course[1] );
    register_course(g_student[1], g_course[2] );
    
    register_course(g_student[2], g_course[0] );
    register_course(g_student[2], g_course[1] );
    register_course(g_student[2], g_course[2] );
    
    printf_student_choose_course(g_student[0]);
    printf_student_choose_course(g_student[1]);
    printf_student_choose_course(g_student[2]);
    return 0;
}

 以上只是实现了学生注册课程表及打印学生所选课程的代码,未实现打印课程包含所包含的学生的函数。

效率分析:

使用循环链标节省空间但是要花费时间,在最坏的情况下,如果第一个学生注册了每一门课程,那么表中的每一项都要检测以确定该生的所有课程名(即遍历每个课程表,以查找表头的课程信息)。因为在本例中每个学生注册了每一门课程相对很少并且每门课程的注册学生也很少,最坏的情况是不可能发生的。

如果怀疑会产生问题,那么每一个(非表头)单元的节点就要有直接指向学生头和班的表头的指针(可改写结构体如下所示)。这将使空间的需求加倍,但是却简化和加速实现的过程。

 

//节点
struct node
{
    struct student  *student_head;
    struct course   *course_head;
    node_t next_student;
    node_t next_course;
};

//学生表头
struct student
{
    char name_student[20];
    node_t first_student;
};

//课程表头
struct course
{
    char name_course[20];
    node_t first_course;
};

 


推荐阅读
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • C语言注释工具及快捷键,删除C语言注释工具的实现思路
    本文介绍了C语言中注释的两种方式以及注释的作用,提供了删除C语言注释的工具实现思路,并分享了C语言中注释的快捷键操作方法。 ... [详细]
  • c语言\n不换行,c语言printf不换行
    本文目录一览:1、C语言不换行输入2、c语言的 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • C语言判断正整数能否被整除的程序
    本文介绍了使用C语言编写的判断正整数能否被整除的程序,包括输入一个三位正整数,判断是否能被3整除且至少包含数字3的方法。同时还介绍了使用qsort函数进行快速排序的算法。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • C++中的三角函数计算及其应用
    本文介绍了C++中的三角函数的计算方法和应用,包括计算余弦、正弦、正切值以及反三角函数求对应的弧度制角度的示例代码。代码中使用了C++的数学库和命名空间,通过赋值和输出语句实现了三角函数的计算和结果显示。通过学习本文,读者可以了解到C++中三角函数的基本用法和应用场景。 ... [详细]
  • C语言常量与变量的深入理解及其影响
    本文深入讲解了C语言中常量与变量的概念及其深入实质,强调了对常量和变量的理解对于学习指针等后续内容的重要性。详细介绍了常量的分类和特点,以及变量的定义和分类。同时指出了常量和变量在程序中的作用及其对内存空间的影响,类似于const关键字的只读属性。此外,还提及了常量和变量在实际应用中可能出现的问题,如段错误和野指针。 ... [详细]
  • 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
    本文旨在全面介绍Windows内存管理机制及C++内存分配实例中的内存映射文件。通过对内存映射文件的使用场合和与虚拟内存的区别进行解析,帮助读者更好地理解操作系统的内存管理机制。同时,本文还提供了相关章节的链接,方便读者深入学习Windows内存管理及C++内存分配实例的其他内容。 ... [详细]
author-avatar
静净精时
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有