约瑟夫环

问题描述:有n个人围成一个环循环报数,每次报数为m的人出局,剩下的人继续游戏,直到最后只剩一个人为止,返回胜利者的下标。


1.普通实现

思路:

    设每个人的起始数据都是0,设置一个计数器,当计数器为m的时候把对应的人的数据设置为1,返回最后对应数据为0的人
的下标。此方法并不会删除数据,只是将对应的数据改变,最后当设置的循环条件不满足时,遍历全部数据找到那个与初始值
相同的元素即可。

看程序:

public class JosephTest{

    public static int reverse(int n, int m) {
        // 设置计数器
        int count = 0;
        // 定义指针
        int pos = 0;
        // 每个人的默认初始值为0
        int[] arr = new int[n];
        // 剩余的人
        int left = arr.length;
        while(left > 1) {
            if(arr[pos] == 0) {
                count++;    
            }
            // 计数器为m时,修改对应的数据,计数器置0,剩余人数减1
            if(count == m) {
                arr[pos] = 1;
                count = 0;
                left--;
            }
            pos++;
            // 指针循环
            if(pos == arr.length) {
                pos = 0;
            }
        }
        /** 根据数据查找剩余的人 */
        for(int x = 0; x < arr.length; x++) {
            if(arr[x] == 0) {
                return x;
            }
        }
        return -1;  
    }

    public static void main(String[] args) {
        System.out.println(reverse(3, 3));  
    }
}

结果:1


2.双向链表实现

思路:

    用一个循环的双向链表来保存对应的人,设结点的数据从0开始依次递增,就可以把结点数据当做下标来使用。设置计数器
的初始值为1,就包含了只有一个数据的情况,每当计数器的值为m,删除对应的结点,直到循环链表只剩一个结点为止。

先来看看同包中要用到的双向循环链表的代码:

class DoubleLink {
    class Entry {
        int data;
        Entry next;
        Entry prev;

        public Entry() {
            data = -1;
            next = null;
            prev = null;
        }

        public Entry(int data) {
            this.data = data;
            next = null;
            prev = null;
        }
    }

    public Entry head;

    public DoubleLink() {
        head = new Entry();
    }

    /**
     * 尾插法
     */
    public void insertTail(int data) {
        Entry cur = head;
        while (cur.next != null) {
            cur = cur.next;
        }
        Entry entry = new Entry(data);
        cur.next = entry;
        entry.prev = cur;
    }

    /**
     * 制作环
     */
    public void makeLoop() {
        Entry cur = head;
        while (cur.next != null) {
            cur = cur.next;
        }
        cur.next = head.next;
        head.next.prev = cur;
    }

    /**
     * 判断是否是环
     */
    public boolean isLoop() {
        Entry fast = head;
        Entry slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow) {
                return true;
            }
        }
        return false;
    }

    /**
     * 求环的长度
     */
    public int getLoopLength() {
        int len = 0;
        boolean flag = false;
        Entry fast = head;
        Entry slow = head;
        if (!isLoop()) {
            return -1;
        }
        while (isLoop()) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow && flag == true) {
                break;
            }
            if (fast == slow && flag == false) {
                flag = true;
            }
            if (flag == true) {
                len++;
            }
        }
        return len;
    }
}

看看约瑟夫环的实现:

public class JosephLinkTest {
    public static void JosepLink(int n, int m) {
        DoubleLink link = new DoubleLink();
        // 对链表动态赋值,数据可以当做结点的序号来使用
        for (int i = 0; i < n; i++) {
            link.insertTail(i);
        }
        // 制作链表环
        link.makeLoop();
        // 定义开始指针指向第一个结点,计数器置1
        DoubleLink.Entry cur = link.head.next;
        int count = 1;
        while (link.getLoopLength() > 1) {
            // 当计数器等于约定的值时,删除对应结点
            if (count == m) {
                cur.prev.next = cur.next;
                cur.next.prev = cur.prev;
                cur = cur.next;
                count = 1;
            }
            cur = cur.next;
            count++;
        }
        // 输出剩余结点的值
        System.out.println(cur.data);
    }

    public static void main(String[] args) {
        JosepLink(1, 3);
    }

}

结果:0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值