数据结构与算法从零开始------------------环形单链表(约瑟夫环)

一.回顾导学

在第一章时,我们简单提到了约瑟夫环(即丢手帕)的问题:

想必,这个游戏我们小时候或多或少都接触过,现在把它转换为代码实现,

即Josephu  问题:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。

二.约瑟夫环问题解决

2.1逻辑分析

1)根据用户的输入,生成一个小孩出圈的顺序,现在方便理解先假设为下列: 

n = 5 , 即有5个人

k = 1, 从第一个人开始报数

m = 2, 数2下

2)需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点.

3) 当小孩报数时,让first 和 helper 指针同时移动  m  - 1 次

4) 这时就可以将first 指向的小孩节点 出圈 first = first .next helper.next = first   原来first 指向的节点就没有任何引用,就会被回收 出圈的顺序 2->4->1->5->3

注意: 小孩报数前,先让 first 和  helper 移动 k - 1次。因为我们假设的k = 1,即刚好不需要移动,在实际问题中还是要把这个问题考虑进去

2.2代码实现

package zy.circleList;

import java.util.Scanner;

/**
 * @author 十三
 * @version 1.0
 */
public class Josephu {
    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        CircleList list = new CircleList();
        list.circle(N);
//        list.show();
        list.count(1,3,N);

    }
}

//创建一个环形单链表
class CircleList {
    private Boy first = null;//创建第一个节点,目前没有编号

    //创建一个方法创建环形单链表
    public void circle(int num) {
        if (num < 1) {//为空的话不满足
            return;
        }
        Boy cur = null;//辅助指针帮助构建环形单链表
        for (int i = 1; i <= num; i++) {
            Boy boy = new Boy(i);
            if (i == 1) {
                first = boy;
                first.setNext(first);
                cur = first;
            } else {
                cur.setNext(boy);
                boy.setNext(first);
                cur = boy;
            }

        }
    }

    public void show() {
        if (first == null) {
            return;
        }
        Boy cur = first;
        while (true) {
            System.out.printf("小孩的编号%d\n", cur.getNo());

            if (cur.getNext() == first) {
                break;
            }
            cur = cur.getNext();

        }
    }

    /**
     * @param start 从第几个小孩开始
     * @param count 步长,
     * @param sums  总共有几个小孩
     */
    public void count(int start, int count, int sums) {
        if (first == null || start < 1 || start > sums) {
            return;
        }
        Boy temp = first;
        while (true) {
            if (temp.getNext() == first){//转一圈了
                break;
            }
            temp = temp.getNext();
        }
        //将小孩移到start
        for (int i = 0; i < start - 1; i++) {
            first = first.getNext();
            temp = temp.getNext();
        }
        while (true){
            if (temp == first){//环形链表只剩一个了
                break;
            }
            for (int i = 0; i < count - 1; i++) {
                first = first.getNext();
                temp = temp.getNext();
            }
            System.out.printf("小孩%d出圈\n",first.getNo());
            first = first.getNext();
            temp.setNext(first);
        }
        System.out.printf("%d留圈\n",first.getNo());
//        System.out.println(first.getNo());
    }


}


class Boy {
    private int no;//编号
    private Boy next;//指向下一个节点,默认为null

    public Boy(int no) {
        this.no = no;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public Boy getNext() {
        return next;
    }

    public void setNext(Boy next) {
        this.next = next;
    }
}

可以注意到,我们直接初始化的从一开始,步长为3,有多少个小孩是我们自己输入的,其实如果需要的话,start 和count也可以自己接受参数,只是刚好我们pta上有一个约瑟夫环的问题,它的要求就是从1 开始,步长为3,

但好像pta上只允许有一个class(哎,欲哭无泪)

2.3结果验证

经验证正确~

三.小结

1)需要注意的是: 小孩报数前,记得先让 first 和  helper 移动 k - 1次。因为刚开始假设的k =1,写代码时忘记了移动了,想当然的直接进行出圈判断了,

2)还是导学里说的,代码思想来源于生活中的实际问题!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单链表是一种常见的数据结构,它由一个或多个节点组成,每个节点包含一个数据域和一个指针域,指针域指向下一个节点。 实现单链表的查找位序算法函数需要以下步骤: 1. 定义一个指针p指向链表的头节点,并定义一个变量count,用于计数。 2. 从头节点开始,遍历链表,当p指向某个节点时,计数器count加1。 3. 如果p指向的节点的数据与目标数据相等,则返回当前的计数器count,即为目标数据的位序。 4. 如果p指向的节点不是目标数据,则将p指向下一个节点,重复步骤3。 5. 如果遍历完链表后仍未找到目标数据,则返回-1,表示未找到。 下面是C语言实现单链表查找位序算法函数的代码示例: ```c #include <stdio.h> #include <stdlib.h> // 定义单链表节点结构 typedef struct Node { int data; // 数据域 struct Node* next; // 指针域 } Node; // 查找位序的算法函数 int findPosition(Node* head, int target) { Node* p = head; // 指向头节点 int count = 0; // 计数器初始化为0 while (p != NULL) { count++; // 计数器加1 if (p->data == target) { return count; // 找到目标数据,返回当前计数器的值 } p = p->next; // 指向下一个节点 } return -1; // 遍历完链表未找到目标数据,返回-1 } int main() { // 创建链表 Node* head = (Node*)malloc(sizeof(Node)); head->data = 1; // 头节点数据为1 Node* node1 = (Node*)malloc(sizeof(Node)); node1->data = 2; Node* node2 = (Node*)malloc(sizeof(Node)); node2->data = 3; head->next = node1; node1->next = node2; node2->next = NULL; // 查找位序示例 int target = 3; // 目标数据为3 int position = findPosition(head, target); if (position != -1) { printf("目标数据 %d 的位序为 %d\n", target, position); } else { printf("未找到目标数据 %d\n", target); } // 释放链表内存 free(node2); free(node1); free(head); return 0; } ``` 在上述代码中,我们首先定义了一个指向头节点的指针p和一个计数器count,然后使用while循环遍历链表。当p指向某个节点时,计数器加1,并判断该节点的数据是否与目标数据相等。如果找到了目标数据,则返回当前计数器的值,即为目标数据的位序。如果遍历完链表仍未找到目标数据,则返回-1表示未找到。最后在主函数中演示了调用该算法函数的示例。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值