【数据结构与算法】5、循环链表、约瑟夫问题、静态链表

news2025/1/13 19:51:16

循环链表目录

  • 一、单向循环链表
    • (1) add()
    • (2) remove()
    • (3) 单向循环链表特点
  • 二、双向循环链表
  • 三、约瑟夫问题(Josephus Problem)
  • 四、静态链表

一、单向循环链表

🌿 单向循环链表在单链表的基础上,尾节点的 next 指向头节点

在这里插入图片描述

(1) add()

🌿 只用考虑添加头节点的情况
🌿 要考虑一个节点都没有,插入第一个节点的情况

  @Override
  public void add(int index, E element) {
      rangeCheck4Add(index);

      if (index == 0) { // 把元素插入到头节点的位置
          Node<E> head = new Node<>(element, first);

          // 拿到尾节点
          Node<E> tail = (size == 0) ? head : node(size - 1);
          tail.next = head;
          first = head; // 头指针指向头节点
      } else {
          // 拿到【index - 1】位置的节点
          Node<E> prev = node(index - 1);
          prev.next = new Node<>(element, prev.next);
      }

      size++;
  }

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

🌿 假如链表中一个节点都没有的时候,插入的第一个节点的 next 指向它本身

(2) remove()

🌿 只用考虑删除头节点的情况
🌿 假如只有一个节点,且要删除该节点的时候,只需要让头指针 first 指向为 null 即可

  @Override
  public E remove(int index) {
      rangeCheck(index);

      Node<E> node = first;

      if (index == 0) { // 删除头节点
          if (size == 1) {
              first = null;
          } else {
              // 拿到尾节点
              // node() 方法中使用到了 first 头指针
              Node<E> tail = node(size - 1);

              first = node.next;
              // 尾节点指向头节点
              tail.next = first;
          }
      } else {
          Node<E> prev = node(index - 1);
          node = prev.next;

          prev.next = node.next;
      }

      size--;

      return node.element;
  }

在这里插入图片描述

在这里插入图片描述

🌿 当要删除的链表中只有一个节点的时候,直接让头指针 first 等于 null

(3) 单向循环链表特点

在这里插入图片描述

🌱 相比单链表的尾节点的 next 指向 null 而言,单向循环链表的尾节点的 next 是指向头节点
🌱 这样一来的话,从单向循环链表的任意一个节点出发都可以获取到整个链表中的全部节点

二、双向循环链表

在这里插入图片描述

🍀 双向循环链表本身也是双向链表
🍀 双向循环链表的头节点的 prev 指向尾节点
🍀 双向循环链表的尾节点的 next 指向头节点

在这里插入图片描述
代码太恶心了: 🤮🤧

/**
 * 双向循环链表
 */
public class DoubleCircleLinkedList<E> extends AbstractList<E> {
    private Node<E> first; // 头指针
    private Node<E> last; // 尾指针

    @Override
    public E get(int index) {
        return node(index).element;
    }

    @Override
    public E set(int index, E element) {
        Node<E> node = node(index);
        E old = node.element;
        node.element = element;
        return old;
    }

    @Override
    public void add(int index, E element) {
        rangeCheck4Add(index);

        if (size == 0 || (first == null && last == null)) { // 添加第一个节点
            Node<E> head = new Node<>(element, null, null);
            head.prev = head;
            head.next = head;
            first = last = head;
        } else {
            if (index == size) { // 往最后插入新节点
                Node<E> oldLast = last;
                last = new Node<>(element, oldLast, null);
                oldLast.next = last;
                // 新节点的 next 指向头节点
                last.next = first;
                // 头节点的  prev 指向新节点
                first.prev = last;
            } else {
                Node<E> next = node(index);
                Node<E> prev = next.prev;
                Node<E> newNode = new Node<>(element, prev, next);

                next.prev = newNode;
                prev.next = newNode;

                if (index == 0) {
                    first = newNode; // 头指针指向新节点
                }
            }
        }

        size++;
    }

    @Override
    public E remove(int index) {
        rangeCheck(index);

        Node<E> node = null;

        if (size == 1) { // 删除的链表中只有一个节点的时候
            node = first;
            first = null;
            last = null;
        } else {
            node = node(index);
            Node<E> prev = node.prev;
            Node<E> next = node.next;

            prev.next = next;
            next.prev = prev;

            if (index == 0) { // 删除头节点
                first = next;
            }

            if (index == (size - 1)) { // 删除尾节点
                last = prev;
            }
        }

        size--;

        return node.element;
    }

    @Override
    public int indexOf(E element) {
        return 0;
    }

    @Override
    public void clear() {
        size = 0;
        first = null;
        last = null;
    }

    private static class Node<E> {
        E element;
        Node<E> prev;
        Node<E> next;

        Node(E element, Node<E> prev, Node<E> next) {
            this.element = element;
            this.prev = prev;
            this.next = next;
        }

        @Override
        public String toString() {
            if (prev == null) {
                return "null" + " ←【" + element + "】→ " + next.element + ", ";
            }

            if (next == null) {
                return prev.element + " ←【" + element + "】→ " + "null";
            }

            return prev.element + " ←【" + element + "】→ " + next.element + ", ";
        }
    }

