数据结构初阶(链表)

news2025/1/11 14:28:05

文章目录

  • 一、链表的基础概念
    • 1.1 什么是链表
    • 1.2 分类
    • 1.3 链表的底层代码
    • 1.4 例题
    • 1.5 LinkedList 的实现
      • (1)什么是LInkedList
      • (2)底层代码
      • (3)LinkedLIst的使用
  • 1.6 ArrayList和LinkedList的区别

一、链表的基础概念

用于解决 ArrayList 中,插入、删除时间复杂度较大,且可能会浪费空间的问题

1.1 什么是链表

逻辑上是连续的,物理上(内存上)是不连续的,由一个个节点组成

在这里插入图片描述

1.2 分类

  • 单向 / 双向
    在这里插入图片描述

  • 带头的 / 不带头的
    在这里插入图片描述

  • 循环 / 非循环
    在这里插入图片描述

1.3 链表的底层代码

public class MyStringList {
    private ListNode head;

    class ListNode{
        private int val;
        private ListNode next;

        public ListNode(int val) {
            this.val = val;
        }
    }

    public void creatList(){
        ListNode node1 = new ListNode(12);
        ListNode node2 = new ListNode(24);
        ListNode node3 = new ListNode(36);
        ListNode node4 = new ListNode(48);

        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = null;

        head = node1;
    }

    //打印这个链表的所有元素
    public void display() {
        ListNode cur = head;
        while (cur != null){
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
    }

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

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


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

    //尾插法
    //要考虑是一个节点的情况
    public void addLast(int data){
        ListNode node = new ListNode(data);
        if (head == null){
            head = node;
            return;
        }

        ListNode cur = head;

        while (cur.next != null){
            cur = cur.next;
        }

        cur.next = node;
    }


    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){
        ListNode cur = head;
        ListNode node = new ListNode(data);
        if (!checkIndex(index)){
            throw new IndexException("下标输入错误 " + index);
        }

        if (index == 0){
            addFirst(data);
            return;
        }

        if (index == this.size()){
            addLast(data);
        }

        //找到
        for (int i = 0; i < index - 1; i++) {
            cur = cur.next;
        }

        //交换
        node.next = cur.next;
        cur.next = node;


    }

    private boolean checkIndex(int index){
        if (index < 0 || index > this.size() ){
            return false;
        }

        return true;
    }

    //删除第一次出现关键字为key的节点
    public void remove(int key){
        if (head == null){
            return;
        }

        if (head.val == key){
            head = head.next;
            return;
        }

        ListNode prev = searchPrev(key);
        if (prev == null){
            System.out.println("没有这个数据");
            return;
        }
        ListNode del = prev.next;
        prev.next = del.next;
    }

    private ListNode searchPrev(int key){
        ListNode prev = head;
        while (prev.next != null){
            if (prev.next.val == key){
                return prev;
            }else{
                prev = prev.next;
            }
        }
        return null;
    }

    //删除所有值为key的节点
    public void removeAllKey(int key){
        if (head == null){
            return;
        }

        while (head.val == key){
            head = head.next;
        }

        ListNode cur = head.next;
        ListNode prev = head;

        while (cur != null){
            if (cur.val == key){
                prev.next = cur.next;
                cur = cur.next;
            }else {
                prev = cur;
                cur = cur.next;
            }
        }
    }

	public void clear() {
        //this.head = null;   第一种方法
        while (head != null) {
            ListNode headNext = head.next;   //第二种方法
            head.next = null;
            head = headNext;
        }
    }
}

1.4 例题

一、移除链表元素

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if (head == null){
            return head;
        }

        ListNode cur = head.next;
        ListNode pre = head;

        while (cur != null){
            if (cur.val == val){
                cur = cur.next;
                pre.next = cur;
            }else{
                pre = cur;
                cur = cur.next;
            }
        }

        if (head.val == val){
            head = head.next;
        }

        return head;
    }
}

二、反转一个单链表

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;
    }
}

三、链表的中间节点

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;
    }
}

四、链表中倒数第四个节点

public ListNode FindKthToTail(ListNode head,int k) {
    ListNode fast = head;
    ListNode slow = head;

    if (head == null || k <= 0){
        return null;
    }
    for(int i = 0; i < k - 1; i++){
        fast = fast.next;
        if (fast == null){
            return null;
        }
    }

    while (fast.next != null){
        slow = slow.next;
        fast = fast.next;
    }
    return slow;
}

