数据结构-线性表(顺序表集合合并,链表,拉丁方阵,魔术师发牌问题等)

线性表的顺序存储结构

# include<stdio.h>
# include<stdlib.h>


# define OK 1
# define ERROR 0
# define TRUE 1
# define FALESE 0


// status 是函数的类型,其值是函数结果的状态代码,如OK等
typedef int status;


// 线性表的顺序存储结构代码
const int Maxsize = 20;
typedef int Elemtype;
typedef struct
{
    Elemtype data[Maxsize];
    int length; // 线性表当前长度
}Sqlist;



// getelem函数
/*
list中第i个元素的值作为e返回
*/
status Getelem(Sqlist list,int i,Elemtype *e)
{
    if(list.length == 0 || list.length < i)   
    {
        return ERROR;
    } 
    *e = list.data[i - 1];
    return *e;
}

// listinsert函数
/*
list中第i个位置插入新元素e
*/
status Listinsert(Sqlist *list,int i,Elemtype e)
{
    int k ;
    if (list->length == Maxsize)
    {
        return ERROR;
    }
    if ( i < 0 || i > list->length + 1)
    {
        return ERROR;
    }
    if ( i <= list->length)
    {
        for ( int k = list->length - 1; k >= i - 1; k --)
        {
            list->data[ k + 1 ] = list->data[k];
        }
    }
    list->data[i - 1] = e; // 将新元素插入,注意这里data下标是i - 1!

    list->length ++ ; // 这一步不要忘记!

    return OK; 

}

//Listdelete
status Listdelete (Sqlist *list,int i,Elemtype e)
{
    if(list->length == 0) // 第一次写的时候未考虑表为空的情况
    {
        return ERROR;
    }
    else if (i <= 0 || i > list->length) // 注意这两个条件是可以并起来的
    {
        return ERROR;
    }
    e = list->data[i - 1]; 
    printf("已删除e!\n");
    for(int k = i - 1; k <= list->length - 1; k ++)
    {
        list->data[k] = list->data[k + 1];
    }
    list->length --;

    return OK;
}

// listprint函数
status Listprint(Sqlist *list)
{
    printf("list: ");
    for(int i = 0; i < list->length; i ++)
    {
        printf("%d ",list->data[i]);
    }
    printf("\n");
    return OK;
}



int main()
{
    printf("hello world!\n");
    printf("------------------------------------------------------\n");
    printf("|选择菜单:\n");
    printf("|1.插入数字\n");
    printf("|2.按照位置删除\n");
    printf("------------------------------------------------------\n");
    printf("请输入一个数字:\n");

    //初始化一个list
    Sqlist list ;
    list.length = 0;
    
    //输入操作
    while(1)
    {
        int option;
        scanf("%d",&option);
        system("cls");
        if(option == 1)
        {
            //输入1 listinsert
            //插入元素
            
            int cnt;
            printf("请输入将要插入多少个元素:\n");
            scanf("%d",&cnt);
            for(int i = 1; i <= cnt ; i ++)
            {
                int e;
                printf("请输入要插入的数字:\n");
                scanf("%d",&e);
                // printf("i = %d ,e = %d\n",i,e);
                Listinsert(&list,i,e);
                Listprint(&list);
            }
        }
        else if (option == 2)
        {
            // 输入2 listdelete
            system("cls");
            printf("请输入要删除list中的第几个数字\n");
            int deln;
            scanf("%d",&deln);
            int e = 0;
            Listdelete(&list,deln,e);
            Listprint(&list);
        }
    }

    system("pause");
    return 0;
}

顺序表实现集合合并(合并后冒泡排序)

/*
    顺序表实现集合合并
*/
# include<stdlib.h>
# include<stdio.h>

# define OK 1
# define ERROR 0
# define Maxsize 100

typedef int Elemtype;

// 定义顺序表结构体
typedef struct list_struct
{
    Elemtype *data;// 基坐标
    int length; // 记录顺序表用到了哪里
}list_struct;
typedef list_struct *plist;


int list_init(plist list)
{
    list->data = (Elemtype *) malloc(Maxsize * sizeof(Elemtype));
    list->length = 0;
    return OK;
}

int list_insert(plist list,Elemtype e)
{
    list->data[list->length ++] = e;
    return OK;
}

int list_delete(plist list,Elemtype e)
{
    int j  = -1;
    for(int i = 0 ; i < list->length ; i ++)
    {
        if(list->data[i] == e)
        {
            j = i;
            break;
        }
    }
    while(j < list->length && j != -1)
    {
        // 覆盖掉即为删除
        list->data[j] = list->data[j + 1];
        j ++;
    }
    list->length --;

    return OK;
}

