数据结构入门到入土——链表(1)

news2024/11/15 18:53:30

目录

一,顺序表表/ArrayList的缺陷

二,链表

三,链表的实现

四,与链表有关的题目练习(1)

1.删除链表中等于给定值 val 的所有节点

2.反转一个单链表

3.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点

4.输入一个链表,输出该链表中倒数第k个结点

5.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的

6.编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前

7.链表的回文结构


一,顺序表表/ArrayList的缺陷

在上期我们已经熟悉了ArrayList的使用,并且进行了简单模拟实现。通过源码我们知道ArrayList底层使用数组来存储元素。

由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后 搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即 链表结构

二,链表

如果说顺序表其底层在物理意义上是一段连续的空间,那么链表便是是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。

顺序表是一段连续不可分的空间:

链表像一列火车一样,多个节点被串成一块具有连续性意义的结构,实际上各个节点都来自于不同的地址:

实际中链表的结构非常多样
单向或者双向:
有头或者没头:
循环或者非循环:
虽然有这么多的链表的结构,但是我们重点掌握两种:
•    无头单向非循环链表 结构简单 ,一般不会单独用来存数据。实际中更多是作为 其他数据结构的子结构 ,如哈希桶、图的邻接表等等。
•    无头双向链表 :在 Java 的集合框架库中 LinkedList 底层实现就是无头双向循环链表。

三,链表的实现

和上期顺序表一样,我们采用接口的方式实现:

接口部分:

// 1、无头单向非循环链表实现
public interface IList {
    //头插法
    public void addFirst(int data);
    //尾插法
    public void addLast(int data);
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data);
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key);
    //删除第一次出现关键字为key的节点
    public void remove(int key);
    //删除所有值为key的节点
    public void removeAllKey(int key);
    //得到单链表的长度
    public int size();
    //清空链表
    public void clear();
    //打印链表
    public void display();
}

接口方法实现:

public class AchieveList implements IList{
    static class ListNode {
        public int val;
        public ListNode next;
        public ListNode(int val) {
            this.val = val;
        }
    }

    public ListNode head;

    //创建一个链表
    public void crateList() {
        ListNode node1 = new ListNode(12);
        ListNode node2 = new ListNode(34);
        ListNode node3 = new ListNode(56);
        ListNode node4 = new ListNode(78);
        ListNode node5 = new ListNode(99);
        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;
        this.head = node1;
    }

    //头插法
    @Override
    public void addFirst(int data) {
        ListNode node =new ListNode(data);
        node.next = this.head;
        this.head = node;
    }

    //尾插法
    @Override
    public void addLast(int data) {
        ListNode cur = this.head;
        while (cur.next != null) {
            cur = cur.next;
        }
        ListNode node = new ListNode(data);
        cur.next = node;
    }

    //任意位置插入,第一个数据节点为0号下标
    @Override
    public void addIndex(int index, int data) {
        ListNode node = new ListNode(data);
        ListNode cur = this.head;
        ListNode prev = this.head;
        if (index == 0) {
            node.next = cur;
            this.head = node;
        } else {
            int count = 0;
            while (count != index) {
                cur = cur.next;
                count++;
            }
            count = 0;
            while ((count+1) != index) {
                prev = prev.next;
                count++;
            }
            node.next = cur;
            prev.next = node;
        }
    }

