《数据结构》实验报告(链表进阶):约瑟夫回环 链表去重

目录

一、约瑟夫回环问题(循环链表 解法)

 !注  !

二、链表去重


一、约瑟夫回环问题(循环链表 解法)

        n个数据元素构成一个环,从环中任意位置开始计数,计数到m将该元素从表中取出,重复上述过程,直至表中只剩下一个元素。

        提示:用一个无头结点循环单链表来实现n个元素的存储。

算法代码:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#define ERROR 0
#define OK 1

typedef  int ElemType;   /*定义表元素的类型*/
typedef struct LNode{    /*线性表的单链表存储*/
    ElemType data;
    struct LNode *next;
}LNode,*LinkList;

LinkList ListCreate(int n);   /*创建元素个数为n的循环链表(不带头结点)*/
void PrintList(LinkList L,int n);   /*打印链表中的n个数据*/
int JosephRing(LinkList L,int n,int m);   /*约瑟夫环循环取出操作*/

LinkList ListCreate(int n){
    ElemType e;
    int i;
    LNode *L;
    LNode *head;   //先分配头节点方便插入数据,最后释放
    head=(LinkList)malloc(sizeof(LNode));
    head->next = head;
    LNode *p = head;   //指针p指向链表中“最后”一个结点
    LNode *r;
    for(i=1;i<n+1;i++){
        r=(LinkList)malloc(sizeof(LNode));   /*为新结点分配空间*/
        printf("输入要插入的元素%d:",i);
        scanf("%d",&e);
        r->data=e;
        r->next=head;
        p->next=r;
        p = r;
    }
    L = head->next;   //L指向链表中第一个结点
    p->next = L;
    free(head);
    return L;
}/*ListCreate*/

void PrintList(LinkList L,int n){
    LNode *p;
    p = L;
    int i;
    for(i=0;i<n;i++){
        printf("%5d  ",p->data);
        p=p->next;
    }
}/*PrintList*/

/*约瑟夫回环*/
int JosephRing(LinkList L,int n,int m){   /*n个数据元素构成环,计数到m取出*/
    LNode *p;
    LNode *q;
    p = L;
    int i,j;
    for(i=1;i<n;i++)  //外层循环执行一次删除一个结点
	{
	    if(1 == m){
            q = p;   //q指向删除结点
            p = q->next;
            printf("\n第%d个删除的元素是:%d",i,q->data);
            free(q);
        }
        else{
            for(j=2;j<=m-1;j++)
	        p=p->next;   //循环退出p指向第m-1个结点
            q=p->next;
	        p->next=q->next;  //删除第m个结点
	        printf("\n第%d个删除的元素是:%d",i,q->data);
	        free(q);
            p = p->next;  //从下一个元素开始重新计数
        }
    }
    return (p->data);  //返回表中最后一个数据
}/*JosephRing*/

int main()
{
	int n,m;
	printf("1-请输入链表元素个数n(n>0):");
	scanf("%d",&n);
    LinkList L = NULL;
	L = ListCreate(n);  //创建含n个元素的循环链表
	printf("\n2-打印表中数据:");
	PrintList(L,n);
	printf("\n\n3-请输入计数m:");
	scanf("%d",&m);
	printf("\n\n4-表中最后一个元素为:%d",JosephRing(L,n,m));
    return 0;
}

运行结果:

 !注  !

       有的代码对计数m使用了m%=n取余操作,但该操作只适用于第一个元素的删除,会导致接下来删除的元素不正确。

具体实例分析:

数据元素个数 n = 5  计数 m = 6 

①直接使用原始m值计数:

第一个删除的数据应该是 1

第二个删除的数据应该是 3

第三个删除的数据应该是 2

 第四个删除的数据应该是 5 (最终剩下的数据为 4 )

②使用 m = m%n 计数:

m = 6 % 5 = 1

第一个删除的数据应该是 1

 

第二个删除的数据应该是 2

以此类推,最后剩下的数据是 5

 

 m%n 取余操作避免了m>n情况下一定会先完整遍历一遍表中所有数据的过程。

1、按照 m = m % n 计数操作下第一个删除的元素和按原始m计数操作下第一个删除的元素是相同的。

2、但删除一个元素后表中数据个数 - 1,接下来继续使用该m计数删除的元素和使用原始m计数删除的数据就不一定相同了( m > n 情况下)。

二、链表去重

        设一带头结点的单链表,设计算法将表中值相同的元素仅保留一个结点。

        提示:指针p从链表的第一个元素开始,利用指针q从指针p位置开始向后搜索整个链表,删除与之值相同的元素;指针p继续指向下一个元素,开始下一轮的删除,直至p==null为至,既完成了对整个链表元素的删除相同值。

算法代码:

#include<stdio.h>
#include<malloc.h>
#define ERROR 0
#define OK 1

typedef  int ElemType;   /*定义表元素的类型*/
typedef struct LNode{    /*线性表的单链表存储*/
    ElemType data;
    struct LNode *next;
}LNode,*LinkList;

LinkList CreateList(int n);   /*创建一个长度为n的链表*/
void PrintList(LinkList L);   /*输出带头结点单链表的所有元素*/
void distinct(LinkList L);    /*链表去重*/