int list_combine(plist lista,plist listb)
{
    // 已经经过主函数处理,这里lista的长度已经比listb的长度大
    // listb长度小,所以让listb插入到a中去

    // 先删除重复元素

    for(int i = 0 ; i < listb->length ; i ++)
    {
        for(int j = 0 ; j < lista->length ; j ++)
        {
            if(listb->data[i] == lista->data[j])
            {
                //删除 listb中的data[i]
                list_delete(listb,listb->data[i]);
            }
            else continue;
        }
    }

    // listb中剩下的插入lista
    int listaidx = lista->length;
    for(int i = 0 ; i < listb->length ; i ++)
    {
        lista->data[listaidx ++] = listb->data[i];
        lista->length ++;
    }

    return OK;
}

int list_printf(plist list)
{
    for(int i = 0 ; i < list->length ; i ++)
    {
        printf("%d-",list->data[i]);
    }
    printf("\n");

    return OK;
}

// 冒泡排序
int list_sort(plist list)
{
    for(int j = 0; j < list->length ; j ++)
    {
        for(int i = 0 ; i < list->length - j - 1 ; i ++) // 这里要多减去一个1,因为本来最后的一个下标是list->length - 1 而不是list->length,这里是i从0开始,因为最大的一定放到了最后面,所以后面j个一定是最大的了不用去继续排序,而前面是小的仍要从0开始
        {
            if( list->data[i] > list->data[i + 1] )
            {
                int n = list->data[i];
                list->data[i] = list->data[i + 1];
                list->data[i + 1] = n;
                // printf("冒泡排序:%d和%d已经交换\n",list->data[i + 1],list->data[i]);
            }
        }
    }

    return OK;
}


int main()
{
    int an,bn,e;
    plist lista = (plist)malloc(sizeof(list_struct));
    plist listb = (plist)malloc(sizeof(list_struct));
    list_init(lista);
    list_init(listb);

    printf("请输入集合a中元素个数\n");
    scanf("%d",&an);
    printf("请输入集合a中的元素\n");
    while(an --)
    {
        scanf("%d",&e);
        list_insert(lista,e);
    }

    printf("请输入集合b中元素个数\n");
    scanf("%d",&bn);
    printf("请输入集合b中的元素\n");
    while(bn --)
    {
        scanf("%d",&e);
        list_insert(listb,e);
    }
    
    if(lista->length > listb->length) 
    {
        list_combine(lista,listb);
        printf("合并并排序后的集合为:");
        list_sort(lista);
        list_printf(lista);
    }
    else 
    {
        list_combine(listb,lista);
        printf("合并并排序后的集合为:");
        list_sort(listb);
        list_printf(listb);
    }
    
    

   

    // printf("listb为:");
    // list_printf(listb);
    // list_sort(listb);

    system("pause");
    return 0;
}

线性表的链式存储结构

单链表

// 单链表
# include<stdio.h>
# include<stdlib.h>


# define OK 1
# define ERROR 0
# define TRUE 1
# define FALESE 0


typedef int status;


typedef int Elemtype;

typedef struct Node
{
    Elemtype data;
    Node *next;
}Node;

typedef struct Node *Linklist;  

Linklist chainlist_creat(void)
{
    Linklist headnode = (Linklist)malloc(sizeof(Node)); // 这里必须使用molloc函数去为结点申请内存空间,这样才会返回一个指向该块内存的指针,我们才能找到那块内存。堆储存区的内存是可以自由使用的,但是在不使用的时候就要记得free。栈储存区的在函数运行完会自动回收内存
    Linklist list = headnode; // 这里直接创建了一个指向node类型的指针(头指针),并将它初始化了。
    /*
        headnode是一个指向头结点类型的指针
    */
    headnode->next = NULL; 
    headnode->data = 0; // 储存链表长度
    return list;
}

// 链表插入
status chainlist_insert(Linklist list, Elemtype e, int i)
{
    Linklist p;
    p = list;
    int j = 0; 
    while(j < i && p)
    {
        p = p->next;
        j ++;
    }
    if (!p) 
    {
        printf("p为null\n");
        return ERROR;
    }

    Linklist pnewnode = (Linklist)malloc(sizeof(Node));
    pnewnode ->data = e;
    pnewnode ->next = p->next;
    p->next = pnewnode;
    list->data ++;
    printf("链表现在长度为:%d\n",list->data);
    return OK;
}

// 链表打印
status chain_printf(Linklist list)
{
    printf("链表为:\n");
    Linklist p = list->next;
    while(p)
    {
        printf("%d-",p->data);
        p = p->next;
    }
    printf("\n");
    return OK;
}
// 链表查找
status chain_find(Linklist list,int i,Elemtype *e)
{
    Linklist p = list->next;
    int j = 1;
    while(p && j < i)
    {
        p = p->next;
        j ++;
    }
    if (!p)
    {
        return ERROR;
    }
    *e = p->data;
    return OK;
}
// 单链表删除
status chain_del(Linklist list,int i)
{
    Linklist p;
    p = list;
    int j = 1;
    while( j < i && p)
    {
        p = p->next;
        printf("删除操作——遍历到%d\n",p->data);
        j ++;
    }
    if( !p )
    {
        return ERROR;
    }
    int e;
    Linklist q = p->next;
    e = q->data;
    printf("第%d个元素%d已删除!\n",i,e);
    p->next = p->next->next;
    list->data --;
    free(q);

    return OK;
}


