数据结构--链表和递归

前面我们所学习的线性数据结构

1、动态数组

2、栈

3、队列

它们的底层都是依托于静态的数组所实现:靠resize解决固定容量的问题

一、链表

1、链表:真正的动态数据结构

 优点:不需要处理固定容量的问题,是真正的动态数据结构

缺点:丧失了随机访问的能力

2、创建Node

class Node<T> {
        T data; //结点本身的数据
        Node next; // 指向下个结点

        public Node(T data) {
            this.data = data;
            this.next = null;
        }

        public Node(T data, Node next) {
            this.data = data;
            this.next = next;
        }
    }

3、对链表进行增加操作

<1>链表头添加元素

<2>在链表中间添加元素

关键点:找到要待添加位置的前一个结点

<3> 在链表的尾部添加元素

4、使用虚拟头结点(解决在头部添加结点的特殊处理)

注意: 添加完成后,需要更新头节点

5、链表的属性和方法

 //链表相关的属性和方法
    private Node head; //链表的头

    private int size; //表示链表中有多少个结点

    public SelfLinkedList() {
        //创建一个虚拟头结点
        Node dummyHead = new Node(Integer.MIN_VALUE);
        this.head = dummyHead;
        this.size = 0;
    }

    //判断链表是否为空
    public boolean isEmpty() {
        return this.size == 0;
    }

    //向链表中添加结点
    //在头部添加
    public void addHead(T data) {
        add(0, data);
    }

    //在尾部添加
    public void addTail(T data) {
        add(this.size, data);
    }

    /* public void add(int index,T data){
         //判断index 位置是否有效
         if (index < 0 || index > this.size){
             throw new IllegalArgumentException("index is invaild");
         }

         //1、创建一个结点
         Node node = new Node(data);

         //特殊处理1,如果索引为0
         if (index == 0){
             this.head = new Node(data,this.head);
             this.size++;
             return;
         }


         //2、找前驱结点
         Node pre = this.head;
         for (int i = 1; i < index; i++) {
             pre = pre.next;
         }
         //3、开始进行结点的连接
         node.next = pre.next;
         pre.next = node;
         this.size++;
     }*/
    //在任意位置添加
    public void add(int index, T data) {
        //判断index 位置是否有效
        if (index < 0 || index > this.size) {
            throw new IllegalArgumentException("index is invaild");
        }

        //1、创建一个结点
        Node node = new Node(data);

        //2、找前驱结点
        Node pre = this.head;
        for (int i = 1; i <= index; i++) {
            pre = pre.next;
        }
        //3、开始进行结点的连接
        node.next = pre.next;
        pre.next = node;
        this.size++;
    }

    //重写toString , 打印整个链表中的元素
    @Override
    public String toString() {
        //从this.head 开始一直向后找
        Node curNode = this.head.next;
        StringBuilder sb = new StringBuilder();
        while (curNode != null) {
            sb.append(curNode.data + "--->");
            curNode = curNode.next;
        }
        sb.append("null");
        return sb.toString();
    }

    //从链表中查找指定的元素是否存在
    public boolean contains(T data) {
        //遍历链表时,头结点不能动
        Node curNode = this.head.next;
        while (curNode != null) {
            if (curNode.data.equals(data)) {
                return true;
            }
            curNode = curNode.next;
        }
        return false;
    }

    //获取链表中元素的个数
    public int getSize() {
        return this.size;
    }

    //获取链表的头结点
    public Optional getFirst() {
        if (this.head.next == null) {
            return Optional.empty();
        }
        return Optional.ofNullable(this.head.next.data);
    }

    //获取链表的尾结点
    public Optional<T> getLast() {
        if (this.head.next == null) {
            return Optional.empty();
        }
        //遍历链表时,头结点不能动
        Node<T> curNode = this.head;
        while (curNode.next != null) {
            curNode = curNode.next;
        }
        return Optional.of(curNode.data);
    }

    //从链表中获取任意位置的头结点
    public T get(int index) {
        if (index < 0 || index >= this.size) {
            throw new IllegalArgumentException("index is invalid");
        }
        Node<T> curNode = this.head.next;
        int i = 0;
        while (i < index) {
            curNode = curNode.next;
            i++;
        }
        return curNode.data;
    }

    // 从链表的头部删除元素
    public T removeHead() {
        if (this.isEmpty()) {
            return null;
        }
        Node<T> delNode = this.head.next;
        this.head.next = delNode.next;
        delNode.next = null;
        //更新size
        this.size--;
        return delNode.data;
    }

    // 从链表的尾部去删除元素
    public T removeTail() {
        if (this.isEmpty()) {
            return null;
        }
        Node<T> pre = this.head;
        while (pre.next.next != null) {
            pre = pre.next;
        }
        Node<T> delNode = pre.next;
        pre.next = delNode.next;
        delNode.next = null;
        //更新size
        this.size--;
        return delNode.data;
    }

    public void remove2(T val){
        //更新头结点
       this.head.next = recusionRemove(this.head.next,val);
    }

    // 使用递归的方式从链表中删除元素
    // 递归函数的语义,从以node为头结点的链表中删除元素value,返回值为删除之后链表的头结点
    private Node recusionRemove(Node node, T value) {
        //递归终止的条件
        if (node == null) {
            return null;
        }
        //递归操作
        if (node.data.equals(value)) {
            this.size--;
            return node.next;
        } else {
            node.next = recusionRemove(node.next, value);
            return node;
        }
    }

    // 删除任意位置的结点
    public T remove(int index) {
        if (index < 0 || index >= this.size) {
            throw new IllegalArgumentException("index is invalid");
        }
        //找删除结点的前驱
        Node<T> pre = this.head;
        int i = 0;
        while (i < index) {
            pre = pre.next;
            i++;
        }
        Node<T> delNode = pre.next;
        pre.next = delNode.next;
        delNode.next = null;
        this.size--;
        return delNode.data;
    }

    //根据值从链表中删除元素(使用虚拟头结点)
    public int removeByValue(T val) {
        int count = 0;

        // 定义前驱结点
        Node<T> pre = this.head;
        while (pre.next != null) {
            Node<T> curNode = pre.next;
            if (curNode.data.equals(val)) {
                pre.next = pre.next.next;
                curNode.next = null;
                this.size--;
                count++;
            } else {
                pre = pre.next;
            }
        }
        return count;
    }

    //根据值从链表中删除元素(不使用虚拟头结点)
    public int removeByValue2(T val) {
        int count = 0;
        Node<T> realHead = this.head.next;

        while (realHead != null && realHead.data.equals(val)) {
            realHead = realHead.next;
            this.size--;
            count++;
        }

        //判断链表是否为空
        if (realHead == null) {
            return count;
        }

        //假设头结点不是要删除的结点
        Node pre = realHead;
        while (pre.next != null) {
            Node<T> delNode = pre.next;
            if (delNode.data.equals(val)) {
                pre.next = delNode.next;
                delNode.next = null;
                this.size--;
                count++;
            } else {
                pre = pre.next;
            }
        }
        return count;
    }