LinkList CreateList(int n){
    LNode *p,*q,*head;
    int i;
    head=(LinkList)malloc(sizeof(LNode));
    head->next=NULL;
    p=head;
    for(i=0;i<n;i++){
       q=(LinkList)malloc(sizeof(LNode));
       printf("请输入数据 %i:",i+1);
       scanf("%d",&q->data);            /*输入元素值*/
       q->next=NULL;                    /*结点指针域置空*/
       p->next=q;                       /*新结点连在表末尾*/
       p=q;                             /*p始终指向链表中最后一个结点*/
    }
    return head;  //head-指向头结点
}/*CreateList*/

void PrintList(LinkList L){
    LNode *p;
    p=L->next;   /*p指向单链表的第1个元素*/
    while(p!=NULL){
        printf("%5d",p->data);
        p=p->next;
    }
}/*PrintList*/

void distinct(LinkList L){
    LNode *p;
    LNode *q;
    LNode *t;
    p = L->next;   /*p指向单链表的第1个元素*/
    while(p){
        t = p;   //t始终指向q前一个元素
        q = p->next;   /*q指向p后一个元素*/
        while(q){
            if(q->data == p->data){
                t->next = q->next;
                q = q->next;
            }
            else{
                t = q;
                q = q->next;   //q向后搜索整个链表
            }
        }
        p = p->next;   //p继续指向下一元素,开始下一轮删除
    }
}/*distinct*/


int main(){
    int n;
    LinkList L=NULL;            /*定义指向单链表的指针*/
    printf("1-请输入链表元素个数n:");   /*输入单链表的元素个数*/
    scanf("%d",&n);
    if(n>0){
        printf("\n");
        L=CreateList(n);
        printf("\n2-打印链表数据:");
        PrintList(L);
        distinct(L);   //链表去重
        printf("\n\n3-去重后的链表:");
        PrintList(L);
    }else
        printf("ERROR");
    return 0;
}

运行结果:

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 好的,我来回复。7-1 数据结构实验之链表一:顺序建立链表是一道链表实验题目,要求学生使用顺序存储的方式建立链表,并实现链表的基本操作,如插入、删除、查找等。这道题目可以帮助学生深入理解链表的原理和实现方式,提高其对数据结构的理解和应用能力。 ### 回答2: 链表是一种常见的数据结构,它是由一系列结点组成的线性结构。结点包括两部分内容,一部分是存储数据的元素,另一部分是指向下一个结点的指针。链表的优点在于可以动态地增加、删除结点,因此它常用于需要频繁增删数据的场合。 本次实验是链表的第一部分,即顺序建立链表。顺序建立链表指的是从头到尾依次输入结点,将其链接在一起形成链表。具体实现方法如下: 1. 定义一个结点结构体,包含存储数据的元素和指向下一个结点的指针。 2. 在主函数中定义一个头结点,头结点的数据元素可以不存储有效数据,仅用于方便遍历链表。 3. 通过循环输入数据,并将数据存储在新结点中。 4. 将新结点的指针指向头结点指向的下一个结点,再将头结点的指针指向新结点,即完成了新结点的插入。 5. 继续输入下一个数据,直到输入完毕。 6. 最后遍历链表输出所有数据。 顺序建立链表算法简单易懂,适合初学者入门。但是该算法的缺点在于它需要依次遍历链表,时间复杂度较高,因此在数据量较大时效率会比较低。此外,该算法只能按照输入的顺序依次插入数据,不能根据数据的大小或其他规则来插入新结点,因此在一些需要按照某种规则排序的场合可能无法满足需求。 总之,顺序建立链表链表的一种基本实现方法,掌握该算法对于学习其他链表操作是非常有帮助的。 ### 回答3: 链表数据结构中很重要的一种,它可以实现灵活的插入、删除、查找等操作。其中,顺序建立链表链表的一种常见方法。 顺序建立链表的过程中,我们需要先定义一个节点结构体,例如: typedef struct node{ int data; // 数据域 struct node* next; // 指针域 }Node; 在这个结构体中,有一个数据域用来存储节点的值,还有一个指针域用来指向下一个节点。接下来,我们可以通过输入数据来构建链表。 具体实现方法是,先定义一个头指针,然后通过循环读取输入的数据,并将其插入到链表中。例如,以下是一个简单的顺序建立链表的代码: void CreateList(Node *&head){ int data; Node *p, *q; head = NULL; q = head; cin >> data; while(data != -1){ p = new Node; p->data = data; p->next = NULL; if(head == NULL){ head = p; q = head; } else{ q->next = p; q = p; } cin >> data; } } 这段代码中,首先定义一个头指针head,将其初始化为NULL。然后依次读取输入的数据,直到读取到-1为止。在每次读取数据之后,新建一个节点p,并将输入的数据存储在节点的数据域中,然后将p插入到链表的尾部。 顺序建立链表的优点是简单快捷,代码实现比较容易,适用于已经知道链表数据的顺序的情况。但是其缺点是无法处理插入数据的顺序不确定的情况,因此需要使用其他方法进行插入操作,例如头插法或尾插法。 综上所述,链表是一个十分重要的数据结构,顺序建立链表链表的一个常用构建方法,其优点是简单快捷,但对于插入顺序不确定的数据,需要使用其他方法进行插入操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值