int main()
{
    Linklist list = chainlist_creat();
    while(1)
    {
        printf("-------------------------菜单---------------------\n");
        printf("0.清屏\n");
        printf("1.在第i个位置后面插入新元素e\n");
        printf("2.按位置查找,输出第i位置的元素e\n");
        printf("3.删除链表中第i个位置的元素\n");
        printf("--------------------------------------------------\n");
        int op;
        scanf("%d",&op);
        if(op == 1)
        {
            int i,e;
            printf("请输入i、e的值(想把新元素插入到第一个位置请输入0 e)\n");
            scanf("%d %d",&i,&e);
            chainlist_insert(list,e,i);
            printf("插入完成!\n");
            chain_printf(list);
        }
        else if (op == 2)
        {
            int i,*e;
            printf("请输入i的值\n");
            scanf("%d",&i);
            chain_find(list,i,e);
            printf("%d\n",*e);
        }
        else if(op == 3)
        {
            int i;
            printf("请输入i的值\n");
            scanf("%d",&i);
            chain_del(list,i);
            chain_printf(list);
            printf("\n");
        }
        else if(op == 0)
        {
            system("cls");
        }
    }
    return 0;

}

静态链表

/*
    静态链表,双数组实现。
    下面的写法很简洁,但是是具有局限性的,首先它删除的结点就不能free利用。还有分头插尾插。。。。。
*/



# include<stdio.h>
# include<stdlib.h>

# define OK 1
# define ERROR 0


const int N = 10000;
int head,e[N],ne[N],idx; //head 指针指向头结点 e[]数组保存的是链表中的数,ne[]中是next指针,俩个通过下标关联起来,idx保存的是内存用到了哪个位置

int initlist(void)
{
    head = -1; // 头指针指向-1,链表为空
    idx = 0; // 内存用到了0

    return OK;
}

int Lengthlist(void)
{
    if(head == -1) return 0;
    int p = e[head];
    int cnt = 0;
    while( p > 0)
    {
        p = ne[p]; // 将指针的值赋值给p,若p为-1退出循环
        cnt ++;
    }
    return cnt;
}

int add_to_head(int x)
{
    e[idx] = x; // e[idx]就是新的内存 这里将新的元素e存入新申请的内存
    ne[idx] = head; // e[idx] 指向head的值,head 是指针,指向的是第一个结点。head的值就是第一个结点的位置(下标)
    head = idx ++; // head 指向idx 然后将idx ++ (下一次申请新内存直接ne[idx]即可)

    return OK;
}

// 在第K个位置的后面插入元素e
int insert_to_list(int k,int x)
{
    e[idx] = x;
    ne[idx] = ne[k];
    ne[k] = idx ++; // 这里注意,结点k的next指针应该指向的是 新结点 ,而新结点的位置(下标)就是idx,所以就直接是idx赋值给 k结点的next指针,idx ++ 是为了malloc新的“内存”

    return OK;
}

int remove_head(void)
{
    head = ne[head];

    return OK;
}

// 移除第k个位置的后继结点
int remove(int k)
{
    ne[k] = ne[ne[k]];

    return OK;
}

int printflist(void)
{
    int p = head;
    printf("链表为:\n");

    while(p >= 0)
    {
        printf("%d-",e[p]);
        p = ne[p];
    }
    printf("\n");

    return OK;
}

int main()
{
    initlist();
    while (1)
    {
        printf("--------------------menu-----------------------\n1.从头部插入\n2.在第k个结点后面插入元素x\n3.删除第K个结点后面的一个结点\n-----------------------------------------------------------------\n");
        int op;
        scanf("%d",&op);
        if(op == 1)
        {
            int x;
            printf("请输入x的值:\n");
            scanf("%d",&x);
            add_to_head(x);

            printflist();
        }
        else if(op == 2)
        {
            int k,x;
            printf("注意此方法不能插在头部和尾部!!!\n");
            printf("请输入k、x的值:\n");
            scanf("%d%d",&k,&x);
            insert_to_list(k,x);

            printflist();
        }
        else if(op == 3)
        {
            int k;
            printf("请输入k的值:\n");
            scanf("%d",&k);
            remove(k);

            printflist();
        }
    }

    return 0;
    
}

作业:如何快速得到单链表的中间结点

思路:快慢指针

// 作业1
// 随机生成20个随机数的链表,使用快慢指针得到中间元素的值

# include<stdio.h>
# include<stdlib.h>
# include<time.h>

// 返回一个1 - 100之内的数字
int my_rand(void)
{
    srand((unsigned)time(0) + rand());
    int a = rand() % 100;
    return a;
}


// 定义一个结点
typedef struct Node 
{
    int data;
    Node *next;
}Node;

typedef Node *Link_list;