五、合并两个有序链表

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode newHead = new ListNode();
        ListNode tmpH = newHead;

        while (list1 != null && list2 != null){
            if (list1.val < list2.val){
                tmpH.next = list1;
                tmpH = tmpH.next;
                list1 = list1.next;
            }else {
                tmpH.next = list2;
                tmpH = tmpH.next;
                list2 = list2.next;
            }
        }

        if (list1 != null){
            tmpH.next = list1;
        }else {
            tmpH.next = list2;
        }

        return newHead.next;
    }
}

六、链表分割

public class Partition {
    public ListNode partition(ListNode pHead, int x) {
        ListNode ae = null;
        ListNode as = null;
        ListNode be = null;
        ListNode bs = null;

        while (pHead != null){
            if (pHead.val < x){
                if (ae == null){
                    ae = pHead;
                    as = pHead;
                }else{
                    as.next = pHead;
                    as = as.next;
                }
            }else{
                if (be == null){
                    be = pHead;
                    bs = pHead;
                }else {
                    bs.next = pHead;
                    bs = bs.next;
                }
            }
            pHead = pHead.next;
        }

        if (ae == null){
            return be;
        }
        as.next = be;
        if (be != null){
            bs.next = null;
        }
        return ae;
    }
}

七、链表的回文结构

public class PalindromeList {
    public boolean chkPalindrome(ListNode A) {
        ListNode slow = A;
        ListNode fast = 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;
        }

        //判断
        while (A != slow){
            if (A.val != slow.val){
                return false;
            }

            if (A.next == slow){
                return true;
            }

            A = A.next;
            slow = slow.next;
        }

        return true;
    }
}

八、相交链表

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode plong = headA;
        ListNode pshort = headB;

        //1、分别求两个链表的长度
        int len1 = 0;
        int len2 = 0;
        //O(M)
        while (plong != null) {
            len1++;
            plong = plong.next;
        }
        //O(N)
        while (pshort != null) {
            len2++;
            pshort = pshort.next;
        }
        plong = headA;
        pshort = headB;
        //2、求差值步的len
        int len = len1 - len2;
        if(len < 0) {
            plong = headB;
            pshort = headA;
            len = len2 - len1;
        }
        //3、哪个链表长 走len步
        while (len != 0) {
            plong = plong.next;
            len--;
        }
        //4、一起走 直到相遇!
        while (plong != pshort) {
            plong = plong.next;
            pshort = pshort.next;
        }
        return plong;
    }

九、环形列表

public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null){
            return false;
        }

        ListNode fast = head;
        ListNode slow = head;

        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;

            if (fast == slow){
                return true;
            }
        }

        return false;
    }
}

十、环形列表||

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null){
            return null;
        }

        ListNode slow = head;
        ListNode fast = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;

            if (fast == slow){
                break;
            }
        }

        if (fast == null || fast.next == null){
            return null;
        }

        fast = head;
        while (fast != slow){
            fast = fast.next;
            slow = slow.next;
        }

        return fast;
    }
}

1.5 LinkedList 的实现

(1)什么是LInkedList

在这里插入图片描述

(2)底层代码

public class MyLinkedList {
        static class ListNode {
            public int val;
            public ListNode prev;
            public ListNode next;

            public ListNode(int val) {
                this.val = val;
            }
        }
        public ListNode head;
        public ListNode last;
        //得到链表的长度   和双向 没有关系 !!!
        public int size(){
            int len = 0;
            ListNode cur = head;
            while (cur != null) {
                cur = cur.next;
                len++;
            }
            return len;
        }

        public void display(){
            ListNode cur = head;
            while (cur != null) {
                System.out.print(cur.val+" ");
                cur = cur.next;
            }
            System.out.println();
        }

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

        //头插法
        public void addFirst(int data){
            ListNode node = new ListNode(data);
            if(head == null) {
                head = node;
                last = node;         //第一种写法
                return;
            }
            node.next = head;
            head.prev = node;
            head = node;
        /*if(head == null) {
            head = node;
        }else {                  //第二种写法
            node.next = head;
            head.prev = node;
            head = node;
        }*/
        }

