数据结构之链表(LinkedList)(三)

数据结构之链表(LinkedList)(二)

环形链表

顾名思义 环形列表是一个首尾相连的环形链表

示意图

循环链表的特点是无须增加存储量,仅对表的链接方式稍作改变,即可使得表处理更加方便灵活。

看一样著名的应用场景

我们就可以用环形单链表解决这个问题。

首先我们怎么构建一个环形链表

分析:

1. 先创建第一个节点, 让 first 指向该节点,并形成环形

2. 后面当我们每创建一个新的节点,就把该节点,加入到已有的环形链表中即可.

示意图:

代码:

// 创建一个Boy类,表示一个节点
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;
    }

}

节点代码

// 创建一个环形的单向链表
class CircleSingleLinkedList {
    // 创建一个first节点,当前没有编号
    private Boy first = null;

    // 添加小孩节点,构建成一个环形的链表
    public void addBoy(int nums) {  // nums代表要插入几个节点
        // nums 做一个数据校验
        if (nums < 1) {
            System.out.println("nums的值不正确");
            return;
        }
        Boy curBoy = null; // 辅助指针,帮助构建环形链表
        // 使用for来创建我们的环形链表
        for (int i = 1; i <= nums; i++) {
            // 根据编号,创建小孩节点
            Boy boy = new Boy(i);
            // 如果是第一个小孩
            if (i == 1) {
                first = boy;
                first.setNext(first); // 构成环
                curBoy = first; // 让curBoy指向第一个小孩
            } else {
                curBoy.setNext(boy);//
                boy.setNext(first);//
                curBoy = boy;
            }
        }
    }
}

代码

怎么遍历环形链表

分析

1. 先让一个辅助指针(变量) curBoy,指向first节点

2. 然后通过一个while循环遍历 该环形链表即可 curBoy.next == first 结束

示意图:

实现代码:

// 遍历当前的环形链表
    public void showBoy() {
        // 判断链表是否为空
        if (first == null) {
            System.out.println("没有任何小孩~~");
            return;
        }
        // 因为first不能动,因此我们仍然使用一个辅助指针完成遍历
        Boy curBoy = first;
        while (true) {
            System.out.printf("小孩的编号 %d \n", curBoy.getNo());
            if (curBoy.getNext() == first) {// 说明已经遍历完毕
                break;
            }
            curBoy = curBoy.getNext(); // curBoy后移
        }
    }

代码

这样就实现了一个环形链表的创建和遍历,回到上面的约瑟夫问题,k位置开始从1到m,m位置出队,然后在从1开始数,循环出队。

分析:

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

2.报数前先让 first 和 helper 移动 k - 1次

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

4.这时就可以将first 指向的小孩节点 出圈

first = first .next

helper.next = first

原来first 指向的节点就没有任何引用,就会被回收

示意图:

实现代码:

// 根据用户的输入,计算出小孩出圈的顺序
    /**
     *
     * @param startNo
     *            表示从第几个小孩开始数数
     * @param countNum
     *            表示数几下
     * @param nums
     *            表示最初有多少小孩在圈中
     */
    public void countBoy(int startNo, int countNum, int nums) {
        // 先对数据进行校验
        if (first == null || startNo < 1 || startNo > nums) {
            System.out.println("参数输入有误, 请重新输入");
            return;
        }
        // 创建要给辅助指针,帮助完成小孩出圈
        Boy helper = first;
        // 需求创建一个辅助指针(变量) helper , 事先应该指向环形链表的最后这个节点
        while (true) {
            if (helper.getNext() == first) { // 说明helper指向最后小孩节点
                break;
            }
            helper = helper.getNext();
        }
        //小孩报数前,先让 first 和  helper 移动 k - 1次
        for(int j = 0; j < startNo - 1; j++) {
            first = first.getNext();
            helper = helper.getNext();
        }
        //当小孩报数时,让first 和 helper 指针同时 的移动  m  - 1 次, 然后出圈
        //这里是一个循环操作,知道圈中只有一个节点
        while(true) {
            if(helper == first) { //说明圈中只有一个节点
                break;
            }
            //让 first 和 helper 指针同时 的移动 countNum - 1
            for(int j = 0; j < countNum - 1; j++) {
                first = first.getNext();
                helper = helper.getNext();
            }
            //这时first指向的节点,就是要出圈的小孩节点
            System.out.printf("小孩%d出圈\n", first.getNo());
            //这时将first指向的小孩节点出圈
            first = first.getNext();
            helper.setNext(first); //

        }
        System.out.printf("最后留在圈中的小孩编号%d \n", first.getNo());

    }
}

代码

public static void main(String[] args) {
        // 测试一把看看构建环形链表,和遍历是否ok
        CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
        circleSingleLinkedList.addBoy(5);// 加入5个小孩节点
        circleSingleLinkedList.showBoy();

        //测试一把小孩出圈是否正确
        circleSingleLinkedList.countBoy(1, 2, 5); // 2->4->1->5->3
        //String str = "7*2*2-5+1-5+3-3";
    }

main

小孩的编号 1
小孩的编号 2
小孩的编号 3
小孩的编号 4
小孩的编号 5
小孩2出圈
小孩4出圈
小孩1出圈
小孩5出圈
最后留在圈中的小孩编号3 