// 生成20个随机数链表,返回头指针
Link_list get_chain(void)
{
    Link_list p = NULL;

    //头结点
    Link_list head = (Link_list)malloc(sizeof(Node));
    p = head;
    head->next = NULL;
    
    // 头插法插入结点
    for(int i = 0; i < 20; i ++)
    {
        Link_list node = (Link_list)malloc(sizeof(Node));
        node->data = my_rand();
        node->next = head->next;
        head->next = node;
        printf("%d-",node->data);
    }
    printf("\n");
    return head;

}

// 获得链表中间值的快慢指针法
int get_chain_midelm(Link_list p)
{
    p = p->next; //指向第一个结点
    Link_list fast_p = p;
    Link_list slow_p = p;

    while(fast_p)
    {
        // printf("遍历到结点的data = %d\n",fast_p->data);
        fast_p = (fast_p->next)->next;
        
        slow_p = slow_p->next;
    }

    int mid = slow_p->data;
    printf("mid = %d\n",mid);

    return mid;
}

int main()
{
    Link_list listp = get_chain();
    get_chain_midelm(listp);
    system("pause");
    return 0;
}

单向循环链表

# include<stdlib.h>
# include<stdio.h>
# define OK 1
# define ERROR 0
typedef struct Node
{
    int data;
    Node *next;
}Node;

typedef Node* Link_list;

// 单向循环链表初始化

Link_list clist_init(void)
{
    Link_list list; // 链表名作为头指针 但是单向循环链表的头指针是指向尾结点的。或者我们称之为尾指针
    Link_list head = (Link_list)malloc(sizeof(Node));// 申请一个头结点

    if(head == NULL) exit(0); // 如果申请失败就 退出

    head->next = head; // 让头结点指向自己,就是一个链表为空的单向循环链表链表

    list = head;

    return list; // 返回尾指针
}

int clist_length(Link_list list)
{
    int length = 0;
    for(Link_list pnode = list; pnode->next != list; )
    {
        pnode = pnode->next;
        length ++;
    }

    return length;
}

//向单向链表头部插入新元素e
void clist_insert_head(Link_list list,int e)
{
    Link_list newnode = (Link_list)malloc(sizeof(Node));
    newnode->data = e;
    newnode->next = list->next;
    list->next = newnode;
}

//打印链表
void clist_printf(Link_list list)
{
    printf("链表为:\n");
    for(Link_list p = list;p->next !=list;)
    {
        p = p->next;
        printf("%d-",p->data);
    }
    printf("\n");
}
// 在单向链表中从尾部插入新的元素e
void clist_insert_end(Link_list list,int e)
{
    Link_list newnode = (Link_list)malloc(sizeof(Node));
    newnode->data = e;
    Link_list p = list;
    while(p->next != list) p = p->next; // 找到了尾结点的前一个结点,并用p指针指向了它
    p->next = newnode;
    newnode->next = list;
    
}
// 在单向循环链表中第i个位置之后插入元素e(即将元素插入到第i + 1个元素),注意不能涉及到从头插入或从尾部插入!!!
int clist_insert(Link_list list,int e,int i)
{
    if(i < 0 || i > clist_length(list))
    {
        printf("i值错误!\n");

        return ERROR;
    }
    Link_list newnode = (Link_list)malloc(sizeof(Node));
    newnode->data = e;
    // 使指针遍历到第i个结点(p此时指向的就是第i个结点)
    Link_list p = list;
    for(int k = 0; k < i; k ++)
    {
        p = p->next;
    }
    // 开始插入
    newnode->next = p->next;
    p->next = newnode;

    return OK;
}

// 删除链表的第i个元素
int clist_remove(Link_list list,int i)
{
    Link_list p = list;
    for(int k = 0;k < i - 1; k ++)
    {
        p = p->next;
    }
    Link_list r_p = p->next;
    p->next = p->next->next;
    free(r_p);

    return OK;
}

int main()
{
    Link_list list = clist_init();
    while(1)
    {
        printf("=======================================================\n");
        printf("请输入操作数:\n1.向链表头部插入新元素e\n2.输出链表的长度\n3.在链表尾部插入新的元素\n4.在第i个元素后面插入新的元素e\n5.删除第i个结点\n...\n");
        printf("=======================================================\n");
        int op;
        scanf("%d",&op);

        if(op == 1)
        {
            int e;
            printf("请输入元素e的值:\n");
            scanf("%d",&e);
            clist_insert_head(list,e);
            clist_printf(list);
        }
        else if(op == 2)
        {
            int length = clist_length(list);
            printf("链表的长度为:%d\n",length); 
        }
        else if(op == 3)
        {
            int e;
            printf("请输入元素e的值:\n");
            scanf("%d",&e);
            clist_insert_end(list,e);
            clist_printf(list);
        }
        else if(op == 4)
        {
            int i,e;
            printf("请输入i、e的值:\n");
            scanf("%d%d",&i,&e);
            clist_insert(list,e,i);
            clist_printf(list);
        }
        else if(op == 5)
        {
            int i;
            printf("请输入要删除的结点i的值:\n");
            scanf("%d",&i);
            clist_remove(list,i);
            clist_printf(list);
        }
    }

    system("pause");
    return 0;
}