        //尾插法
        public void addLast(int data){
            ListNode node = new ListNode(data);
            if(head == null) {
                head = node;
                last = node;
            }else {
                last.next = node;
                node.prev = last;
                last = node;//last = last.next;
            }
        }

        //任意位置插入,第一个数据节点为0号下标
        public void addIndex(int index,int data){
            int size = size();
            if(index < 0 || index > size) {
                throw new IndexOutOfBounds("双向链表index不合法!");
            }
            if(index == 0) {
                addFirst(data);
                return;
            }
            if(index == size) {
                addLast(data);
                return;
            }
            ListNode cur = head;
            while (index != 0) {
                cur = cur.next;
                index--;
            }
            ListNode node = new ListNode(data);
            node.next = cur;
            cur.prev.next = node;
            node.prev = cur.prev;
            cur.prev = node;
        }



        //删除第一次出现关键字为key的节点
        public void remove(int key){
            ListNode cur = head;
            while (cur != null) {
                if(cur.val == key) {
                    //开始删
                    if(cur == head) {
                        //删除头节点
                        head = head.next;
                        //只要1个节点的时候
                        if(head != null) {
                            head.prev = null;
                        }else {
                            last = null;
                        }
                    }else {
                        cur.prev.next = cur.next;
                        if(cur.next != null) {
                            //cur.prev.next = cur.next;
                            cur.next.prev = cur.prev;
                        }else {
                            //cur.prev.next = cur.next;
                            last = last.prev;
                        }
                    }
                    return;
                }else {
                    cur = cur.next;
                }
            }
        }




        //删除所有值为key的节点
        public void removeAllKey(int key){
            ListNode cur = head;
            while (cur != null) {
                if(cur.val == key) {
                    //开始删
                    if (cur == head) {
                        //删除头节点
                        head = head.next;
                        //只要1个节点的时候
                        if (head != null) {
                            head.prev = null;
                        }else {
                            last = null;
                        }
                    } else {
                        cur.prev.next = cur.next;
                        if (cur.next != null) {
                            //cur.prev.next = cur.next;
                            cur.next.prev = cur.prev;
                        } else {
                            //cur.prev.next = cur.next;
                            last = last.prev;
                        }
                    }
                }

                cur = cur.next;
            }
        }
        public void clear(){
            ListNode cur = head;
            while (cur != null) {
                ListNode curNext = cur.next;
                cur.prev = null;
                cur.next = null;
                //cur.val = null;     如果cur.val是引用的情况
                cur = curNext;
            }
            head = null;
            last = null;
        }
}

(3)LinkedLIst的使用

❤️构造

方法解释
LinkLIst()无参构造
public LinkedList(Collection<? extends E>c)使用其他集合容器中元素构造List
List<Integer> list1 = new LinkedList<>();
List<String> list2 = new LinkedList<>();
List<String> list3 = new LinkedList<>(list2);

❤️方法

方法解释
boolean add(E e)尾插 e
void add(int index, E element)将 e 插入到 index 位置
boolean addAll(Collection<? extends E> c)尾插 c 中的元素
E remove(int index)删除 index 位置元素
boolean remove(Object o)删除遇到的第一个 o
E get(int index)获取下标 index 位置元素
E set(int index, E element)将下标 index 位置元素设置为 element
void clear()清空
boolean contains(Object o)判断 o 是否在线性表中
int indexOf(Object o)返回第一个 o 所在下标
int lastIndexOf(Object o)返回最后一个 o 的下标
List subList(int fromIndex, int toIndex)截取部分 list

❤️遍历

public static void main(String[] args) {
	LinkedList<Integer> list = new LinkedList<>();
	list.add(1); // add(elem): 表示尾插
	list.add(2);
	list.add(3);
	list.add(4);
	list.add(5);
	list.add(6);
	list.add(7);
	System.out.println(list.size());

	//直接打印
	System.out.println(linkedList);
	
	// foreach遍历
	for (int e:list) {
		System.out.print(e + " ");
	}
	System.out.println();
	
	// 使用迭代器遍历---正向遍历
	ListIterator<Integer> it = list.listIterator();
	while(it.hasNext()){
		System.out.print(it.next()+ " ");
	}
	System.out.println();
	
	// 使用反向迭代器---反向遍历
	ListIterator<Integer> rit = list.listIterator(list.size());
	while (rit.hasPrevious()){
		System.out.print(rit.previous() +" ");
	}
	System.out.println();
}

