循环链表应用——约瑟夫置换

约瑟夫问题

介绍

约瑟夫问题,又称约瑟夫置换丢手绢问题

一般形式

(本部分内容来自百度百科

约瑟夫问题是个有名的问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3。

代码

问题描述

本文以以下问题为例

编号为1-10的10 个人围成一圈,从第一个开始报数,第9个被淘汰出圈,剩下的组成新的圈。依次这样下去,求最后一个人的编号

解决

注意:该段代码与上篇文章——《 循环链表定义及操作 》相接

//解答约瑟夫问题
bool Joseph(LinkList* &L, int interval)
{
    LinkList *p = L, *q;
    int i = 0, j = 0;
    int times = 0;  //当前轮数
    int num = 0;

    if(!L || p->next == L)
    {
        cout << "链表为空\n";
        return false;
    }

    if(interval < 1)
    {
        cout << "报数淘汰口令不能小于1\n";
        return false;
    }

    do{

        i += interval;

        //找查第i个结点,p指向该结点的上一个结点
        while(p->next){
            if(p->next != L)
            {
                //如果不是头结点的话
                j++;
            }
            if(j >= i) break;
            p = p->next;

        }
        times++;
        q = p->next;        //临时保存被删结点以备释放空间
        num = q->data;

        cout << "当前结点:" << q->data
             << " 上一个结点:" << p->data
             <<" 下一个结点:" << q->next->data
            << endl;
        p->next = q->next;
        delete q;           //释放被删除结点内存
        LinkListPrint(L);
    }while(L->next != L);   //链表不为空,继续报数

    cout << "最后一个出圈者的编号" << num << endl;

    return true;
}

完整代码

代码

#include <iostream>
#include <string>

using namespace std;


typedef struct _LinkNode
{
    int data;
    struct _LinkNode *next;
}LinkList;

bool InitList(LinkList* &L)
{
    L = new LinkList;

    if(!L) return false;

    L->next = L;
    L->data = 0;

    return true;
}

bool ListInsert_back(LinkList* &L, LinkList *node)
{
    LinkList *last = NULL;
    if(!L || !node) return false;

    if(L == L->next)
    {
        //头结点指针指向了自己(链表只有头结点)
        node->next = L;
        L->next = node;
    }else{
        //非空的循环链表
        last = L->next;
        //寻找尾结点(指向头结点的结点)
        while(last->next != L)
        {
            last = last->next;
        }

        node->next = L;
        last->next = node;
    }
    return true;
}

void LinkListPrint(LinkList *L)
{
    LinkList *p;
    if(!L || L == L->next)
    {
        cout << "链表为空\n";
        return;
    }

    p = L->next;

    while(p != L)
    {
        cout << p->data << "\t";
        p = p->next;
    }


    cout << endl;
}

//解答约瑟夫问题
bool Joseph(LinkList* &L, int interval)
{
    LinkList *p = L, *q;
    int i = 0, j = 0;
    int times = 0;  //当前轮数
    int num = 0;

    if(!L || p->next == L)
    {
        cout << "链表为空\n";
        return false;
    }

    if(interval < 1)
    {
        cout << "报数淘汰口令不能小于1\n";
        return false;
    }

    do{

        i += interval;

        //找查第i个结点,p指向该结点的上一个结点
        while(p->next){
            if(p->next != L)
            {
                //如果不是头结点的话
                j++;
            }
            if(j >= i) break;
            p = p->next;

        }
        times++;
        q = p->next;        //临时保存被删结点以备释放空间
        num = q->data;

        cout << "当前结点:" << q->data
             << " 上一个结点:" << p->data
             <<" 下一个结点:" << q->next->data
            << endl;
        p->next = q->next;
        delete q;           //释放被删除结点内存
        LinkListPrint(L);
    }while(L->next != L);   //链表不为空,继续报数

    cout << "最后一个出圈者的编号" << num << endl;

    return true;
}

int main()
{
    LinkList *L, *s;
    int i = 0;
    if(InitList(L))
    {
        cout << "创建一个空的循环链表\n";
    }else{
        exit(-1);
    }

    cout << "尾插10个元素...\n";

    while((++i)<=10)
    {
        s = new LinkList;
        s->data =  i;
        s->next = NULL;
        if(ListInsert_back(L, s))
        {
            cout << "success\n";
        }else{cout << "default\n";}
    }

    LinkListPrint(L);

    Joseph(L,9);

    return 0;

}

输出结果

注:0为头结点的数据,该结点并不计入约瑟夫环中(但最后也将其删除销毁链表释放内存)

创建一个空的循环链表
尾插10个元素...
success
success
success
success
success
success
success
success
success
success
1	2	3	4	5	6	7	8	9	10	
当前结点:9 上一个结点:8 下一个结点:10
1	2	3	4	5	6	7	8	10	
当前结点:8 上一个结点:7 下一个结点:10
1	2	3	4	5	6	7	10	
当前结点:10 上一个结点:7 下一个结点:0
1	2	3	4	5	6	7	
当前结点:2 上一个结点:1 下一个结点:3
1	3	4	5	6	7	
当前结点:5 上一个结点:4 下一个结点:6
1	3	4	6	7	
当前结点:3 上一个结点:1 下一个结点:4
1	4	6	7	
当前结点:4 上一个结点:1 下一个结点:6
1	6	7	
当前结点:1 上一个结点:0 下一个结点:6
6	7	
当前结点:6 上一个结点:0 下一个结点:7
7	
当前结点:7 上一个结点:0 下一个结点:0
链表为空
最后一个出圈者的编号7
上一篇:C语言单链表操作


下一篇:数据结构与算法C程序(2单链表)