丢手帕问题

来源:互联网 发布:mini metro mac破解 编辑:程序博客网 时间:2024/04/28 23:34

最近看到一道经典的题目,就是丢手帕问题。这道题目在多年前曾看到过,但当时有没有看过或者思考过解答,已经完全不记得了。于是从头开始实现。

问题是这样的:有N个小朋友围成一圈,老师让报数,从1报到M。凡是报到M的小朋友站出圈。假设小朋友们的ID从0到N-1,请问最后一个留在圈中的小朋友的ID是多少?

先想用数组,那么为了实现出圈这个操作,就需要把全数组默认值设为1,出圈的位置设为0.这样理论上应该没问题,但增加了对0和1的判断。又想了下,发现刚好可以用环形链表来做:每次出圈只要把相应节点delete掉就可以了,很省事。于是开始写程序。写了一会儿,又重新整理了一下思路,有程序如下:

#include <iostream>using namespace std;struct Node {    int value;    Node * next;};    Node * createCircleLinkedList(int size) {    Node * head = nullptr;    Node * previous = head;        for (int i=0; i<size; ++i) {        Node * tmp = new Node;        tmp->value = i;                if (i==0) {            previous = head = tmp;        }        else {            previous->next = tmp;            previous = tmp;            if (i == size - 1) {                tmp->next = head;            }        }    }        return head;}void printCircleLinkedList(Node * head) {    if (head == nullptr) return;        Node * tmp = head;    while (tmp->next != head) {        cout << tmp->value << " ";        tmp = tmp->next;    }    cout << tmp->next->value << endl;}int getLastOne(Node * head, int size, int interval) {    if (size <= 0) {        return 0;    }    if (size == 1) {        return head->value;    }        if (interval <= 0) {        return 0;    }    if (interval == 1) {        return size - 1;    }        // p1代表当前节点的前一个节点,p2代表当前节点,p3代表当前节点的下一个几点    Node *p1, *p2, *p3;    p1 = head;    p2 = head->next;     p3 = p2->next;        // 每次总删除当前节点p2,故当p2是head->next时,相当于报数2了。    // 注意,若interval为1,则直接返回size-1即可。    int count = 2;          int result = -1;    while (p3->next != p3) {  // 循环结束的条件:只剩最后一个节点了,必然是next节点仍是自己。        if (count == interval) {            p1->next = p3;            cout << "Deleted ID: "  << p2->value << endl;            delete p2;            p2 = p3;                        if (p3->next != p3) {                p3 = p3->next;            }            else {                result = p3->value;                delete p3;                break;            }                        count = 1; // 做删除后,p2处于下一个位置了,故相当于报数为1了。        }                p1 = p2;        p2 = p3;        p3 = p3->next;        count ++;    }        return result;}void deleteCircleLinkedList(Node * head) {    if (head == nullptr) {        return;     }    else if (head->next == head) {        delete head;        return;    }        Node *p1, *p2;    p1 = head->next;     p2 = p1->next;        while (p2->next != p2) {        cout << "Delete " << p1->value << endl;        delete p1;        head->next = p2;        p1 = p2;        p2 = p2->next;    }    cout << "Delete " << p2->value << endl;    delete p2;}int main(){    int size = 20;    int interval = 3;        Node * head = createCircleLinkedList(size);    printCircleLinkedList(head);    // deleteCircleLinkedList(head);            int lastOne = getLastOne(head, size, interval);    cout << "Last One is " << lastOne << endl;    return 0;}

执行结果如下:

Deleted ID: 2Deleted ID: 5Deleted ID: 8Deleted ID: 11Deleted ID: 14Deleted ID: 17Deleted ID: 0Deleted ID: 4Deleted ID: 9Deleted ID: 13Deleted ID: 18Deleted ID: 3Deleted ID: 10Deleted ID: 16Deleted ID: 6Deleted ID: 15Deleted ID: 7Deleted ID: 1Deleted ID: 12Last One is 19

这个程序带给我一点点启示:对链表做一些操作的时候,如果写出p1, p2, p3这样的形式,往往对于理清思路比较有好处。除了本程序之外,在本程序中的写出来却并没有被用到的函数deleteCircleLinkedList()也是个例子,它是用来删除一个环形链表的。


1 0
原创粉丝点击