1.6 ArrayList和LinkedList的区别

不同点ArrayListLInkedList
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持:O(N)
头插需要搬移元素,效率低O(N)只需修改引用的指向,时间复杂度为O(1)
插入空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除频繁

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

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

相关文章

Spring底层架构核心概念

文章目录 Spring底层架构核心概念BeanDefinitionBeanDefinitionReaderAnnotatedBeanDefinitionReaderXmlBeanDefinitionReaderClassPathBeanDefinitionScanner BeanFactoryApplicationContext国际化资源加载获取运行时环境事件发布 类型转换PropertyEditorConversionServiceTyp…

20行Python代码获取 心碎榜单文件保存本地,准备开始emo......

人生苦短 我用python&#xff08;emo版&#xff09; (语气充满悲伤…) 今天咱们试试只用20行代码来实现 批量获取 某某云 文件保存本地&#xff0c;炒鸡简单&#xff01; 悄悄的告诉你&#xff0c;其实不到20行代码~ 事前准备 软件环境 环境Python3.8编辑器是pycharm 模块…

轻松掌握k8s的kubectl使用命令行操作01知识点

程序员使用的kubectl&#xff0c;只能在主节点使用kubectl命令 1、查看集群所有节点 kubectl get nodes 2、根据配置文件&#xff0c;给集群创建资源 kubectl apply -f xxxx.yaml 3、查看集群部署了哪些应用 kubectl get pods -A 4、指定查看命名空间部署了哪些应用 不指…

[DSCoding2] 反转链表——迭代法

题目描述 核心思路 观察上图可以发现&#xff0c;将链表反转后&#xff0c;原有的结点间的引用关系发生了改变&#xff0c;比如反转前1指向2&#xff0c;反转后2指向1&#xff0c; 所以我们可以从修改节点间的引用关系下手。 在遍历链表时&#xff0c;将当前节点的next指针指向…

ReentrantLock原理

实现了Lock接口 内部也维护了一个同步器Sync继承自AQS&#xff0c;Sync是抽象的&#xff0c;两个实现NonFairSync和FairSync public ReentrantLock() {sync new NonfairSync(); } public ReentrantLock(boolean fair) {sync fair ? new FairSync() : new NonfairSync(); }非…

算法训练Day30:332.重新安排行程 51. N皇后 37. 解数独