    /**
     * 根据索引找节点
     */
    private Node<E> node(int index) {
        rangeCheck(index);

        if (index < (index >> 1)) { // 找左边的节点
            Node<E> node = first;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
            return node;
        } else {
            Node<E> node = last;
            for (int i = size - 1; i > index; i--) {
                node = node.prev;
            }
            return node;
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("{size=").append(size).append(", [");

        Node<E> moveNode = first;

        for (int i = 0; i < size; i++) {
            sb.append(moveNode);
            moveNode = moveNode.next;
        }

        sb.append("]}");
        return sb.toString();
    }

}

三、约瑟夫问题(Josephus Problem)

在这里插入图片描述

在这里插入图片描述

❄️ 在双向循环链表的基础上增加1个成员变量、3个方法,以便解决约瑟夫问题
❄️ current 成员变量:用于指向某个节点
❄️void reset():让 current 指向头节点 first
❄️ E next():让 current 往后走一步(current = current.next
❄️E remove():删除 current 指向的节点,删除成功后让 current 指向下一个节点,并返回被删除的节点值

四、静态链表

🌱 前面所学习的链表,是依赖于指针(引用)实现的
🌱 有些编程语言是没有指针的,比如早期的 BASIC、FORTRAN 语言

🌱 没有指针的情况下,如何实现链表?

  • 可以通过数组来模拟链表,称为静态链表
  • 数组的每个元素存放 2 个数据:① 值、② 下个元素的索引
  • 数组 0 位置存放的是头节点信息

在这里插入图片描述

在这里插入图片描述

❓ 如果数组的每个元素只能存放 1 个数据呢 ❓
🌱 使用 2 个数组,1 个数组存放索引关系,1 个数组存放值

基于这种思路可以对 ArrayList 做进一步优化


  • 我学习数据结构与算法的全部代码:https://gitee.com/zgq666good/datastructureandalgorithm.git
  • 学习资料来自于我偶像 ~ 李明杰(小码哥教育)
    在这里插入图片描述

🌿如有错误,请不吝赐教🌿

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/693642.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

欺诈无所遁形:反欺诈(羊毛盾)API 应用解析

随着互联网的快速发展&#xff0c;欺诈行为不断演变和扩大&#xff0c;涉及的领域也越来越广泛。虚假账户注册、刷单、恶意评论、虚假广告等欺诈手段成为一些不法分子获取利益的途径。这些行为不仅损害了用户的利益&#xff0c;也对在线平台的声誉和可信度造成了威胁。为了解决…

为什么推荐用易模来制作真人手办所需的人像模型?

要问最近什么手办最流行&#xff0c;真人手办必须拥有姓名。大家看腻了传统的动漫以及游戏的周边手办以后&#xff0c;想要玩出点新花样&#xff0c;于是纷纷把眼光放到真人的身上&#xff0c;真人手办开始应运而生。 现阶段制作真人手办通常都是线下来进行&#xff0c;需要消费…

Docker安装pritunl

Background Pritunl是一款图形化的OpenVPN软件&#xff0c;提供一个友好的图形WebUI界面&#xff0c;对传统OpenVPN服务端与客户端复杂的安装配置流程进行了极大的简化&#xff0c;并且通过图形界面可以进行人员管理、参数配置&#xff0c;给予用户非常大的便捷性&#xff0c;适…

【运维】Windows 通过注册表禁用服务

【运维】Windows 通过注册表禁用服务 以这个服务为例子 Windows Push Notifications User Service 双击查看服务名称 WpnUserService_671f3 打开注册表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\{服务名称} HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Ser…

网络基础一:网络协议初识与网络传输基本流程

目录 网络协议认识“协议”网络协议初识协议分层OSI七层模型&#xff08;理论模型&#xff09;TCP/IP五层(或四层)模型&#xff08;工程实现模型&#xff09; 网络中的地址管理MAC地址IP地址 网络传输基本流程路由的本质 数据包封装和分用网络协议需要解决的问题 网络协议 计算…

#10041. 「一本通 2.1 练习 7」门票

题目 题目描述 RPK 要带 MSH 去一个更加神秘的地方&#xff01; RPK 带着 MSH 穿过广场&#xff0c;在第 1618 块砖上按下了一个按钮&#xff0c;在一面墙上随即出现了一个把手。RPK 握住把手&#xff0c;打开了一扇石质大门。他们穿过悠长而芬芳的小道&#xff0c;走到了一扇…

基于Vue3+Vite+TS+ESLint+Prettier+Husky+lint-staged+commitlint+stylelint的项目构建

博客后台管理系统使用后的是基于Vue3ViteTSESLintPrettier的开发&#xff0c;具体项目构建如下 1、基于Vite创建vue-ts模板的项目骨架 pnpm create vite 项目名称 --template vue-ts2、安装ESLint、Prettier相关的 ESLint: 控制代码质量 Prettier: 控制代码风格 2.1、安装ESLi…

ASEMI代理NXP高压三端双向可控硅BT139-800E综合指南

编辑-Z BT139-800E是一种高压三端双向可控硅开关&#xff0c;近年来由于其卓越的性能和多功能性而广受欢迎。这种强大的半导体器件广泛应用于各种应用&#xff0c;包括电机控制、照明控制和温度调节。 BT139-800E的特点 1.高压能力&#xff1a;BT139-800E设计用于处理高压&am…

Vue Router query 命名路由 params props

6.5.路由的 query 参数 传递参数<!-- 跳转并携带query参数&#xff0c;to的字符串写法 --> <router-link :to"/home/message/detail?id${m.id}&title${m.title}">跳转</router-link><!-- 跳转并携带query参数&#xff0c;to的对象写法&am…

1753_使用Perl修改文件时间戳

全部学习汇总&#xff1a; GreyZhang/perl_basic: some perl basic learning notes. (github.com) 对于使用软件对文件进行造假的手段我一直感兴趣&#xff0c;我很想知道那些人是通过什么手段修改的文件属性。一直以来&#xff0c;我觉得修改文件的时间戳是一个很难的工作&…

Altium Designer23 设计备忘

1、新建工程 2、新建原理图 3、新建PCB 4、添加元器件SCHLIB&#xff0c;PCBLIB库 5、绘制原理图 6、给元器件添加封装 7、原理图更新至PCB 8、绘制PCB

[State of GPT] OpenAI讲座随笔记

原版&#xff1a;State of GPT B站翻译版&#xff1a;【精校版】Andrej Karpathy微软Build大会精彩演讲&#xff1a; GPT状态和原理 - 解密OpenAI模型训练 1 GPT Training Pipeline图解 记录一下对这个图的理解&#xff1a; 大模型训练的四个阶段&#xff1a; Pretraining 阶…

开发者活动:云原生的开源 AI 大模型基础设施

随着 ChatGPT 的火热&#xff0c;大语言模型和相关应用不断涌现。你是否了解大语言模型的技术细节&#xff1f;你是否曾经开发过大语言模型应用&#xff1f;如果你对大语言模型背后的基础设施感兴趣&#xff0c;那么7月8号&#xff0c;北京海淀中关村创业大街&#xff0c;云原生…

chatgpt赋能python:Python调用关系图:了解你的代码依赖关系

Python调用关系图&#xff1a;了解你的代码依赖关系 Python是一种强大的编程语言&#xff0c;具有易读易写、开放源代码、多功能等优点。但是在开发大型项目时&#xff0c;代码会变得复杂&#xff0c;不同模块之间的依赖关系也会变得混乱。为了更好地了解你的代码依赖关系&…

Openresty原理概念篇(九)LuaJIT分支和标准Lua有什么不同

一 LuaJIT分支和标准Lua有什么不同 ① 背景 luajit官方 标准lua官方 openresty自身维护的luajit 编译luajit ② LuaJIT 在 OpenResty 整体架构中的位置 ③ 标准 Lua 和 LuaJIT 的关系 ④ 为什么选择LuaJIT ⑤ lua特别之处 1&#xff09;Lua 的下标从1开始 2) 使用…

Java HelloWorld

一、java命令 javac&#xff1a;将.java文件编译成.class文件 cp - 指定class搜索路径 d - 指定class文件生成目录 java: 执行.class或.jar文件 cp指定class搜索路径 示例&#xff1a;java test1.app #表示执行./test1/app.class文件main函数 jar:打包生成.jar文件 v- 可视化输…

星星之火,可以燎原——关于太赫兹的技术进展

盼望着&#xff0c;盼望着&#xff0c;5G来了&#xff0c;6G的脚步也近了。除了做好现有的技术工作&#xff0c;作为通信人还要不断关注新技术的发展&#xff0c;真心不易&#xff01;无线数据链路的容量在过去十几年中呈指数级增长&#xff0c;但对更高数据速率的需求持续增加…

MySQL数据库优化技术一

纵论 对mysql优化时一个综合性的技术&#xff0c;主要包括 表的设计合理化(符合3NF)添加适当索引(index) [ 四种: 普通索引、主键索引、唯一索引unique、全文索引 ]分表技术( 水平分割、垂直分割 ) 水平分割根据一个标准重复定义几个字段值相同&#xff0c;表名称不同的表&…

Android Jetpack Compose之ModalBottomSheet的使用

Android Jetpack Compose是一个现代化的UI工具包&#xff0c;让开发者以声明式的方式来构建Android应用。今天我们要讨论的是其中一个重要组件——ModalBottomSheet。 1. ModalBottomSheet简介 ModalBottomSheet是Jetpack Compose中的一个组件&#xff0c;它允许我们从屏幕底部…

Hugging News #0626: 音频课程更新、在线体验 baichuan-7B 模型、ChatGLM2-6B 重磅发布

每一周&#xff0c;我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新&#xff0c;包括我们的产品和平台更新、社区活动、学习资源和内容更新、开源库和模型更新等&#xff0c;我们将其称之为「Hugging News」&#xff0c;本期 Hugging News 有哪些有趣的消息…