单向循环链表练习-约瑟夫环问题

// 约瑟夫环问题
/*
    一共41人围成一个圈,开始报数,每个报3的人自杀,求出这41个人自杀顺序编号输出

    -----循环列表解决
*/

# include<stdio.h>
# include<stdlib.h>

# define OK 1
# define ERROR 0
# define TRUE 1
# define FALSE 0

// 每个人为一个结点,idx是这个人的编号
typedef struct Node
{
    int idx;
    Node *next;
}Node;

typedef Node *Clink_list;

Clink_list cylist;

int clist_init(void)
{
    Clink_list end_node = (Clink_list)malloc(sizeof(Node));
    end_node->idx = 41;
    end_node->next = end_node;
    cylist = end_node;
    for(int i = 40;i >=1;i --)
    {
        Clink_list new_node = (Clink_list)malloc(sizeof(Node));
        new_node->idx = i;
        new_node->next = end_node->next;
        end_node->next = new_node;
    }
    return OK;
}

void clist_printf(void)
{
    Clink_list p = cylist->next;
    while(p->next != cylist->next)
    {
        printf("%d-",p->idx);
        p = p->next;
    }
    printf("%d",p->idx);
    printf("\n");
}

// 一直遍历链表,将答案输出
void answer_clist_remove(void)
{
    Clink_list p = cylist->next;
    int i = 1,cnt = 1;
    while(p != NULL)
    {
        // 每当轮到人报数2,他的下一个人必定报3自杀
        if(i == 2)
        {
            Clink_list throw_p = p->next;
            p->next = p->next->next; // 直接绕过即可,然后下一个人报数1
            printf("%d:编号为%d的人自杀\n",cnt ++ ,throw_p->idx);
            free(throw_p);
        }
        i ++;
        if(i == 3) i = 1;
        p = p->next;
        if(p->next == p) 
        {
            printf("最后一个人编号为%d\n",p->idx);
            break;
        }
    }
}

int main()
{
    clist_init();
    clist_printf();
    answer_clist_remove();
    system("pause");
    return 0;
}

单向循环链表练习2-约瑟夫环问题升级版

// 约瑟夫环问题-升级版
/*
    有1-N个人,每个人手中有一个密码(正整数m,可以控制输入),然后从第一个人开始,第一个人手中的密码为m1
    ,开始报数,当谁报的数为第一个人手中的密码数字m1时,他就自杀,自杀的这个人手中的密码m'为新的自杀数。
    
    输出每个自杀的人的编号
*/

# include<stdio.h>
# include<stdlib.h>

# define OK 1
# define ERROR 0
# define TRUE 1 
# define FALSE 0

typedef struct Node
{
    int idx; // 这个人的序号值
    Node *next; 
    int m_number; // 这个人手中的密码
}Node;

typedef Node *Clink_list;

Clink_list clist;

Clink_list clist_init(void)
{
    printf("链表初始化:\n");
    
    Clink_list head = (Clink_list)malloc(sizeof(Node));
    head->idx = 1;
    
    int pwd;
    printf("请输入第1个人的密码设为?\n");
    scanf("%d",&pwd);
    head->m_number = pwd;
    head->next = head;

    clist = head;
    return clist;
}

// 传入数据n个人,将剩下的n-1个人插入到循环列表中
// 尾插法
int clist_init_2(int n)
{
    for(int i = 1;i < n ; i ++)
    {
        int n_nbr;
        printf("请设置第%d个人的密码是?\n",i + 1);
        scanf("%d",&n_nbr);
        
        Clink_list newnode = (Clink_list)malloc(sizeof(Node));
        newnode->idx = i + 1;
        newnode->m_number = n_nbr;
        newnode -> next = clist -> next; // clist表示的就是尾结点
        clist->next = newnode;

        clist = newnode;
    }

    return OK;
}

int clist_printf()
{
    Clink_list p = clist -> next;
    while(p->next != clist)
    {
        printf("第%d个人,密码是:%d\n",p->idx,p->m_number);
        p = p->next;
    }
    printf("第%d个人,密码是:%d\n",p->idx,p->m_number);
    p = p->next; // 上面只是循环到了clist(尾结点)的前一个结点,所以要再走一步输出
    printf("第%d个人,密码是:%d\n",p->idx,p->m_number);

    return OK;
}