输出

原文地址:https://www.cnblogs.com/justBobo/p/10982548.html

时间: 2024-08-08 21:48:45

数据结构之链表(LinkedList)(三)的相关文章

数据结构之链表(LinkedList)(一)

链表(Linked List)介绍 链表是有序的列表,但是它在内存中是存储如下 1)链表是以节点方式存储的,是链式存储 2)每个节点包含data域(value),next域,指向下一个节点 3)各个节点不一定连续存储,如上图 4)链表分 带头节点的链表和 不带头节点的链表,根据实际需求确定 单链表介绍 单链表(带头结点) 逻辑结构示意图如下 应用实例 使用带head头的单向链表实现 –学生成绩录入管理   {学号,姓名,分数} 1.完成学生成绩的增删改查操作 2.第一种方法在添加英雄时,直接添加

数据结构与算法JavaScript (三) 链表

数据结构与算法JavaScript (三) 链表 我们可以看到在javascript概念中的队列与栈都是一种特殊的线性表的结构,也是一种比较简单的基于数组的顺序存储结构.由于 javascript的解释器针对数组都做了直接的优化,不会存在在很多编程语言中数组固定长度的问题(当数组填满后再添加就比较困难了,包括添加删除, 都是需要把数组中所有的元素全部都变换位置的,javascript的的数组确实直接给优化好了,如 push,pop,shift,unshift,split方法等等…) 线性表的顺序

[读书笔记]-大话数据结构-3-线性表(三)-静态链表、循环链表和双向链表

静态链表 对于没有指针的编程语言,可以用数组替代指针,来描述链表.让数组的每个元素由data和cur两部分组成,其中cur相当于链表的next指针,这种用数组描述的链表叫做静态链表,这种描述方法叫做游标实现法.我们对数组的第一个和最后一个元素做特殊处理,不存数据.让数组的第一个元素cur存放第一个备用元素(未被占用的元素)下标,而数组的最后一个元素cur存放第一个有值的元素下标,相当于头结点作用.空的静态链表如下图 当存放入一些数据时("甲""乙""丁&q

数据结构-List接口-LinkedList类-Set接口-HashSet类-Collection总结

一.数据结构:4种--<需补充> 1.堆栈结构:     特点:LIFO(后进先出);栈的入口/出口都在顶端位置;压栈就是存元素/弹栈就是取元素;     代表类:Stack;     其它:main方法最后一个出去; 2.数组结构:     特点:一片连续的空间;有索引,查找快;增删慢;     代表类:ArrayList;     应用场景:用于查询多的场景,如天气预报; 3.队列结构:     特点:FIFO(先进先出);入口/出口在两侧;     代表:Queue接口     应用场景

数据结构:链表(单链表)

一.链表 概念:链表[Linked List]是由一组不必相连(可连续可不连续)的内存结构(节点),按照特定的顺序链接在一起的抽象数据类型: 分类:链表常用的大概有三类:单链表.双向链表.循环链表(这篇文章主要讲单链表) 操作:链表的核心操作主要有三个(查找遍历.插入.删除) 二.单链表 上图就是一个单链表的数据结构了,从上图可以看出,每个节点包含两个重要的元素(data.next),那么什么是单链表呢? 概念:各个内存结构通过一个Next指针链接在一起,每个内存结构都存在后续的内存结构(链尾除

基本数据结构:链表(list)

copy from:http://www.cppblog.com/cxiaojia/archive/2012/07/31/185760.html 基本数据结构:链表(list) 谈到链表之前,先说一下线性表.线性表是最基本.最简单.也是最常用的一种数据结构.线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的.线性表有两种存储方式,一种是顺序存储结构,另一种是链式存储结构. 顺序存储结构就是两个相邻的元素在内存中也是相邻的.这种存储方式的优点是

例说Linux内核链表(三)

经常使用的linux内核双向链表API介绍 linux link list结构图例如以下: 内核双向链表的在linux内核中的位置:/include/linux/list.h 使用双向链表的过程,主要过程包括创建包括struct link_head结构的结构体(item),建立链表头.向链表中加入item(自己定义数据结构.双向链表数据单元).删除链表节点.遍历链表,判空等. 1.建立自己定义链表数据结构 struct kool_list{ int to; struct list_head li

用结点实现链表LinkedList,用数组和结点实现栈Stack,用数组和结点链表实现队列Queue

一,用结点实现链表LinkedList,不用换JavaAPI的集合框架 import java.util.Scanner; public class Main { public static class Node { int data; Node next=null; public Node(int data){this.data=data;}; } public static class MyLinkedList { Node head=null; public MyLinkedList()

数据结构:单向链表系列6--交换相邻两个节点1(交换数据域)

给定一个单向链表,编写函数交换相邻 两个元素 输入: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 输出: 2 -> 1 -> 4 -> 3 -> 6 -> 5 -> 7 输入: 1 -> 2 -> 3 -> 4 -> 5 -> 6 输出: 2 -> 1 -> 4 -> 3 -> 6 -> 5 通过观察发现:当输入的与元素个数是单数的时候,最后一位不参与交换