文章目录 重新安排行程题解 [N 皇后](https://leetcode.cn/problems/n-queens/description/)题解 解数独题解 重新安排行程 CategoryDifficultyLikesDislikesContestSlugProblemIndexScorealgorithmsHard (47.57%)7650--0 Tags Companies 给你一份航线列表 tickets &#xf…

微服务学习——微服务

认识微服务 单体架构 将业务的所有功能集中在一个项目中开发&#xff0c;打成一个包部署。 优点: 架构简单部署成本低 缺点: 耦合度高 分布式架构 根据业务功能对系统进行拆分&#xff0c;每个业务模块作为独立项目开发&#xff0c;称为一个服务。 优点: 降低服务耦合有利…

【LeetCode】剑指 Offer 58. 反转字符串 p284 -- Java Version

1. 题目介绍&#xff08;58. 反转字符串&#xff09; 面试题58&#xff1a;翻转字符串&#xff0c; 一共分为两小题&#xff1a; 题目一&#xff1a;翻转单词顺序题目二&#xff1a;左旋转字符串 2. 题目1&#xff1a;翻转单词顺序 题目链接&#xff1a;https://leetcode.cn/p…

使用 ip2region 获取用户的 IP 归属地

目录 1. ip2region 简介2. 使用步骤2.1 下载资源2.2 引入依赖2.3 编写工具类2.3.1 获取 IP 地址2.3.2 根据 IP 地址获取 IP 归属地2.3.3 完整代码 2.4 结果测试 1. ip2region 简介 ip2region 是一个离线IP地址定位库和IP定位数据管理框架&#xff0c;10微秒级别的查询效率&…

部署zabbix代理服务器和snmp监控

目录 zabbix代理服务器 分布式监控的作用 部署zabbix代理服务器 在 Web 页面配置 agent 代理 snmp监控 SNMP简介 部署zabbix-snmp 服务端安装snmp监控程序 在 Web 页面配置 snmp 方式监控 zabbix代理服务器 分布式监控的作用 分担 server 的集中式压力 解决多机房之…

HTTP | 强缓存与协商缓存

缓存&#xff0c;开发绕不开的环节。 web缓存分为很多种&#xff0c;比如数据库缓存、代理服务器缓存、CDN缓存&#xff0c;以及浏览器缓存&#xff08;localStorage, sessionstorage, cookie&#xff09;。 一个web应用&#xff0c;需要各式各样的资源&#xff08;html/css/…

【C++】C++11 右值引用和移动语义

文章目录 一、左值与左值引用二、右值与右值引用三、左值引用和右值引用的比较四、右值引用的使用场景和意义1、左值引用的短板2、移动构造和移动赋值3、STL 容器的变化 五、万能引用与完美转发1、万能引用2、完美转发 六、新增默认成员函数七、成员变量的缺省值八、default 和…

【Blender建模】newbird从零开始学+新手常见问题处理

目标 第一阶段&#xff1a;在跟着教程下&#xff0c;熟悉如何使用blender 教程地址&#xff1a;https://www.youtube.com/watch?vnIoXOplUvAw 一、移动、旋转、扩展各视角下的物体&#xff0c;熟悉各个窗口 鼠标中键&#xff08;Shift&#xff09;控制视角的方向 ~键快速选择…

Redis --- 入门、数据类型

一、前言 1.1、什么是Redis Redis是一个基于内存的key-value结构数据库。Redis 是互联网技术领域使用最为广泛的存储中间件&#xff0c;它是「Remote Dictionary Service」的首字母缩写&#xff0c;也就是「远程字典服务」。 基于内存存储&#xff0c;读写性能高适合存储热点…

Pytorch基础 - 5. torch.cat() 和 torch.stack()

目录 1. torch.cat(tensors, dim) 2. torch.stack(tensors, dim) 3. 两者不同 torch.cat() 和 torch.stack()常用来进行张量的拼接&#xff0c;在神经网络里经常用到。且前段时间有一个面试官也问到了这个知识点&#xff0c;虽然内容很小很细&#xff0c;但需要了解。 1. t…

Spring(10. 面试问题简析)学习笔记

上一篇&#xff1a;9. Spring 底层原理 文章目录 1. 对Spring的IOC机制的理解2. 对spring的AOP机制的理解3. 了解过cglib动态代理吗&#xff1f;他跟jdk动态代理的区别是什么&#xff1f;4. 能说说Spring中的Bean是线程安全的吗&#xff1f;5. Spring的事务实现原理是什么&…

Leetcode-二叉树

1.中序-后序构建二叉树 106. 从中序与后序遍历序列构造二叉树 - 力扣&#xff08;LeetCode&#xff09; 1. 首先根据后序&#xff08;左右中&#xff09;确定顶点元素&#xff1b; 2. 根据顶点元素划分中序序列&#xff1b; 3. 根据划分中序序列中-左子树的长度&#xff0c;进…

半小时学会HTML5

一、了解几个概念 1、HTML定义 HTML是&#xff08;Hyper Text Markup Language&#xff09;超文本标记语言&#xff0c;超文本包含&#xff1a;文字、图片、音频、视频、动画等。 2、W3C 是什么&#xff1f; W3C 即&#xff08;World Wide Web Consortium&#xff09; 万维…

【性能测试】常见适用场景以及策略

面对日益复杂的业务场景和不同的系统架构&#xff0c;前期的需求分析和准备工作&#xff0c;需要耗费很多的时间。而不同的测试策略&#xff0c;也对我们的测试结果是否符合预期目标至关重要。 这篇博客&#xff0c;聊聊我个人对常见的性能测试策略的理解&#xff0c;以及它们…

RK3399 Android 10 Camera2保存录像时缩略图获取为空

RK3399 Android 10相机录像保存时无法获取缩略预览图 先找到录像点击按钮 //点击快门按钮时可以通过log打印看到停止录像的流程onShutterButtonClick() //这里主要看停止的流程即stop true时会进入onStopVideoRecording方法 public void onShutterButtonClick() {Log.d(TAG…