    //查找是否包含关键字key是否在单链表当中
    @Override
    public boolean contains(int key) {
        ListNode cur = this.head;
        while (cur != null) {
            if (cur.val == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    //删除第一次出现关键字为key的节点
    @Override
    public void remove(int key) {
        if (head == null) {
            return;
        }
        ListNode cur = this.head;
        ListNode prev = this.head;
        int count = 1;
        while (cur.val != key) {
            if (count == size()) {
                return;
            }
            cur = cur.next;
            count++;
        }
        if (count == 1) {
            head = null;
            head = prev.next;
        } else {
            while (prev.next.val != key) {
                prev = prev.next;
            }
            prev.next = cur.next;
        }
    }

    //删除所有值为key的节点
    @Override
    public void removeAllKey(int key) {
        ListNode cur = this.head;
        int count = 1;
        while (cur.next != null) {
            if (cur.val == key) {
                count++;
            }
            cur = cur.next;
        }
        for (int i = 0; i < count; i++) {
            remove(key);
        }
    }

    //得到单链表的长度
    @Override
    public int size() {
        int count = 1;
        ListNode cur = this.head;
        if (cur == null) {
            return 0;
        }
        while (cur.next != null) {
            cur = cur.next;
            count++;
        }
        return count;
    }

    @Override
    public void clear() {
        ListNode cur = this.head;
        while (cur.next != null) {
            head = null;
            head = cur.next;
            cur = cur.next;
        }
        head = null;
    }

    //打印链表
    @Override
    public void display() {
        ListNode cur = this.head;
        if (head == null) {
            System.out.println("[" + "]");
        } else {
            System.out.print("[");
            while (cur != null) {
                if (cur.next == null) {
                    System.out.println(cur.val + "]");
                } else {
                    System.out.print(cur.val + " ");
                }
                cur = cur.next;
            }
        }
    }

四,与链表有关的题目练习(1)

1.删除链表中等于给定值 val 的所有节点

class Solution {
        public ListNode removeElements(ListNode head,int val) {
            if (head == null) {
                return null;
            }
            ListNode cur = head.next;
            ListNode prev = head;
            while (cur != null) {
                if (cur.val == val) {
                    prev.next = cur.next;
                    cur = cur.next;
                } else {
                    cur = cur.next;
                    prev = prev.next;
                }
            }
            if (head.val == val) {
                head = head.next;
            }
            return head;
        }
    }

2.反转一个单链表

思路:

现有如下链表:

定义cur = head.next

将head.next指向null

定义变量curNext = cur.next

将cur的下一个节点设为head

令cur = curNext;curNext = cur.next;

后以此思路循环

class Solution {
        public ListNode reverseList(ListNode head) {
            if (head == null) {
                return null;
            }
            if (head.next == null) {
                return head;
            }
            ListNode cur = head.next;
            head.next = null;
            while (cur != null) {
                ListNode curNext = cur.next;
                cur.next = head;
                head = cur;
                cur = curNext;
            }
            return head;
        }
    }

3.给定一个带有头结点 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点

思路:

定义如图两个变量fast和slow,且都等于head

令fast每次走两步,slow每次走一步。当fast==null或者fast.next === null时

此时slow的位置必定是中间节点

初:

移动一次:

移动两次:

末:

class Solution {
        public ListNode middleNode(ListNode head) {
            ListNode fast = head;
            ListNode slow = head;
            while (fast != null && fast.next != null) {
                fast = fast.next.next;
                slow = slow.next;
            }
            return slow;
        }
    }
}

4.输入一个链表,输出该链表中倒数第k个结点

假设有以下链表:

要求倒数第k个节点,我们可以先定义fast和slow两个位于头节点的变量

假如K为3,我们就先令fast先走K-1步

然后令fast和slow一起走同样的步数,直到fast.next为null

此时slow对应的位置就是所求的倒数第K个节点

public class Solution {
        public ListNode FindKthToTail(ListNode head,int k) {
            if (head == null) {
                return head;
            }
            if (k <= 0 && head != null ) {
                return null;
            }
            ListNode fast = head;
            ListNode slow = head;
            for (int i = 0; i < k-1; i++) {
                fast = fast.next;
                //处理K太大的问题
                if (fast == null) {
                    return null;
                }
            }
            while (fast.next != null) {
                fast = fast.next;
                slow = slow.next;
            }
            return slow;
        }
    }

5.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的

思路:

先有以下两个有序链表需要被合并为一个有序链表

定义一个newH节点

将head1.val与head2.val进行比较,较小令它等于tmpH且令它等于它的下一个节点,tmpH为newH的下一个节点

如上图head1.val<head2.val,故发生以下变化

接着对head1.val与head2.val进行比较

head2.val更小

继续比较

head1.val更小

继续比较

head2.val更小

……

当其中有一个数组比较完了的时候,此时只需令tmpH.next为另一个head即可

最后返回newH.next即可

class Solution {
        public ListNode mergeTwoLists(ListNode head1, ListNode head2) {
            ListNode newH = new ListNode(-1);
            ListNode tmpH = newH;
            while (head1 != null && head2 != null) {
                if (head1.val < head2.val) {
                    tmpH.next = head1;
                    tmpH = tmpH.next;
                    head1 = head1.next;
                } else {
                    tmpH.next = head2;
                    tmpH = tmpH.next;
                    head2 = head2.next;
                }
            }
            if (head1 != null) {
                tmpH.next = head1;
            }
            if (head2 != null) {
                tmpH.next = head2;
            }
            return newH.next;
        }
    }

6.编写代码,以给定值x为基准将链表分割成两部分,所有小于x的结点排在大于或等于x的结点之前

使用cur遍历顺序表

当cur.val<给定的值x时就让它进入链表1

当cur.val>=给定的值x时就让它进入链表2

最后将链表2的尾部插在链表1的头部,返回链表2

public class Partition {
        public ListNode partition(ListNode pHead, int x) {
            ListNode cur = pHead;
            ListNode tmpH1 = new ListNode(-1);
            ListNode tmpH2 = new ListNode(-1);
            ListNode head1 = tmpH1;
            ListNode head2 = tmpH2;
            while (cur != null) {
                if (cur.val < x) {
                    head2.next = cur;
                    head2 = head2.next;
                } else {
                    head1.next = cur;
                    head1 = head1.next;
                }
                cur = cur.next;
            }
            tmpH2 = tmpH2.next;
            tmpH1 = tmpH1.next;
            head2.next = tmpH1;
            head1.next = null;
            if (tmpH2 == null) {
                return tmpH1;
            }
            if (tmpH1 == null) {
                return tmpH2;
            }
            return tmpH2;
        }
    }

7.链表的回文结构

通过以上快慢指针的方式找到链表的中间节点

翻转

当slow和fast相遇时停止

public class PalindromeList {
        public boolean chkPalindrome(ListNode A) {
            if (A == null || A.next == null) {
                return true;
            }
            ListNode fast = A;
            ListNode slow = A;
            //找中间位置
            while (fast != null && fast.next != null) {
                fast = fast.next.next;
                slow = slow.next;
            }
            ListNode cur = slow.next;
            //翻转
            while (cur != null) {
                ListNode curNext = cur.next;
                cur.next = slow;
                slow = cur;
                cur = curNext;
            }
            ListNode right = slow;
            ListNode left = A;
            //从前到后  从后到前
            while (left.next != right.next ) {
                if (left.val != right.val) {
                    return false;
                }
                if (left.next == right)
                {
                    return true;
                }
                left = left.next;
                right = right.next;
            }
            return true;
        }
    }

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

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

相关文章

全新的C++语言

一、概述 C 的最初目标就是成为 “更好的 C”&#xff0c;因此新的标准首先要对基本的底层编程进行强化&#xff0c;能够反映当前计算机软硬件系统的最新发展和变化&#xff08;例如多线程&#xff09;。另一方面&#xff0c;C对多线程范式的支持增加了语言的复杂度&#xff0…

专业实习day3、4(路由器做内网访问公网)

专业实习 代码 display ip interface brief 显示当前设备下所有接口IP undo IP地址支持覆盖&#xff0c;但是正常的命令不能覆盖必须undo&#xff08;删除&#xff09;掉 un in en 在做配置的过程中&#xff0c;设备系统一般都会出现一些提示或者告警之类的东西&#xff0c;从…

书生·浦语大模型全链路开源体系 学习笔记 第一课

背景 大模型是发展人工通用人工智能的一个重要途径&#xff0c;能够解决多种任务和多种模态&#xff0c;展示了一个更面向更高阶的智能的潜在途径。大模型的发展历程是从专用模型到通用模型的过程&#xff0c;从语音识别、图像识别、人脸识别等专用模型&#xff0c;到通用的大…

高级分布式系统-第3讲 网络与网络互联

万维网的诞生 1957年10月4日&#xff0c; 苏联发射了人类第一颗人造卫星—斯普特尼克一号 美国政府震惊不已。 他们认为&#xff0c; 在日趋激烈的冷战对抗中&#xff0c; 自己已经全面落后于苏联。 为了扭转这一局面&#xff0c; 美国国防部很快于1958 年 2 月组建了一个神秘…

现代 C++ 小利器:参数绑定包装器堪称「Lambda 小平替」

以下内容为本人的学习笔记&#xff0c;如需要转载&#xff0c;请声明原文链接 微信公众号「ENG八戒」https://mp.weixin.qq.com/s/gt_zxMwhu8UixzCMF73Dng C 原生支持函数输入参数的默认值&#xff0c;但是有些业务场景下对原有设定的默认值不满意&#xff0c;那么可不可以临时…

【已解决】js定义对象属性是.如何访问

当变量没有length属性的时候&#xff0c;可能是个对象变量&#xff0c;当有键值对的时候就可能是个对象&#xff0c;读者都知道的是&#xff0c;用typeof(变量)可以查看属性&#xff0c;今天本文解决的问题是如果js定义对象中属性是"点"如何访问 问题再现 var a {…

java实验室预约管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java servlet 实验室预约管理系统是一套完善的java web信息管理系统 系统采用serlvetdaobean&#xff08;mvc模式)&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数 据库&#xff0c;系统主要采用B/S模式开发。开发环境为T…

【数据采集与预处理】流数据采集工具Flume

一、Flume简介 数据流 &#xff1a;数据流通常被视为一个随时间延续而无限增长的动态数据集合&#xff0c;是一组顺序、大量、快速、连续到达的数据序列。通过对流数据处理&#xff0c;可以进行卫星云图监测、股市走向分析、网络攻击判断、传感器实时信号分析。 &#xff08;…

【Proteus仿真】【Arduino单片机】太阳能追光系统设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduino单片机控制器&#xff0c;使用LCD1602液晶、光敏传感器、ADC模块、按键模块、28BYJ48步进电机驱动模块、直流电机模块等。 主要功能&#xff1a; 系统运行后&#xff0c;L…

Jupyter Lab | 在指定文件夹的 jupyter 中使用 conda 虚拟环境

Hi&#xff0c;大家好&#xff0c;我是源于花海。本文主要了解如何在指定文件夹的 jupyter 中使用 conda 虚拟环境&#xff0c;即在 conda 里面创建虚拟环境、将虚拟环境添加至 jupyter lab/notebook、安装软件包。 目录 一、创建虚拟环境 二、激活并进入虚拟环境 三、安装 …

我们公司内应届生身上的6个共性问题

如题目&#xff0c;本文主要是根据我们公司内真实的应届生身上共同的问题&#xff0c;总结而来。 1. 一天会做很多工作&#xff1a;会跟很多人对接&#xff0c;会一会忙这个一会忙哪个 现象&#xff1a; 说实话&#xff0c;这种情况&#xff0c;我看着都替她着急。自己正在解…

IPD(集成产品开发)

一、简介IPD IPD是指应用于集成产品开发&#xff08;Integrated Product Development&#xff09;的一套流程。 IPD流程可分为很多小的流程&#xff0c;这些流程确保企业做正确的事&#xff0c;且正确地做事。 IPD核心的流程之一是PDP&#xff08;Product Development Proce…

用C语言实现完全平方数计算【一题一策】第三期

题目&#xff1a;一个整数&#xff0c;它加上100后是一个完全平方数&#xff0c;再加上 168 又是一个完全平方数&#xff0c;请问该数是多少&#xff1f; 一、题目分析 首先假设该数为x&#xff0c;则x100y?&#xff0c;y为完全平方数。 然后加上168又是一个完全平方数&…

某金属加工公司的核心人才激励体系搭建项目纪实

【客户行业】金属加工行业 【问题类型】薪酬体系/激励体系 【客户背景】 某大型金属加工企业位于河北地区&#xff0c;成立于2000年&#xff0c;隶属于某大型有色金属集团&#xff0c;是一家集科研、开发、生产、销售于一体的国有企业&#xff0c;人员达到1000人。经过多年…

基于多反应堆的高并发服务器【C/C++/Reactor】(中)主线程给子线程添加任务以及如何处理该任务

在看此篇文章&#xff0c;建议先看我的往期文章&#xff1a; 基于多反应堆的高并发服务器【C/C/Reactor】&#xff08;中&#xff09;在EventLoop的任务队列中添加新任务-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/135346492?spm1001.2014.3001.5501一…

kubesphere和k8s的使用分享

文章目录 什么是kubernetesKubernetes的部分核心概念互式可视化管理平台与kubernetes的关系市面是常见的kubernetes管理平台 什么是kubesphereKubesphere默认安装的组件Kubesphere涉及的服务组件kubesphere的安装Kubesphere相关的内容 什么是kubernetes 就在这场因“容器”而起…

backtrader框架初探,轻松跑通策略并策略分析

网上有很多backtrader的文章&#xff0c;并有些将其与vnpy做比较&#xff0c;经过安装后发现&#xff0c;还是backtrader教程简单。 1、前期准备 # 安装akshare免费行情源 pip install akshare -i http://mirrors.aliyun.com/pypi/simple/ --trusted-hostmirrors.aliyun.com …

kubectl 源码分析

Cobra库 k8s各组件的cli部分都使用Cobra库实现&#xff0c;Cobra 中文文档 - 掘金 (juejin.cn)&#xff0c;获取方式如下&#xff1a; go get -u github.com/spf13/cobralatest cobra库中的Command结构体的字段&#xff0c;用于定义命令行工具的行为和选项。它们的作用如下&…

性能优化-OpenMP基础教程(五)-全面讲解OpenMP基本编程方法

本文主要介绍OpenMP编程的编程要素和实战&#xff0c;包括并行域管理详细实战、任务分担详细实战。 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#xff08;HPC&#xff09;开发基础教程 &#x1f380;C…