int clist_remove(void)
{
    Clink_list p = clist->next;
    int k = 1; // 记录每个人报数
    int cnt = 1; // 记录每个人的序号
    int m = p->m_number;
    while(p->next != p) // 只剩自己一个人的时候退出
    {
        // p 真杀他自己
        if (m == 1) 
        {
            // 使用一个新指针q来遍历到 要自杀的p的前一个结点,从而实现对p的绕过
            Clink_list q = clist;
            while(q->next != p)
            {
                q = q->next; 
            }
            Clink_list cut_p = q->next;
            // 如果要删除的结点是尾结点,需要将尾结点的指针前移
            if(q->next->idx == clist->idx)
            {
                clist = q;
            }
            q->next = q->next->next;
            
            p = p->next;
            printf("第%d个自杀的人的序号是%d,他的密码是%d\n",cnt ++,cut_p->idx,cut_p->m_number);
            m = cut_p->m_number;

            free(cut_p);
            k = 1;
        }
        else if(k == m - 1) // 如果报数到 m - 1,则下一个人需要被自杀,即这时候p的下一个结点需要被删掉
        {
            Clink_list cut_p = p->next; 
            
            if(p->next->idx == clist->idx)
            {
                clist = p;
            }
            p->next = p->next->next;
            printf("第%d个自杀的人的序号是%d,他的密码是%d\n",cnt ++,cut_p->idx,cut_p->m_number);
            m = cut_p->m_number;
            free(cut_p);
            p = p->next;
            k = 1;
        }
        else 
        {
            k++;
            p = p->next;
        }
    }
    printf("第%d个自杀的人的序号是%d,他的密码是%d\n",cnt ++,p->idx,p->m_number);

    return OK;
}

int main()
{
    clist_init();
    printf("一共有n个人,请输入n的值:\n");
    int n;
    scanf("%d",&n);
    clist_init_2(n);
    clist_printf();
    clist_remove();
    system("pause");
    return 0;
}

两个单向循环链表的合并

/*
    将两个单向循环链表链接在一起
*/
# include<stdio.h>
# include<stdlib.h>

# define OK 1
# define ERROR 0
# define FALSE 0
# define TRUE 1

typedef struct Node
{
    int data;
    Node * next;
}Node;

typedef Node *Clink_list;



// 初始化循环链表,传入循环链表名字,返回循环链表名字
Clink_list clist_init(Clink_list clist)
{
    // 先设置尾结点
    Clink_list end_node = (Clink_list)malloc(sizeof(Node));
    end_node -> data = 10;
    end_node -> next = end_node;
    for(int i = 9; i >= 1; i --)
    {
        Clink_list p = (Clink_list)malloc(sizeof(Node));
        p->data = i;
        p->next = end_node->next;
        end_node->next = p;
        end_node = p;
    }
    clist = end_node;

    return clist;
}
int clist_printf(Clink_list clist)
{
    int clength = 1;
    printf("链表为:");
    Clink_list p;
    for(p = clist->next; p != clist;p = p->next) // p遍历到维结点就立马退出了,所以尾结点需要单独输出一次并length++
    {
        printf("%d-",p->data);
        clength ++;
    }
    printf("%d-",p->data);
    printf("\n");

    printf("该链表长度为%d\n",clength ++);
    return OK;
}

Clink_list clist_combine(Clink_list lista,Clink_list listb)
{
    Clink_list ahead = lista->next;
    lista->next = listb->next;
    listb->next = ahead;

    return listb;
}

int main(void)
{
    // 初始化
    Clink_list clist_a = clist_init(clist_a);
    Clink_list clist_b = clist_init(clist_b);

    clist_printf(clist_a);
    clist_printf(clist_b);

    Clink_list clist_c = clist_combine(clist_a,clist_b);
    clist_printf(clist_c);
    
    system("pause");
    return 0;
}

判断链表是否有环

/*
    判断单链表中是否有环:
    双指针算法,使用快慢双指针。
    指针p q 同时开始遍历,一个移动1一个移动2,当p == q 真值为1的时候说明有环
*/

// 这里做一个单向循环列表,一个单向链表用来判断

# include<stdio.h>
# include<stdlib.h>

# define OK 1
# define ERROR 0
# define TRUE 1
# define FALSE 0

typedef struct Node
{
    int data;
    Node *next;
}Node;

typedef Node *Link_list;

Link_list list_init(Link_list list)
{
    Link_list head_node = (Link_list)malloc(sizeof(Node));
    head_node->data = 10;
    head_node->next = NULL;
    for(int i = 9;i >= 1; i --)
    {
        Link_list node = (Link_list)malloc(sizeof(Node));
        node->data = i;
        node->next = head_node;
        head_node = node;
    }

    list = head_node;
    return list;
}

int list_printf(Link_list list)
{
    Link_list p = list;
    printf("单向链表为:");
    while(p != NULL)
    {
        printf("%d-",p->data);
        p = p->next;
    }
    printf("\n");

    return OK;
}

Link_list clist_init(Link_list clist)
{
    Link_list end_node = (Link_list)malloc(sizeof(Node));
    end_node->data = 10;
    end_node->next = end_node;
    clist = end_node;
    for(int i = 9;i >= 1; i --)
    {
        Link_list new_node = (Link_list)malloc(sizeof(Node));
        new_node->data = i;
        new_node->next = end_node->next;
        end_node->next = new_node;
    }

    return clist;
}

int clist_printf(Link_list clist)
{
    Link_list p = clist->next;
    printf("链表为:");
    while(p != clist)
    {
        printf("%d-",p->data);
        p = p->next;
    }
    printf("%d-\n",p->data);

    return OK;
}

