西北电专大二电院_数据结构上机报告记录_第一次上机报告

发布时间 2023-10-28 11:52:58作者: WCMS_XDU

数据结构是最近纳入电院的必修主课,但是其期末考核是笔试形式(,日常有上机安排。

这门课还是需要一定的课后上机练习和调试来增加对其的认识程度、发现自己欠缺的知识、可能犯下的错误,包括但不限于语法等

这里主要收录几次上机安排的报告和自己的答案,作为记录

 

第一次上机

问题一:顺序表的合并

1.问题描述:

线性表的基本操作:p20算法2.2,已知线性表LA和LB中的数据元素按值非递减有序排列,现要求将LA和LB归并为一个新的线性表LC,且LC中的数据元素仍按值非递减有序排列。

例如:设LA=(3,5,8,11),LB=(2,6,8,9,11,15,20),则LC=(2,3,5,6,8,8,9,11,15,20)

2.数据结构设计

考虑用顺序表或者数组来实现
3.算法设计

首先初始化三个空表,利用Insert函数往表中输入元素,for循环存到a,b表中,利用标识数字-1结束输入,注意到表长不能超过Maxsize;然后封装一个Funtion函数来处理比较和合并的问题。

由于题目要求非递减顺序有序排列,那么在循环中逐个将A,B表元素比对,将符合条件的也就是小或者相等的存入到C表中,最后用一个循环输出C表即可
4.源代码

  1 /*
  2   1.问题描述:
  3   已知线性表LA和LB中的数据元素按值非递减有序排列,
  4   现要求将LA和LB归并为一个新的线性表LC,
  5   且LC中的数据元素仍按值非递减有序排列。
  6   例如:设 
  7   LA=(3,5,8,11),LB=(2,6,8,9,11,15,20),则LC=(2,3,5,6,8,8,9,11,15,20)
  8   
  9   2.数据结构和算法设计
 10   这里分析用线性表存储数据并进行合并,考虑静态数组?把处理放在函数funtion里;
 11   首先建立顺序表的存储结构,建立空表C来存放数据,求出a,b表的长度,并新定义表C的长度;
 12   在循环里比较表A,B的元素大小来决定是否存入C,同时计数器自增,计数器判定依据是两表表长;
 13   如果其中一表的长度大于另一表,显然需要处理剩余项,也就是计数器继续自增并存入数据到表C
 14   直到计数结束;
 15   最后把表打印出来即可
 16  */
 17 #include<stdio.h>
 18 #include<stdlib.h>
 19 #define Maxsize 100
 20 
 21 typedef int Elemtype;
 22 typedef struct
 23 {
 24     Elemtype data[Maxsize];
 25     int Length;
 26     
 27 }Orderlist;//建立存储结构
 28 
 29 void InitList(Orderlist **L);
 30 _Bool Insert(Orderlist *L,Elemtype data,int i);
 31 int Get_Length(Orderlist *L);
 32 void Funtion(Orderlist *LA,int a,Orderlist *LB,int b,Orderlist *LC);
 33 void Print(Orderlist *L,int sum_length);//表是指针的集合,可以看到传参都是*L
 34 
 35 int main()
 36 {
 37     //怎么建立一个动态输入的顺序表?
 38     Orderlist *La,*Lb,*Lc;
 39     InitList(&La);
 40     InitList(&Lb);
 41     InitList(&Lc);//初始化三个表
 42     
 43     int data;
 44     
 45     for(int i=1;i<=Maxsize;i++)
 46     {
 47         scanf("%d",&data);
 48         if(data==-1)
 49             break;
 50         Insert(La,data,i);
 51     }
 52     for(int j=1;j<=Maxsize;j++)
 53     {
 54         scanf("%d",&data);
 55         if(data==-1)
 56             break;
 57         Insert(Lb,data,j);
 58     }//分别输入两个表的内容,用-1来作为结束判定的标识符,Insert函数逐个插入
 59     
 60     int length_a=Get_Length(La);
 61     int length_b=Get_Length(Lb);
 62     int length_c=length_a+length_b;//获取表长,输出和后面处理用
 63      
 64     Funtion(La,length_a,Lb,length_b,Lc);
 65     
 66     Print(Lc,length_c);
 67     
 68     return 0;
 69     
 70 }
 71 
 72 void InitList(Orderlist **L)//二级指针法建立空表
 73 {
 74     *L=(Orderlist*)malloc(sizeof(Orderlist));
 75     (*L)->Length=0;
 76 }
 77 
 78 int Get_Length(Orderlist *L)//获取表长,直接返回L->length
 79 {
 80     return L->Length;
 81 }
 82 
 83 _Bool Insert(Orderlist *L,Elemtype data,int i)//插入的函数
 84 {
 85     if(i<-1||i>L->Length+1||L->Length>=Maxsize)//判断i异常或者长度异常
 86         return 0;
 87     for(int j=L->Length;j>=i;j--)
 88     {
 89         L->data[j]=L->data[j-1];
 90     }
 91     L->data[i-1]=data;
 92     L->Length++;
 93     return 1;
 94 }
 95 
 96 void Print(Orderlist *L,int sum_length)//输出
 97 {
 98     
 99     printf("------------------------\n");
100     for(int i=1;i<=sum_length;i++)
101     {
102         printf("%d\t",L->data[i-1]);
103     }
104 }
105 void Funtion(Orderlist *LA,int a,Orderlist *LB,int b,Orderlist *LC)
106 {
107     int i=0,j=0,k=0;
108     while(i<a&&j<b)
109     {
110         if((LA->data[i])<(LB->data[j]))//逐个比对,小的存入C表并自增
111         {
112             LC->data[k]=LA->data[i];
113             i++;
114         }
115         else
116         {
117             LC->data[k]=LB->data[j];
118             j++;
119         }
120         k++;
121     }
122     
123     while(i<a)//处理剩余的,有的表比较长
124     {
125         LC->data[k]=LA->data[i];
126         i++;
127         k++;
128     }
129     
130     while(j<b)
131     {
132         LC->data[k]=LB->data[j];
133         j++;
134         k++;
135     }
136 }

 

 

问题二:链表

1.问题描述:约瑟夫环。

编号为1,2,...,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。

现在给定一个随机数m>0,从编号为1的人开始,按顺时针方向1开始顺序报数,报到m时停止。

报m的人出圈,从他在顺时针方向上的下一个人开始,重新从1开始报数,如此下去,直至所有的人全部出列为止。

请编写程序求出圈的顺序。

上机实现提示:

① 编写建立循环链表算法

②编写结点输出算法(带输入参数k)

③编写主程序

要求,把程序看懂,然后进行调试,得出正确的结果。

1,2,3,4,5,6,7。从第1个人开始报数,报到3的人出列,出列顺序为3,6,2,7,5,1,4

2.数据结构分析

如题,用循环链表实现这个环

3.算法分析

 

考虑新建一个循环链表来实现这个环,问题一是建立循环链表和存数据,问题二是怎么样实现约瑟夫的求解
解决约瑟夫,设一共n人,报数m的时候第m个出列,然后重新报数,同时用flag来标记出列的人数,显然当flag=n-1时完成
4.源代码
/*
  约瑟夫环。
  编号为1,2,...,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。
  现在给定一个随机数m>0,从编号为1的人开始,按顺时针方向1开始顺序报数,报到m时停止。
  报m的人出圈,从他在顺时针方向上的下一个人开始,重新从1开始报数,
  如此下去,直至所有的人全部出列为止。请编写程序求出圈的顺序。
  1,2,3,4,5,6,7。从第1个人开始报数,报到3的人出列,出列顺序为3,6,2,7,5,1,4
  
  考虑新建一个循环链表来实现这个环,问题一是建立循环链表和存数据,问题二是怎么样实现约瑟夫的求解
  解决约瑟夫,设一共n人,报数m的时候第m个出列,然后重新报数,同时用flag来标记出列的人数,显然当flag=n-1时完成
 */
#include<stdio.h>
#include<stdlib.h>

typedef struct node
{
    int data;
    struct node *next;
}Lnode;//建立链表的存储结构

Lnode*Tail_Creat(int n);
void YuesefuHuan(Lnode *L,int n,int m,int a[]);//声明调用的两个函数,一个尾插法一个处理约瑟夫环

int main()
{
    int n,m;
    scanf("%d\t%d",&n,&m);//输入总人数n和报数m
    Lnode*tail=NULL;//声明一个尾指针指向空,并在下面用尾插法返回值赋值
    tail=Tail_Creat(n);
    int a[n];//新建一个输出数组
    YuesefuHuan(tail,n,m,a);
    for(int i=0;i<n;i++)
    {
        printf("%d\t",a[i]);
    }
    return 0;
}


Lnode*Tail_Creat(int n)//尾插法建立新循环链表,这里假定了链表必定不为空表
{
    Lnode *head,*new_elem,*tail;
    head=NULL,new_elem=NULL,tail=NULL;//建立头指针,存放数据的指针和尾指针且初始指向空
    
    int i;
    for(i=1;i<=n;i++)//遍历,从1开始到n,并把i依次存入到链表中
    {
        new_elem=(Lnode*)malloc(sizeof(Lnode));//开辟空间
        new_elem->data=i;
        if(head==NULL)//考虑到每个结点都存数据,这里是不带头结点的尾插法,head是头指针指向第一个元素。如果带头结点呢?
        {
            head=new_elem;//初始状态,头指针存入第一个元素
        }
        else
        {
            tail->next=new_elem;//如果不是初始状态,用尾插法,将尾指针的后继指向新元素
        }
        tail=new_elem;//无论是第一个还是后续,都将尾指针指向更新后最后一个元素
    }
    tail->next=head;//构建循环链表,将尾指针的后继指向头指针
    return tail;//返回尾指针,考虑到约瑟夫实现里面循环链表不是双向,返回tail相当于返回i-1位指针
    
}

void YuesefuHuan(Lnode *L,int n,int m,int a[])//传入的是指针
{
    int cnt=0,flag=0,i=0;//cnt计数用判断是否到m,flag计数出列人数,到n-1时只剩下最后一个,结束
    Lnode*s=L,*p=L->next;//初始L传入的是尾指针,s指向一个结点,p指向s的下一个结点,实际上要出列看的是p,初始cnt从0自增1对照的是p的序号1
    while(flag!=n-1)
    {
        cnt++;//计数器从0自增,序号和p相同;
        if(cnt!=m)//如果还没到要出列的位置,后移两个指针,注意先s=p避免丢失
        {
            s=p;
            p=p->next;
        }
        else if(cnt==m)//如果到了要出列的位置(画图看比较容易看出)
        {
            cnt=0;
            flag++;//重新开始计数,出列人数自增1
            a[i++]=p->data;//存数据到要输出的数组中
            s->next=p->next;
            free(p);
            p=s->next;//注意这里s还是在上一个位置,只是把p(每次遍历判断是否要出列的结点)移向出列结点的后一个结点,所以不是写s=p->next
            
        }
    }
    a[i]=p->data;//传入最后一个数据
    free(p);
    p=NULL;
}

这里补充一个要求高一点的约瑟夫环的实现,其题目要求做了一些改动:

时间限制S    内存限制10000 Kb

问题描述

习题集P79。编号为1,2,...,nn个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。现在给定一个随机数m>0,从编号为1的人开始,按顺时针方向1开始顺序报数,报到m时停止。报m的人出圈,同时留下他的密码作为新的m值,从他在顺时针方向上的下一个人开始,重新从1开始报数,如此下去,直至所有的人全部出圈为止。

问题输入

输入数据第一行为两个正整数nm,分别表示人的个数及初始随机数,每组数据的第二行为n个整数,表示每个人持有的密码。

问题输出

用一行输出n个整数表示依次出圈人的编号,整数之间用空格分隔

输入样例

7 20

3 1 7 2 4 8 4

输出样例

6 1 4 7 2 3 5

提示

使用不带头节点的循环链表

 这里多了一个m值作为下一次循环用,那么新建一个数组来存

对约瑟夫环处理函数里面的elseif分支改动一下即可

给出改动后的源代码,对应xdoj_264题

  1 /*
  2   约瑟夫环  
  3   时间限制
  4   2 S 
  5   内存限制
  6   10000 Kb 
  7   问题描述 
  8   编号为1,2,...,n的n个人按顺时针方向围坐一圈,每人持有一个密码(正整数)。
  9   现在给定一个随机数m>0,从编号为1的人开始,按顺时针方向1开始顺序报数,报到m时停止。
 10   报m的人出圈,同时留下他的密码作为新的m值,从他在顺时针方向上的下一个人开始,重新从1开始报数,
 11   如此下去,直至所有的人全部出圈为止。 
 12   问题输入 
 13   输入数据第一行为两个正整数n和m,分别表示人的个数及初始随机数,每组数据的第二行为n个整数,表示每个人持有的密码。 
 14   问题输出 
 15   用一行输出n个整数表示依次出圈人的编号,整数之间用空格分隔 
 16   输入样例 
 17   7 20
 18   3 1 7 2 4 8 4
 19   输出样例 
 20   6 1 4 7 2 3 5 
 21   提示 
 22   使用不带头节点的循环链表 
 23   
 24   考虑新建一个循环链表来实现这个环,问题一是建立循环链表和存数据,问题二是怎么样实现约瑟夫的求解
 25   解决约瑟夫,设一共n人,怎么出列?报数m的时候第m个出列,然后重新报数,同时用flag来标记出列的人数,怎么结束?显然当flag=n-1时完成
 26   这个问题输出的还是编号,只是m值作为变量会发生变化,每个人随身带有一个m值需要建立数组额外存取(考虑到结构体的思想,也写在链表的结构体里!另写一个c代码)
 27  */
 28 #include<stdio.h>
 29 #include<stdlib.h>
 30 
 31 typedef struct node
 32 {
 33     int data;
 34     struct node *next;
 35 }Lnode;//建立链表的存储结构
 36 
 37 Lnode*Tail_Creat(int n);
 38 void YuesefuHuan(Lnode *L,int n,int m,int a[],int b[]);//声明调用的两个函数,一个尾插法一个处理约瑟夫环
 39 
 40 int main()
 41 {
 42     /*
 43       流程描述
 44       输入 总人数和报数的值m
 45       建立两个新数组,一个存m值,一个输出编号
 46       for循环输入m值到一个数组
 47       
 48       尾插法建立链表
 49       函数处理约瑟夫环问题
 50       
 51       for循环输出另一个数组(显然上一个函数已经把数据存到了存编号的数组中)
 52      */
 53     int n,m,m_data,i;
 54     scanf("%d\t%d",&n,&m);//输入总人数n和报数m
 55     int a[n];//新建一个输出数组
 56     int b[n];//新建一个存储数组存储每个人的m值
 57     
 58     for(i=0;i<n;i++)
 59     {
 60         scanf("%d",&m_data);
 61         b[i]=m_data;
 62     }
 63     
 64     Lnode*tail=NULL;//声明一个尾指针指向空,并在下面用尾插法返回值赋值
 65     tail=Tail_Creat(n);
 66     YuesefuHuan(tail,n,m,a,b);
 67     for(i=0;i<n;i++)
 68     {
 69         printf("%d\t",a[i]);
 70     }
 71     return 0;
 72 }
 73 
 74 
 75 Lnode*Tail_Creat(int n)//尾插法建立新循环链表,这里假定了链表必定不为空表,同时不带头结点
 76 {
 77     Lnode *head,*new_elem,*tail;
 78     head=NULL,new_elem=NULL,tail=NULL;//建立头指针,存放数据的指针和尾指针且初始指向空
 79     
 80     int i;
 81     for(i=1;i<=n;i++)//遍历,从1开始到n,并把编号i依次存入到链表中
 82     {
 83         new_elem=(Lnode*)malloc(sizeof(Lnode));//开辟空间
 84         new_elem->data=i;
 85         if(head==NULL)//考虑到每个结点都存数据,这里是不带头结点的尾插法,head是头指针指向第一个元素。如果带头结点呢?
 86         {
 87             head=new_elem;//初始状态,头指针存入第一个元素
 88         }
 89         else
 90         {
 91             tail->next=new_elem;//如果不是初始状态,用尾插法,将尾指针的后继指向新元素
 92         }
 93         tail=new_elem;//无论是第一个还是后续,都将尾指针指向更新后最后一个元素
 94     }
 95     tail->next=head;//构建循环链表,将尾指针的后继指向头指针
 96     return tail;//返回尾指针,考虑到约瑟夫实现里面循环链表不是双向,返回tail相当于返回i-1位指针
 97     
 98 }
 99 
100 void YuesefuHuan(Lnode *L,int n,int m,int a[],int b[])
101 {
102     int cnt=0,flag=0,i=0;//cnt计数用判断是否到m,flag计数出列人数,到n-1时只剩下最后一个,结束
103     Lnode*s=L,*p=L->next;//初始L传入的是尾指针,s指向一个结点,p指向s的下一个结点,实际上要出列看的是p,初始cnt从0自增1对照的是p的序号1
104     while(flag!=n-1)
105     {
106         cnt++;//计数器从0自增,序号和p相同;
107         if(cnt!=m)//如果还没到要出列的位置,后移两个指针,注意先s=p避免丢失
108         {
109             s=p;
110             p=p->next;
111         }
112         else if(cnt==m)//如果到了要出列的位置(画图看比较容易看出)
113         {
114             cnt=0;
115             flag++;//重新开始计数,出列人数自增1
116             m=b[p->data-1];//把b数组存的m值更新到函数里即可,注意到数组的位序要比表的位序少1
117             a[i++]=p->data;//存数据到要输出的数组中
118             s->next=p->next;//避免断开
119             free(p);
120             p=s->next;//注意这里s还是在上一个位置,只是把p(每次遍历判断是否要出列的结点)移向出列结点的后一个结点,所以不是写s=p->next
121         
122         }
123     }
124     a[i]=p->data;//传入最后一个数据(这种细节不要忽视)
125     free(p);
126     p=NULL;
127 }