循环链表(约瑟夫问题)C_LinkedList

//
//  C_LinkedList.hpp
//  test1
//  循环链表
//  Created by Zy on 2020/3/28.
//  Copyright © 2020 Jovan. All rights reserved.
//

#ifndef C_LinkedList_hpp
#define C_LinkedList_hpp
#define OK 1
#define ERROR 0
#define OVERFLOW -1
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
typedef int Status;
typedef int ElemType;
typedef struct node{
    ElemType data = 0;
    int index = 0;
    struct node *next = NULL;
    struct node *bf = NULL;
}C_LinkedList;
//初始化、构建实例->插入,删除,长度,读取—>清空循环链表
C_LinkedList * InitList();
void CreateList(C_LinkedList *);//尾插入构建list,实验中用的随机数构建也是用的尾插入法
int GetLength(C_LinkedList *);
C_LinkedList *Locate(C_LinkedList *, ElemType );//查找元素位置f,返回一个指针
C_LinkedList *GetAt(C_LinkedList *,int);//查找,move为正代表向正向移动,否则向反向移动,为0不移动;返回移动后的指针。
Status InsertElem(C_LinkedList *, ElemType);//按照指针位置插入元素(如果参数是头指针,则是首插入,可以通过p=p->next改变插入位置)
Status InsertElemAt(C_LinkedList *, int, ElemType);//按序插入,距离该点d处插入值。如果d为0表示替换该点
C_LinkedList *DeleteElem(C_LinkedList *,ElemType *);//删除指针位置的结点,并得到该点的值x,返回该指针所对应的下一个结点的指针
Status DeleteElemAt(C_LinkedList *, int ,ElemType *);//按序删除
void Erase(C_LinkedList *);
void PrintList(C_LinkedList *);
Status IsEmpty(C_LinkedList *);
#endif /* C_LinkedList_hpp */
//
//  C_LinkedList.cpp
//  test1
//  循环链表
//  Created by Zy on 2020/3/28.
//  Copyright © 2020 Jovan. All rights reserved.
//