// 返回0或1,0代表无环,1代表有环
int judge_loop(Link_list list)
{
    int judge = 0; // 0代表无环,1代表有环
    Link_list fast_p = list;
    Link_list slow_p = list;

    while(slow_p != NULL )
    {
        fast_p = fast_p->next;
        if(fast_p == NULL) break;
        fast_p = fast_p->next;
        if(fast_p == NULL) break;
        slow_p = slow_p->next;
        if(fast_p == slow_p)
        {
            judge = 1;
            break;
        }
    }

    return judge;
}

int main()
{
    Link_list lista; // 单向链表
    lista = list_init(lista);
    list_printf(lista);

    Link_list clista;
    clista = clist_init(clista);
    clist_printf(clista);
    
    int jud_clist = 0,jud_list = 0;
    jud_list = judge_loop(lista);
    jud_clist = judge_loop(clista);

    printf("clista有无环:%d\nlista有无环:%d\n",jud_clist,jud_list);

    system("pause");
    return 0;
}

★魔术师发牌问题★

首先手动模拟一下,思路见代码

一个思想,假设是正确的。假设我们的循环链表就是正确的答案。

/*
    魔术师发牌问题:
    魔术师将A 2 3 4 5 6 7 8 9 10 J Q K十三张牌按照一定顺序叠放好,将有花色的一面朝下。
    第一次,魔术师开始数数,数1,停止数数。将最上面的牌翻过来,然后将其放在桌面上展示,是A。
    第二次,魔术师开始数数,数1,2,停止数数。将牌顶的第一张牌放到牌堆最下方,第二张牌翻过来,然后将其放在桌面上展示,是2。
    第三次,魔术师开始数数,数1,2,3,停止数数。将牌顶的第一二张牌放到牌堆最下方,第三张牌翻过来,然后将其放在桌面上展示,是3。
    ......
    直到将所有的牌亮出来为止,问原来的顺序是怎么样的?
*/
# include<stdlib.h>
# include<stdio.h>

# define OK 1
# define ERROR 0
# define TRUE 1
# define FALSE 0

typedef struct Node 
{
    int idx;
    int data;
    Node *next;
}Node;

typedef Node *Clink_list;

// 初始化循环列表 
Clink_list magic_clist_init(Clink_list clist)
{
    Clink_list end_node = (Clink_list)malloc(sizeof(Node));
    end_node->data = 0;
    end_node->idx = 13;
    end_node->next = end_node;
    clist = end_node;

    for(int i = 1 ; i <= 12; i ++) // 由于只有13张牌,所以这里就直接使用数值13.如果扑克牌有14、15等等,把这里的12改成13、14等等即可
    {
        Clink_list new_node = (Clink_list)malloc(sizeof(Node));
        new_node->data = 0;
        new_node->idx = i;
        Clink_list p = end_node->next;
        while(p->next != end_node)
        {
            p = p->next;
        }
        p->next = new_node;
        new_node->next = end_node;
    }

    return clist;
}

int clist_count(Clink_list clist)
{
    Clink_list p = clist->next;
    int cnt = 0;
    // printf("链表:");
    while(p != clist)
    {
        // printf("%d-",p->data);
        cnt ++;
        p = p->next;
    }
    // printf("%d\n",p->data);
    // printf("循环链表中有%d个元素\n",++ cnt);

    return ++cnt;
}
/*
    假设我的链表中的每一个结点的data的值就是正确的顺序。
    向循环链表中的结点添加data = see_n(将要翻过来牌的数),然后将这个结点remove掉

*/
int mclist_remove(Clink_list clist)
{
    int speak_n = 1,see_n = 1;//speak_n表示魔术师的报数,see_n表示下一个翻过来的牌的数,order_n表示正确排序的第几张牌
    Clink_list p = clist->next; // p去遍历
    while(p)
    {
        for(speak_n = 1;speak_n < see_n ; speak_n ++)
        {
            p = p->next;
        }
        p->data = see_n ++;
        printf("牌堆中第%d牌应该是%d\n",p->idx,p->data);

        //删除结点
        Clink_list cut_p = p,q = clist->next;
        // 使用q遍历到想要删除的结点p前面的一个结点
        while(q->next != cut_p)
        {
            q = q->next;
        }
        // 对p结点绕过
        q->next= q->next->next;


        if(clist_count(clist) == 1) break;
        p = q->next;
        // printf("p->data = %d\n",p->data);
        free(cut_p);
    }
    p = p->next;
    p->data = see_n ++;
    printf("牌堆中第%d牌应该是%d\n",p->idx,p->data);

    return OK;
}

int main()
{
    Clink_list clist;
    clist = magic_clist_init(clist);
    clist_count(clist);
    mclist_remove(clist);
    system("pause");
    return 0;
}

拉丁方阵问题