6、从链表中删除元素

7、链表的时间复杂度分析

八、使用链表实现栈

  以链表作为栈的底层数据结构。

public class LinkedStack<T> implements Stack_I<T> {

    private SelfLinkedList<T> data;

    public LinkedStack(){
        this.data = new SelfLinkedList<>();
    }

    @Override
    public void push(T ele) {
        this.data.addHead(ele);
    }

    @Override
    public T pop() {

        try {
            return this.data.removeHead();
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public T peek() {
        return this.data.getFirst().orElse(null);
    }

    @Override
    public boolean isEmpty() {
        return this.data.isEmpty();
    }

    @Override
    public int getSize() {
        return this.data.getSize();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【leetcode C++】最小栈

leetcode 155. 最小栈 题目 设计一个支持 push &#xff0c;pop &#xff0c;top 操作&#xff0c;并能在常数时间内检索到最小元素的栈。 实现 MinStack 类: MinStack() 初始化堆栈对象。void push(int val) 将元素val推入堆栈。void pop() 删除堆栈顶部的元素。int top() 获…

2024.3.11

作业&#xff1a; #include <iostream> #include<iomanip> #include<string> using namespace std;int main() {string str; // array<char,128> a; // array<char,128>::iterator iter;cout << "请输入一个字符串:" <…

位运算#蓝桥杯

位运算#蓝桥杯 文章目录 位运算#蓝桥杯1、小蓝学位运算2、异或森林3、位移4、笨笨的机器人5、博弈论 1、小蓝学位运算 #include<bits/stdc.h> using namespace std; using LL long long; const LL N 1e97; template<int kcz> struct ModInt { #define T (*this)…

HubSpot和NETFARMER是什么关系?

HubSpot和NETFARMER之间的关系是合作伙伴关系&#xff0c;特别是在亚太地区。NETFARMER作为HubSpot的合作伙伴&#xff0c;专注于帮助企业在海外市场获得更多客户&#xff0c;实现业务增长和成功。 NETFARMER具备丰富的经验和专业的营销团队&#xff0c;他们深入了解亚太地区各…

如何在Linux使用Docker部署Firefox并实现无公网IP访问本地浏览器

文章目录 1. 部署Firefox2. 本地访问Firefox3. Linux安装Cpolar4. 配置Firefox公网地址5. 远程访问Firefox6. 固定Firefox公网地址7. 固定地址访问Firefox Firefox是一款免费开源的网页浏览器&#xff0c;由Mozilla基金会开发和维护。它是第一个成功挑战微软Internet Explorer浏…

YOLOv7_pose-Openvino和ONNXRuntime推理【CPU】

1 环境&#xff1a; CPU&#xff1a;i5-12500 Python&#xff1a;3.8.18 2 安装Openvino和ONNXRuntime 2.1 Openvino简介 Openvino是由Intel开发的专门用于优化和部署人工智能推理的半开源的工具包&#xff0c;主要用于对深度推理做优化。 Openvino内部集成了Opencv、Tens…

STM32串口:DMA空闲中断实现接收不定长数据(基于HAL库)

STM32串口&#xff1a;DMA空闲中断实现接收不定长数据&#xff08;基于HAL库&#xff09;&#xff1a; 第一步&#xff1a;设置rcc&#xff0c;时钟频率&#xff0c;下载方式 设置system core->RCC如图所示&#xff1a;&#xff08;即High Speed Clock和Low Speed Clock都选…

【企业动态】国际知名设备商来访东胜物联,考察交流

本周&#xff0c;国际知名设备商的技术及生产团队一行莅临东胜物联杭州总部和湖州生产工厂&#xff0c;进行参观考察&#xff0c;深入交流。该设备商的业务范围广泛&#xff0c;在全球各地拥有许多分公司&#xff0c;其中机器人和工业自动化设备等多项业务处于业界领先水平。 …

设计模式深度解析:工厂方法模式与抽象工厂模式的深度对比

​&#x1f308; 个人主页&#xff1a;danci_ &#x1f525; 系列专栏&#xff1a;《设计模式》 &#x1f4aa;&#x1f3fb; 制定明确可量化的目标&#xff0c;坚持默默的做事。 探索设计模式的魅力&#xff1a;工厂方法模式文章浏览阅读17k次&#xff0c;点赞105次&#xff0…

【棘手问题】Spring JPA一级缓存导致获取不到数据库表中的最新数据,对象地址不发生改变

【棘手问题】Spring JPA一级缓存导致获取不到数据库表中的最新数据&#xff0c;对象地址不发生改变 一、问题背景二、解决步骤2.1 debug2.2 原因分析2.2.1 数据步骤2.2.2 大模型解释2.2.3 解释举例2.2.4 关键函数 2.3 解决方案 三、Spring JPA一级缓存 一、问题背景 项目的数据…

CubeMX使用教程(5)——定时器PWM输出

本篇我们将利用CubeMX产生频率固定、占空比可调的两路PWM信号输出 例如PA6引脚输出100Hz的PWM&#xff1b;PA7引脚输出500Hz的PWM&#xff0c;双路同时输出 我们还是利用上一章定时器中断的工程进行学习&#xff0c;这样比较方便 首先打开CubeMX对PA6、PA7进行GPIO配置 注&a…

Python递归函数你用对了吗?

1.递归函数 递归函数&#xff1a;函数自己调用自己 2.需求 使用函数的方式&#xff0c;计算数字n的阶乘 # 5&#xff01; """ 5! 1 * 2 * 3 * 4 * 5 4! 1 * 2 * 3 * 4 3! 1 * 2 * 3 2! 1 * 2 1! 1综上可以总结出&#xff1a;n! n * (n - 1) "&qu…

Spring Cloud部署篇2——Docker Compose部署至CentOS云服务器

一、项目介绍 系统模块 com.mingink |--mingink-api // 接口模块 | └──mingink-api-system // 系统接口 |--mingink-common // 通用模块 | └──mingink-common-core // 系统接口 |--mingink-gateway…

Mybatis操作sql报错ibatis.binding.BindingException: Parameter ‘empId‘ not found.

你们好&#xff0c;我是金金金。 场景 在使用Mybatis操作sql语句过程当中&#xff0c;更新操作&#xff0c;报错信息如下&#xff1a;Caused by: org.apache.ibatis.binding.BindingException: Parameter ‘empId’ not found. Available parameters are [arg1, arg0, param1, …

链表中的经典问题——奇偶链表

奇偶链表 给定单链表的头节点 head &#xff0c;将所有索引为奇数的节点和索引为偶数的节点分别组合在一起&#xff0c;然后返回重新排序的列表。 第一个节点的索引被认为是 奇数 &#xff0c; 第二个节点的索引为 偶数 &#xff0c;以此类推。 请注意&#xff0c;偶数组和奇…

R语言绘制桑基图教程

原文链接&#xff1a;R语言绘制桑基图教程 写在前面 在昨天3月10日&#xff0c;我们在知乎、B站等分享了功能富集桑基气泡图的绘制教程。相关链接&#xff1a;NC|高颜值功能富集桑基气泡图&#xff0c;桑基气泡组合图。 确实&#xff0c;目前这个图在文章中出现的频率相对比较…

仿牛客网项目---Elasticsearch分布式搜索引擎

1.什么是ElasticSearch分布式搜索引擎&#xff1f; Elasticsearch是一个开源的分布式搜索引擎&#xff0c;提供实时的、高可用性的搜索和分析解决方案。它支持快速索引和搜索大规模数据&#xff0c;具有分布式架构、RESTful API、基于JSON的查询语言等功能&#xff0c;适用于各…

手机备忘录可以设置密码吗 能锁屏加密的备忘录

在繁忙的生活中&#xff0c;手机备忘录成了我随身携带的“小秘书”。那些关于工作的灵感、生活的琐事&#xff0c;甚至深藏心底的小秘密&#xff0c;都被我一一记录在里面。然而&#xff0c;每次当手机离开我的视线&#xff0c;或者需要借给他人使用时&#xff0c;我总会心生担…

力扣:118. 杨辉三角

力扣&#xff1a;118. 杨辉三角 描述 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例 2: 输…

根据xlsx文件第一列的网址爬虫(selenium)

seleniumXpath 在与该ipynb文件同文件下新增一个111.xlsx&#xff0c;第一列放一堆需要爬虫的同样式网页 然后使用seleniumXpath爬虫 from selenium import webdriver from selenium.webdriver.common.by import By import openpyxl import timedef crawl_data(driver, url)…