循环链表--约瑟夫环问题

传说在公元1 世纪的犹太战争中,犹太历史学家弗拉维奥·约瑟夫斯和他的40 个同胞被罗马士兵包围。犹太士兵决定宁可自杀也不做俘虏,于是商量出了一个自杀方案。他们围成一个圈,从一个人开始,数到第三个人时将第三个人杀死,然后再数,直到杀光所有人。约瑟夫和另外一个人决定不参加这个疯狂的游戏,他们快速地计算出了两个位置,站在那里得以幸存。写一段程序将n 个人围成一圈,并且第m个人会被杀掉,计算一圈人中哪两个人最后会存活。使用循环链表解决该问题。

说了这么一大堆,其实最重要的就是最后两句,n个人围成一圈,很明显这就是循环了,第m个人会被杀掉,圈中最后会剩两个人。

根据上述的问题,我们可以知道:n=41,m=3,循环链表中一共有41个人,第3个人被杀掉后就从链表中移除,当链表中只剩两个人时,就返回剩下的两个人的位置。

首先,我们要先实现循环链表的一些基本方法:

1、定义Node对象

function Node(element) {
    this.element = element;
    this.next = null;
    this.previous = null;
}

2、找到最后的节点

首先定义一个LList函数,在函数内部定义头结点:

this.head = new Node('head');

当然咯,后面的这些循环链表的函数全部都是定义在LList函数内的。

this.lastNode = function () {
        if (this.head.next == null) {
            return this.head;
        } else {
            var lastNode = this.head;
            while (lastNode.next.element != 'head') {
                lastNode = lastNode.next;
            }
            return lastNode;
        }
    }

3、插入节点

this.insert = function (element) {
        //1,创建新的节点
        var node = new Node(element);
        //2,获得最后的节点
        var lastNode = this.lastNode();
        //3,和最后的节点连在一起
        lastNode.next = node;
        node.previous = lastNode;
        //新的节点就是最后的节点
        this.head.previous = node;
        node.next = this.head;
    }

4、查找节点

如果头结点的下一个节点为空,说明循环链表中就只有头结点一个节点,直接返回null;

如果头结点的下一个节点不为空,让node等于头结点的下一个节点,如果node的元素不等于要查找元素并且不等于头结点元素,就让当前节点等于当前节点的下一个节点,即node=node.next;

如果经过上述循环后,最后的node节点等于头结点,说明已经循环了链表一次,没有节点元素符合我要找的元素,返回为空;否则,就返回节点元素node。

this.find = function (element) {
        if (null == this.head.next)
            return null;
        var node = this.head.next;
        while (node.element != element && node.element != 'head') {
            node = node.next;
        }

        if (node.element == 'head') {
            return null;
        } else {
            return node;
        }
    }

5、移除节点

首先,找到当前元素的节点,例如循环链表   1      2      3       4

若是我现在需要移除2,那么首先就要找到2所在的节点,所以现在node节点为2;

在移除2之后,我需要做什么,是不是应该把1和3连接起来,所以说,2的前一个节点的后一个节点应该是2的后一个节点,也就是说,2的前一个节点是1,2的后一个节点是3,现在1的后一个节点是不是指向3,这个是没有问题的,即node.previous.next=node.next;后面的也是一样,2后一个节点的前一个节点等于2的前一个节点。总之,这里总结起来就一句话:1指向3,3指向1。我的目的也只是把1和3连接起来。

this.remove = function (element) {
        //1,获得当前的节点
        var node = this.find(element);

        // var previousNode = node.previous;
        // var nextNode = node.next;
        // previousNode.next=nextNode;
        // nextNode.previous=previousNode;

        node.previous.next = node.next;
        node.next.previous = node.previous;
    }

6、遍历节点

this.forEach = function (call) {
        if (this.head.next == null)
            return;
        var node = this.head.next;
        while (node != null && node.element != 'head') {
            call(node);
            node = node.next;
        }
    }

7、节点个数

如果说头结点的下一个节点元素不等于头结点元素,就让当前节点等于下一个节点,直到循环到头结点为止。

this.Count = function () {
        var node = this.head;
        var i = 0;
        while (node.next.element != 'head') {
            node = node.next;
            i++;
        }
        return i;
    }

好了,这里我们也只是简单的例举了我们所要用的方法。对于约瑟夫环这个问题,我是将循环链表作为一个js外部引入的,毕竟还有很多其他地方会调用。

为了解决这个问题,我定义了一个Survive函数,通过for循环调用循环链表的插入节点insert方法,将41个人放到循环链表中:

function Survive(num) {
        for(var i=1;i<=num;i++){
            list.insert(i);
        }
    }

    var list = new LList();
    Survive(41);

接下来,就通过遍历循环链表中的数据,数到第3个人时移除节点,即index==3;移除节点后,将index重新置为0,如此循环下去,直到循环链表中的数据只剩下2个,就输出循环链表中的数据。这里的Count方法就是循环链表中的节点个数。

    var index=0;
    var m=3;

    while(list.Count()>2){
        list.forEach(function (person) {
            index++;
            if(index==m){
                list.remove(person.element);
                index=0;
            }
        })
    }

现在,就只差把数据输出了,我们只需把循环链表剩下的数据遍历出来即可:

 list.forEach(function (person) {
        console.log(person.element);
    })

猜你喜欢

转载自blog.csdn.net/weixin_38629529/article/details/81168559