/*
        拉丁方阵是一种n×n的方阵,方阵中恰有n中不同的元素,每种元素恰有n个,并且每种元
    素在一行和一列中恰好出现一次。著名数学家和物理学家欧拉使用拉丁字母来做为方阵里元素
    的符号,拉丁方阵因此而得名。
*/

# include<stdio.h>
# include<stdlib.h>

typedef struct Node
{
    int data;
    Node *next;
}Node;

typedef Node *Clink_list;

Clink_list clist_init(Clink_list clist)
{
    Clink_list end_node = (Clink_list)malloc(sizeof(Node));
    clist = end_node;

    end_node->data = 9;
    end_node->next = end_node;

    for(int i = 1;i <= 8; i ++)
    {
        Clink_list new_node = (Clink_list)malloc(sizeof(Node));
        new_node->data = i;
        Clink_list p = end_node;
        while(p->next != end_node)
        {
            p = p->next;
        }
        new_node->next = end_node;
        p->next = new_node;
    }

    return clist;
}

void latin_printf(Clink_list clist)
{
    printf("latin:\n");
    int k = 9;
    while(k --)
    {
        Clink_list p = clist->next;
        while(p != clist)
        {
            printf("%d ",p->data);
            p = p->next;
        }
        printf("%d\n",p->data);
        clist = clist->next;
    }
}

int main()
{
    Clink_list clist ;
    clist = clist_init(clist);
    latin_printf(clist);

    system("pause");
    return 0;
}

双向循环链表

/*
    双向链表
    初始化
    插入
    删除
    输出
*/

# include<stdio.h>
# include<stdlib.h>

typedef struct Node
{
    int data;
    Node *prior;
    Node *next;
}Node;

typedef Node *Doublelink_list;

Doublelink_list doublelist_init(Doublelink_list dlist)
{
    Doublelink_list head_node = (Doublelink_list)malloc(sizeof(Node));
    dlist = head_node;
    Doublelink_list end_node = (Doublelink_list)malloc(sizeof(Node));
    head_node->data = 0;
    end_node->data = 0;

    head_node->next = end_node;
    head_node->prior = end_node;
    end_node->next = head_node;
    end_node->prior = head_node;

    return dlist;
}
int doublelist_length(Doublelink_list dlist)
{
    int length = 0;
    for(Doublelink_list p = dlist;p != dlist->prior ; p = p->next)
    {
        length ++ ;
    }
    length ++ ;

    return length;
}

// 在第i个元素之后插入元素e
Doublelink_list doublelist_insert(Doublelink_list dlist,int i,int e)
{
    if(i <= doublelist_length(dlist) - 2 && i >= doublelist_length(dlist)) 
    {
        printf("双向链表与长度与输入的i不符!\n");
        exit(1);
    }
    Doublelink_list p = dlist;
    // 使用p指针遍历到第i个结点本身
    for(int k = 1;k < i ;k ++)
    {
        p = p->next;
    }
    // 插入
    Doublelink_list new_node = (Doublelink_list)malloc(sizeof(Node));
    new_node->data = e;

    new_node->next = p->next;
    new_node->prior = p;
    p->next->prior = new_node;
    p->next = new_node;
    
    return dlist;
}

// 删除第i个位置的元素,并返回其值e
int doublelist_delete(Doublelink_list dlist,int i)
{
    if(i >= doublelist_length(dlist) && i <= 1)
    {
        printf("链表长度与i的值不符!\n");
        exit(1);
    }
    Doublelink_list p = dlist;
    // 先使用p指针遍历到第i个元素的前一个元素
    for(int k = 1; k < i - 1; k ++)
    {
        p = p->next;
    }
    Doublelink_list cut_p = p->next;
    p->next->prior = p;
    p->next = p->next->next;

    int e = cut_p->data;
    printf("被删掉的元素是%d\n",e);
    free(cut_p);

    return e;
}

void doublelist_printf(Doublelink_list dlist)
{
    Doublelink_list p = dlist;
    printf("链表为:");
    while(p != dlist->prior)
    {
        printf("%d-",p->data);
        p = p->next;
    }
    printf("%d\n",p->data);
}

int main()
{
    Doublelink_list dlist;
    dlist = doublelist_init(dlist);
    int dlist_length = doublelist_length(dlist);
    printf("%d\n",dlist_length);

    printf("插入操作:请输入插入操作的个数?\n");
    int cnt = 0;
    scanf("%d",&cnt);
    while(cnt --)
    {
        int i,e;
        printf("请输入要在第几个元素后面插入?插入元素的值是多少?\n");
        scanf("%d%d",&i,&e);
        doublelist_insert(dlist,i,e);
        doublelist_printf(dlist);
    }
    printf("删除操作:请输入删除操作的个数?\n");
    scanf("%d",&cnt);
    while(cnt --)
    {
        int i;
        printf("请输入要删除第几个元素?\n");
        scanf("%d",&i);
        doublelist_delete(dlist,i);
        doublelist_printf(dlist);
    }


    system("pause");
    return 0;
}
posted @ 2021-10-28 19:45  r涤生  阅读(49)  评论(0编辑  收藏  举报