#include "C_LinkedList.hpp"
C_LinkedList * InitList(){
    C_LinkedList *L;
       L = (C_LinkedList *)malloc(sizeof(C_LinkedList));
    L->next = L;
    L->bf = L;
    return L;
} //构建空的循环链表
void CreateList(C_LinkedList * L){
    C_LinkedList *rear = L,*p;
    int i,d,n,x;
    printf("输入循环链表的初始长度n:");
    scanf("%d",&n);
    printf("输入随机数范围d(代表密码范围在[-d,d]之间):");
    scanf("%d",&d);
    srand((int)time(0));
    for(i=0;i<n;i++)
    {   x = rand()%(2*d+1)-d;
        if(!x) x++; //不允许密码为0,要么正要么负。但是这样会导致出现1的概率增大,但是随着d的增大,概率会变小,所以尽量让d取大一点
        if(i==0) {rear->data = x;rear->index=1;}
        else {
            p = (C_LinkedList *)malloc(sizeof(C_LinkedList));
            p->data = x;
            p->index = rear->index+1;
            p->next = rear->next;
            p->bf = rear;
            rear->next = p;
            p->next->bf = p;
            rear=p;
        }
    }
}
Status IsEmpty(C_LinkedList *L){
    return L==L->next&&L->index==0;
}
C_LinkedList *DeleteElem(C_LinkedList *L,ElemType *x){
    if(L->index==0) {printf("空的循环链表!
");return NULL;}
    else if(L->next == L){*x = L->data;printf("%d ",L->index);L->index = 0;return L;}
    else {
        C_LinkedList *p = L->next;
        L->next->bf = L->bf;
        L->bf->next = L->next;
        *x = L->data;
        printf("%d ",L->index);
        free(L);
        if(*x>0) return p;
        else return p->bf; //必须用返回值更新L,不然L指针的值没有得到修改;或者用C++引用来实现函数内修改
    }
}
C_LinkedList *GetAt(C_LinkedList *L,int move){
    int i;
    C_LinkedList *p = L;
    if(move>0) for(i=0;i<move;i++) p = p->next;
    else if(move<0) for(i=0;i>move;i--) p = p->bf;
    return p;
}
void PrintList(C_LinkedList *L){
    if(!L||L->index==0) {printf("链表为空,无法打印。
");return;}
    C_LinkedList *p = L;
    printf("%d ",p->data);
    p = p->next;
       while(p!=L){
           printf("%d ",p->data);
           p = p->next;
       }
       printf("
");
}
int GetLength(C_LinkedList *L){
    C_LinkedList *p = L->next;
    int len = 1;
    while(p!=L){p = p->next;len++;}
    return len;
}
C_LinkedList *Locate(C_LinkedList *L, ElemType x){
    C_LinkedList *p = L;
    if(p->data == x) return L;
    p = p->next;
    while(p!=L&&p->data!=x){p = p->next;}
    if(p==L) return NULL;
    else return p;
}
Status InsertElem(C_LinkedList *L, ElemType x){
    if(L==NULL) return ERROR;
    if(L->index==0){L->data = x;L->index = 1;return OK;}
    C_LinkedList *p = (C_LinkedList *)malloc(sizeof(C_LinkedList));
    p->data = x;
    p->index = L->index+1;
    p->next = L->next;
    p->next->bf = p;
    p->bf = L;
    L->next = p;
    return OK;
}
Status InsertElemAt(C_LinkedList *L, int d, ElemType x){
    C_LinkedList *p = L;
    if(!L) return ERROR;
    if(!d) {L->data = x;return OK;} 
    d = d>0?d-1:d;
    p = GetAt(p, d);
    return InsertElem(p, x);
}
Status DeleteElemAt(C_LinkedList *L, int d,ElemType *x){
    C_LinkedList *p = L;
    if(!L) return ERROR;
    p = GetAt(p, d); DeleteElem(p, x);
    return OK;
}
void Erase(C_LinkedList * L){
    C_LinkedList *p = L->next, *temp;
    while(p!=L){temp = p->next; free(p); p=temp;}
    free(p);
    L->index = 0;
}

测试:

#include <stdio.h>
#include <stdlib.h>
#include "C_LinkedList.hpp"
int main()
{
    int m;
    C_LinkedList *L = InitList();
    CreateList(L);
    printf("第一次:");
    PrintList(L);
    printf("输入初始报数上限值:");
    scanf("%d",&m);//m如果为正数从1开始报,如果m为负数,从-1开始报,正负只代表报数方向
    int next_move = m-1;
    while(!IsEmpty(L)){
        L = GetAt(L, next_move);
        L = DeleteElem(L, &next_move);//L处删除后L自动更新到删除点后一位处,next_move保存删除点的密码值
        next_move =next_move>0?next_move-1:next_move+1;
    }
    Erase(L);
    printf("
");
    PrintList(L);
}

输入循环链表的初始长度n:7

输入随机数范围d(代表密码范围在[-d,d]之间):10

第一次:-6 -10 -3 1 -6 10 10 

输入初始报数上限值:6

6 3 7 2 1 4 5 

链表为空,无法打印。

Program ended with exit code: 0

——————————————————————

优化:根据循环链表长度可以通过取余来减少每次移动次数:即修改GetAt函数,和设置全局变量size

修改后代码为:

//
//  C_LinkedList.hpp
//  test1
//  循环链表
//  Created by Zy on 2020/3/28.
//  Copyright © 2020 Jovan. All rights reserved.
//

#ifndef C_LinkedList_hpp
#define C_LinkedList_hpp
#define OK 1
#define ERROR 0
#define OVERFLOW -1
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
typedef int Status;
typedef int ElemType;
typedef struct node{
    ElemType data = 0;
    int index = 0;
    struct node *next = NULL;
    struct node *bf = NULL;
}C_LinkedList;
extern int size;
//初始化、构建实例->插入,删除,长度,读取—>清空循环链表
C_LinkedList * InitList();
void CreateList(C_LinkedList *);//尾插入构建list,实验中用的随机数构建也是用的尾插入法
int GetLength(C_LinkedList *);
C_LinkedList *Locate(C_LinkedList *, ElemType );//查找元素位置f,返回一个指针
C_LinkedList *GetAt(C_LinkedList *,int);//查找,move为正代表向正向移动,否则向反向移动,为0不移动;返回移动后的指针。
Status InsertElem(C_LinkedList *, ElemType);//按照指针位置插入元素(如果参数是头指针,则是首插入,可以通过p=p->next改变插入位置)
Status InsertElemAt(C_LinkedList *, int, ElemType);//按序插入,距离该点d处插入值。如果d为0表示替换该点
C_LinkedList *DeleteElem(C_LinkedList *,ElemType *);//删除指针位置的结点,并得到该点的值x,返回该指针所对应的下一个结点的指针
Status DeleteElemAt(C_LinkedList *, int ,ElemType *);//按序删除
void Erase(C_LinkedList *);
void PrintList(C_LinkedList *);
Status IsEmpty(C_LinkedList *);
#endif /* C_LinkedList_hpp */

其中size前面要加extern 表示全局变量声明而非定义,定义在cpp文件中。

//
//  C_LinkedList.cpp
//  test1
//  循环链表
//  Created by Zy on 2020/3/28.
//  Copyright © 2020 Jovan. All rights reserved.
//

#include "C_LinkedList.hpp"
int size = 0;
C_LinkedList * InitList(){
    C_LinkedList *L;
       L = (C_LinkedList *)malloc(sizeof(C_LinkedList));
    L->next = L;
    L->bf = L;
    return L;
} //构建空的循环链表
void CreateList(C_LinkedList * L){
    C_LinkedList *rear = L,*p;
    int i,d,n,x;
    printf("输入循环链表的初始长度n:");
    scanf("%d",&n);
    size = n;
    printf("输入随机数范围d(代表密码范围在[-d,d]之间):");
    scanf("%d",&d);
    srand((int)time(0));
    for(i=0;i<n;i++)
    {   x = rand()%(2*d+1)-d;
        if(!x) x++; //不允许密码为0,要么正要么负。但是这样会导致出现1的概率增大,但是随着d的增大,概率会变小,所以尽量让d取大一点
        if(i==0) {rear->data = x;rear->index=1;}
        else {
            p = (C_LinkedList *)malloc(sizeof(C_LinkedList));
            p->data = x;
            p->index = rear->index+1;
            p->next = rear->next;
            p->bf = rear;
            rear->next = p;
            p->next->bf = p;
            rear=p;
        }
    }
}
Status IsEmpty(C_LinkedList *L){
    return L==L->next&&L->index==0;
}
C_LinkedList *DeleteElem(C_LinkedList *L,ElemType *x){
    if(L->index==0) {printf("空的循环链表!
");return NULL;}
    else if(L->next == L){*x = L->data;printf("%d ",L->index);L->index = 0;return L;}
    else {
        C_LinkedList *p = L->next;
        L->next->bf = L->bf;
        L->bf->next = L->next;
        *x = L->data;
        printf("%d ",L->index);
        free(L);
        size--;
        if(*x>0) return p;
        else return p->bf; //必须用返回值更新L,不然L指针的值没有得到修改;或者用C++引用来实现函数内修改
    }
}
C_LinkedList *GetAt(C_LinkedList *L,int move){
    int i;
    move = move%size;
    C_LinkedList *p = L;
    if(move>0) for(i=0;i<move;i++) p = p->next;
    else if(move<0) for(i=0;i>move;i--) p = p->bf;
    return p;
}
void PrintList(C_LinkedList *L){
    if(!L||L->index==0) {printf("链表为空,无法打印。
");return;}
    C_LinkedList *p = L;
    printf("%d ",p->data);
    p = p->next;
       while(p!=L){
           printf("%d ",p->data);
           p = p->next;
       }
       printf("
");
}
int GetLength(C_LinkedList *L){
    C_LinkedList *p = L->next;
    int len = 1;
    while(p!=L){p = p->next;len++;}
    return len;//也可以用全局变量size
}
C_LinkedList *Locate(C_LinkedList *L, ElemType x){
    C_LinkedList *p = L;
    if(p->data == x) return L;
    p = p->next;
    while(p!=L&&p->data!=x){p = p->next;}
    if(p==L) return NULL;
    else return p;
}
Status InsertElem(C_LinkedList *L, ElemType x){
    if(L==NULL) return ERROR;
    if(L->index==0){L->data = x;L->index = 1;size++;return OK;}
    C_LinkedList *p = (C_LinkedList *)malloc(sizeof(C_LinkedList));
    p->data = x;
    p->index = L->index+1;
    p->next = L->next;
    p->next->bf = p;
    p->bf = L;
    L->next = p;
    size++;
    return OK;
}
Status InsertElemAt(C_LinkedList *L, int d, ElemType x){
    C_LinkedList *p = L;
    if(!L) return ERROR;
    if(!d) {L->data = x;size++;return OK;}
    d = d>0?d-1:d;
    p = GetAt(p, d);
    return InsertElem(p, x);
}
Status DeleteElemAt(C_LinkedList *L, int d,ElemType *x){
    C_LinkedList *p = L;
    if(!L) return ERROR;
    p = GetAt(p, d); DeleteElem(p, x);
    return OK;
}
void Erase(C_LinkedList * L){
    C_LinkedList *p = L->next, *temp;
    while(p!=L){temp = p->next; free(p); p=temp;}
    free(p);
    L->index = 0;
    size = 0;
}
#include <stdio.h>
#include <stdlib.h>
#include "C_LinkedList.hpp"
int main()
{
    int m;
    C_LinkedList *L = InitList();
    CreateList(L);
    printf("第一次:");
    PrintList(L);
    printf("输入初始报数上限值:");
    scanf("%d",&m);//m如果为正数从1开始报,如果m为负数,从-1开始报,正负只代表报数方向
    int next_move = m-1;
    while(!IsEmpty(L)){
        L = GetAt(L, next_move);
        L = DeleteElem(L, &next_move);//L处删除后L自动更新到删除点后一位处,next_move保存删除点的密码值
        next_move =next_move>0?next_move-1:next_move+1;
    }
    Erase(L);
    printf("
");
    PrintList(L);
    printf("size = %d
",size);
}

输入循环链表的初始长度n:7

输入随机数范围d(代表密码范围在[-d,d]之间):5

第一次:-5 5 1 5 1 5 2 

输入初始报数上限值:6

6 4 3 5 7 2 1 

链表为空,无法打印。

size = 0

Program ended with exit code: 0

通过实验计算时间复杂度===========================================================================================================================

测试运算时间,通过测试每一种规模[n,m] (n<=30), 用10组数据算出运算时间的平均值,控制n不变,改变m;然后控制m不变改变n;m变化区间为[0,2n],步长设为1;

#include <stdio.h>
#include <stdlib.h>
#include "C_LinkedList.hpp"
#include <time.h>
#define MAX 100
#include <time.h>
void my_delay(int delay_t)
{
clock_t start_time; //the start time
start_time=clock();
while((clock()-start_time)/CLOCKS_PER_SEC <delay_t);//delay_t表示延迟多少ms
}
typedef struct person{
    int code = 0;
    int isout = 0;
}Person;
int Inputcode[MAX]={};
double Array_time[MAX]={};
double CList_time[MAX]={};
double Time[2][MAX]={};
double average(double x[],int n){
    double sum = 0;
    for(int i=0;i<n;i++)
        sum += x[i];
    return sum/n;
}
int main()
{   clock_t begin,end;
    double t1,t2;
    int n,m,d,i,x;
    int pointer,count,left;
    C_LinkedList *L = InitList();
    Person a[MAX];
    memset(a,0,sizeof(a));
    
   // printf("输入循环链表的初始长度n:");
     //  scanf("%d",&n);
    n = 20;
    printf("输入初始报数上限值:");
    scanf("%d",&m);//m如果为正数从1开始报,如果m为负数,从-1开始报,正负只代表报数方向
    printf("输入随机数范围d(代表密码范围在[-d,d]之间):");
    scanf("%d",&d);//范围设置的10
    for(d = 10;d<=100;d+=10){
        printf("
>>>>>>>>>>>>>>>>>>n = %d, m = %d ,d = %d >>>>>>>>>>>>>>>>>",n,m,d);
        printf("start?Y or N:");
        getchar();getchar();
    for(int k = 1;k <= 10; k++)
    {
        my_delay(1);
    srand((int)time(0));
/*================生成数据==================*/
    for(i=1;i<=n;i++){
    x = rand()%(2*d+1)-d;
    Inputcode[i]=x?x:1;//不允许密码为0,要么正要么负。但是这样会导致出现1的概率增大,但是随着d的增大,概率会变小,所以尽量让d取大一点
    }
    printf("
第%d组数据:(n=%d,m=%d)
",k,n,m);
        for(i=1;i<=n;i++)
            printf("%d ",Inputcode[i]);
        printf("
=============================
");
 /*================输入数据===================*/
        int j = 5;
        int temp = m;
        while(j--){
    pointer = count = 0;
    memset(a, 0, sizeof(a));
    for(i = 1;i<=n;i++)
    a[i].code = Inputcode[i];
    left = n;
    size = n;
    CreateList(L,Inputcode,n);//用Inputcode分别给循环链表和循环数组赋初值,保证测试数据一致。
    //PrintList(L);

/*===============测试时间(每组数据测十次取平均值)===================*/
        begin = clock();
        do{
            if(temp>0)pointer++;
            else pointer--;
            if(pointer==n+1) pointer=1;
            else if(pointer==0)pointer=n;
            if(!a[pointer].isout){
                if(temp>0) count++;
                else count--;
            }
            if(count==temp){
                count=0;
                printf("%d ",pointer);
                a[pointer].isout = 1;
                temp = a[pointer].code;
                left--;
                if(!left) break;
                if(!(temp%left)) temp = temp>0?-1:1;
                else if(temp>left) temp = temp%left;
                else if(temp<-left) temp = temp%left-left;
            }
        }while(1);
        end = clock();
        printf("
");
        Array_time[j] = (double)(end-begin)/CLOCKS_PER_SEC;
          //  printf("%lf",Array_time[j]);
    
            temp = m;
    int next_move = temp-1;
        begin = clock();
    while(!IsEmpty(L)){
        L = GetAt(L, next_move);
        L = DeleteElem(L, &next_move);//L处删除后L自动更新到删除点后一位处,next_move保存删除点的密码值
        next_move =next_move>0?next_move-1:next_move+1;
    }
            end = clock();
            printf("
");
            CList_time[j] = (double)(end-begin)/CLOCKS_PER_SEC;
    }
        Time[0][k] = average(Array_time, 5);
        Time[1][k] = average(CList_time, 5);
        printf("循环数组所花时间:%lfns
",1000000*Time[0][k]);
        printf("循环队列所花时间:%lfns
",1000000*Time[1][k]);
    }
        t1 = average(Time[0], 10);
        t2 = average(Time[1],10);
        printf("t1 = %lfns;
t2 = %lfns",1000000*t1,1000000*t2);
    }
}
原文地址:https://www.cnblogs.com/raiuny/p/12589915.html