单链表和双链表

news2025/5/25 20:24:59

单链表和双链表

单链表:只有一个指向下一节点的指针   -->  单向读取

双链表:既有指向下一节点的指针,也有指向上一节点的指针,可以通过此向前查找

单链表和双链表的反转:逆序

整个链表逆序、部分链表逆序(修改循环条件即可)

是否需要返回值

链表的反转需要换头(整个链表逆序)的操作,则需要返回值(需要一个Node类型的返回值,存储换头之后的head值)

单链表的逆序及部分链表的逆序

注:当head为整个链表的头结点时,pre初始值为null,next指针反转之后head头节点指向pre,也就是null 

package linkedlist;

public class SingleLinkedList {

    class Node {
        public int value;
        public Node next;

        public Node(int data) {
            this.value = data;
        }

    }

    //单链表的反转
    //单链表的反转只需要将指针的方向反转,在逆序整个链表的时候,用head遍历链表的所有结点,使得每个指针由后一结点指向前一结点
    public static Node reverseList(Node head) {//head传入时是链表的头结点,在循环中为当前结点
        Node pre = null;//处理的结点的前一个结点
        Node next = null;//处理的结点的后一个结点
        while (head != null) {
            next = head.next;//head的后一个结点
            head.next = pre;//反转指针
            pre = head;
            head = next;
        }
        return pre;//出循环时head==null,pre为整个链表的最后一个结点,也就是链表反转之后的头
    }

    //部分单链表的反转
    //这里说部分反转应当是不需要返回值的,但是取的不同的start和end数值,可能会导致链表的头改变,需要返回值,因此返回
    public static Node reverselistPart(Node head, int start, int end) {//head为当前结点,starthe end为起始位置
        Node startNode = null;
        Node endNode = null;
        int length = 0;//因为链表与数组不同,不能够直到当前结点在整个链表之间的位置,需要计数
        Node pre = head;

        //遍历整个链表,此时pre指的是当前结点
        while (pre != null) {//获得整个链表的长度
            length++;
            //找到startNode节点的前一个结点
            if (length == start - 1) {
                startNode = pre;
            }
            //找到endNode节点的后一个节点
            if (length == end + 1) {
                endNode = pre;
            }
            pre = pre.next;//遍历,避免死循环
        }


        //判断所给的start和end是否合理
        if (start < 1 || end > length || start >= end) {
            return head;//不合理的数视为不对链表进行逆序
        } else if (startNode == null) {//start==1,反转的部分包含头结点
            pre = head;
        } else {
            pre = startNode.next;
        }


        //反转startNode和后一个结点
        //pre为开始结点的下一个结点,headNode为pre结点的下一个结点
        Node headNode = pre.next;
        pre.next = startNode;//next指针由pre指向startNode,实现指针的反转

        Node next = null;


        //反转从startNode后一个结点到endNode的前一个结点中所有的结点
        while (headNode != endNode) {//链表的反转
            next = headNode.next;
            headNode.next = pre;
            pre = headNode;
            headNode = next;
        }

//        if (startNode != null) {
//            startNode.next = pre;
//            return headNode;
//        }

        return pre;
    }
}


双链表的逆序

双链表的逆序和单链表相同,但是需要反转pre和next两个指针

package linkedlist;

class DoubleNode {
    public DoubleNode(int data) {
        this.value = data;
    }

    public int value;
    public DoubleNode next;
    public DoubleNode pre;

    public static DoubleNode reverseList(DoubleNode head) {
        DoubleNode pre = null;
        DoubleNode next = null;
        while (head != null) {
            next = head.next;
            head.next = pre;
            head.pre = next;
            pre = head;
            head = next;
        }
        return pre;
    }
}

打印两个有序链表的公共部分

《代码》

判断一个链表是否是一个回文结构

1、将链表结构放到栈中,比较栈弹出的顺序是否与原序相同(栈先进后出),全部相同则是回文

      O(n)的额外空间

package linkedlist;

import java.util.Stack;

public class IsPalindromic {
    class Node {
        public int value;
        public Node next;

        public Node(int data) {
            this.value = data;
        }
    }

    public static boolean isplindromicbyStack(Node head) {
        Stack<Node> stack = new Stack<Node>();//创建栈结构

        Node temp = head;
        while (temp != null) {
            stack.push(temp);//入栈
            temp = temp.next;
        }
        
        while (head != null){
            if(head.value != stack.pop().value){//出栈,判断前后顺序是否相同
                return false;
            }
            head = head.next;
        }
        return true;
    }
}

2、优化:降低空间复杂度

      O(n/2)的额外空间

将右半部分的链表放到栈中,从链表左侧开始遍历,每遍历一个栈弹出一个,一一进行比较,全部相同则回文       

        单链表无法获知整个链表有多少数,单指针无法判断此时经历的结点是否是链表结构的一半

        快慢指针:快指针一次走2步,慢指针一次走1步;当快指针遍历完整个链表,慢指针走到中点

        在实际情况中快慢指针需要根据题目调整代码

public static boolean isplindromicbyfastandslowPointer(Node head){
        if(head == null || head.next == null){
            return true;//整个链表为空或是只有一个Node也被视为回文结构
        }

        //快慢指针
        Node slow = head.next;
        Node fast = head;

        //整个链表为偶数时,慢指针位于n/2位置上;奇数时,整个链表位于n/2位置向上取整+1(5 - 4)
        while(fast.next != null && fast.next.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }

       //取出后半段入栈
        Stack<Node> stack = new Stack<Node>();
        while(slow != null){
            stack.push(slow);
            slow = slow.next;
        }
        
        //将后半段与前半段比较,是否相同
        while(!stack.isEmpty()){
            if(head.value != stack.pop().value){
                return false;
            }
            head = head.next;
        }
        return true;
    }

3、优化:不使用额外的数据结构

      O(1)的额外空间

快指针一次走2步,慢指针一次走1步;慢指针走到中间的位置,遍历后面部分的链表逆序;

两个指针,一个从head向后遍历,一个从end向前遍历,直到一个指针指向空

public static boolean isplindromicbytwoPointer(Node head) {
        if (head == null || head.next == null) {
            return true;//整个链表为空或是只有一个Node也被视为回文结构
        }

        Node slow = head;
        Node fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;//最后到达中点位置,奇数时中点位置向上取整
            fast = fast.next.next;//最后到达链表最后一位或两位
        }

        fast = slow.next;//从右半部分的起点开始
        slow.next = null;//空出空间便于后半部分逆序

        //后半部分逆序
        Node temp = null;
        while (fast != null) {
            temp = fast.next;
            fast.next = slow;//反转指针
            slow = fast;
            fast = temp;
        }
        temp = slow;//slow为反转部分的最后一个结点,也就是后半部分反转部分的头
        fast = head;//左半部分的头

        boolean res = true;
        //slow表示右半部分,fast表示左半部分
        while (slow != null && fast != null) {
            if (slow.value != fast.value) {
                res = false;
                break;
            }
            slow = slow.next;
            fast = fast.next;
        }

        //将链表反转回去  slow -- fast ,  temp -- slow , fast -- temp
        slow = temp.next;
        temp.next = null;
//      fast = slow.next;
//      slow.next = null;
        
        while (slow != null) {
//      while (fast != null)

            fast = slow.next;
            slow.next = temp;
            temp = slow;
            slow = fast;
//          temp = fast.next;
//          fast.next = slow;//反转指针
//          slow = fast;
//          fast = temp;
        }
        
        return res;
    }

将单链表按某值划分为左边小、中间相等、右边大的形式

使用额外空间:将单链表的每个结点放到数组中,对数组进行partition操作(左边小、中间相等、右边大) 

《代码》

不使用额外空间:六个变量,分别为小于、等于、大于部分的头和尾

注:三区域串起来的时候需要注意区域是否存在,否则会指向null导致出错 

package linkedlist;

public class ListPartition {
    class Node {
        public int value;
        public Node next;

        public Node(int data) {
            this.value = data;
        }
    }


    public static Node listPattition(Node head, int pivot) {
        Node SH = null;
        Node ST = null;
        Node EH = null;
        Node ET = null;
        Node BH = null;
        Node BT = null;
        Node temp = null;

        while (head != null) {
            temp = head.next;//存储下一个head
            head.next = null;//消去指针


            if (head.value < pivot) {
                if (SH == null) {
                    SH = head;
                    ST = head;
                } else {
                    ST.next = head;
                    ST = head;
                }
            }
            if (head.value == pivot) {
                if (EH == null) {
                    EH = head;
                    ET = head;
                } else {
                    ET.next = head;
                    ET = head;
                }
            }
            if (head.value > pivot) {
                if (BH == null) {
                    BH = head;
                    BT = null;
                } else {
                    BT.next = head;
                    BT = head;
                }
            }

            head = temp;//再次进行赋值,使得head遍历整个链表
        }

        if (ST != null) {//有小于区域
            if (ET != null) {//有等于区域
                ST.next = EH;
            } else if (BT != null) {//没有等于区域,有大于区域
                ST.next = BH;
            } else {//啥也没有
                ST.next = null;
            }
        }

        if (ET != null) {//有等于区域
            if (BT != null) {//有大于区域
                ET.next = BH;
            } else {//无大于区域
                ET.next = null;
            }
        }
        
        //最终返回结果
        if(ST != null){
            return SH;
        } else if (ET != null) {
            return EH;
        }else{
            return BH;
        }
    }
}

复制含有随机指针节点的链表

使用额外空间:哈希表拷贝节点,通过原来的节点1的next指针找到节点2,拷贝后的节点1'对应节点2',由此可知节点1'的next指针指向节点2'。random指针同理。

《代码》

不使用额外空间:在链表的每个节点后插入其对应拷贝节点,通过节点1的next指针找到拷贝的节点1',节点1的random指针指向节点3,节点3的next指针指向节点3',由此可以拷贝节点1'指向节点3'的random指针

由next指针向下遍历,将每一对的指针都拷贝出,拷贝所有的random指针后抽离出拷贝的链表(画的好丑)

package linkedlist;

public class CopyLinkedListWithRand {
    class Node {
        public int value;
        public Node next;
        public Node rand;

        public Node(int data) {
            this.value = data;
        }

    }

    public Node copylistwithRand(Node head) {
        if (head == null) {
            return null;
        }

        Node node1 = head;
        Node node2 = null;

        //1 -> 2  
        //1 -> 1' -> 2
        while (node1 != null) {
            node2 = node1.next;
            node1.next = new Node(node1.value);//克隆的新节点
            node1.next.next = node2;
            node1 = node2;
        }
        
        node1 = head;//重新来过
        
        //拷贝random指针
        while (node1 != null) {
            
            if (node1.rand != null) {//节点1的random指针
                node1.next.rand = node1.rand.next;//节点1'的random指针=节点3的next=节点3'
            } else {
                node1.next.rand = null;
            }

            node1 = node1.next.next;
        }
        
        Node res = head.next;//拷贝出来的链表的头节点
        node1 = head;//再次重新来过
        Node temp = res;
        
        while(node1 != null){
            node1 = node1.next.next;//跳到第三个,第五个...
            if(node1.next != null) {
                temp.next = node1.next;//第二个指向第四个,第四个指向第六个...
            }else{
                temp.next = null;
            }
            temp = temp.next;//第二个跳第四个,第四个跳第六个...
        }

        return res;//返回拷贝链表的头
    }
}

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

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

相关文章

【数据分享】2001-2022年我国省市县镇四级的逐月降水量数据(免费获取/Shp/Excel格式)

气象数据在日常研究中非常常用&#xff0c;之前我们分享过来自国家青藏高原科学数据中心提供的1901-2022年1km分辨率逐月降水栅格数据以及基于该数据处理而得到的1901-2022年1km分辨率的逐年降水栅格数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff01; 本次我…

设置伙伴(buddy)-给窗口控件增加快捷键

在官方教程或者很多qt程序中经常看到能使用全键盘操作软件&#xff0c;那么QT creator也支持了这一特性&#xff0c;就是使用设置伙伴来实现的。 我们可以在设计界面按照如下几步实现&#xff1a; 先放置label 再放置一个lineEdit控件。 这个时候我们就可以开始伙伴绑定的步骤…

初试占比70%,计算机招生近200人,安徽理工大学考情分析

安徽理工大学 考研难度&#xff08;☆&#xff09; 内容&#xff1a;23考情概况&#xff08;拟录取和复试分析&#xff09;、院校概况、23专业目录、23复试详情、各专业考情分析、各科目考情分析。 正文980字&#xff0c;预计阅读&#xff1a;3分钟 2023考情概况 安徽理工大…

小程序Saas平台源码:开启电商小程序新时代,可视化后台自由DIY的无限可能

在当今数字化的时代&#xff0c;小程序已成为各行各业开展业务的重要工具。尤其在电商领域&#xff0c;小程序能有效地缩短消费者与商品之间的距离&#xff0c;提升营销效率。给大家分享一款针对电商行业的小程序Saas平台源码&#xff0c;它具有一键生成电商小程序、可视化后台…

【mars3d学习】淹没分析,计算最高最低值出错

问题一&#xff1a;淹没分析&#xff08;地形分析&#xff09; Mars3d淹没分析的示例 - 功能示例(Vue版) | Mars3D三维可视化平台 | 火星科技 初始化一个polygon面的时候&#xff0c;使用 mars3d.PolyUtil.interPolygonByDepth 直接计算淹没的最大最小高度值&#xff1b; 但…

【深度学习】 Python 和 NumPy 系列教程(十九):Matplotlib详解:2、3d绘图类型(5)3D等高线图(3D Contour Plot)

目录 一、前言 二、实验环境 三、Matplotlib详解 1、2d绘图类型 2、3d绘图类型 0. 设置中文字体 1. 3D线框图&#xff08;3D Line Plot&#xff09; 2. 3D散点图&#xff08;3D Scatter Plot&#xff09; 3. 3D条形图&#xff08;3D Bar Plot&#xff09; 4. 3D曲面图…

AIGC(生成式AI)试用 4 -- 从模糊到精确

从模糊到精确&#xff0c;也许差异在于 更多的描述&#xff0c;更多的信息更具像的描述&#xff0c;更多的数据&#xff0c;更有效的信息主题明确和目标清晰层次感与条理性更简洁清晰的逻辑 “说清楚点&#xff0c;不太明白&#xff0c;提供更多的信息也许能知道要做什么。” …

GIS前端-地图操作与交互

GIS地图操作与交互 地图操作与交互基本原理Leaflet提供的事件缩放控件 常用的基础功能通常是一个应用系统所必需的&#xff0c;如地图的缩放、导航、定位、弹出框等&#xff0c;它让一张静态的地图动起来&#xff0c;让地图承载更多的空间信息&#xff0c;并以友好的交互方式呈…

YOLOv5:修改backbone为ConvNeXt

YOLOv5&#xff1a;修改backbone为ConvNeXt 前言前提条件相关介绍ConvNeXtYOLOv5修改backbone为ConvNeXt修改common.py修改yolo.py修改yolov5.yaml配置 参考 前言 记录在YOLOv5修改backbone操作&#xff0c;方便自己查阅。由于本人水平有限&#xff0c;难免出现错漏&#xff0c…

算法分析与设计编程题 回溯法

装载问题 题目描述 解题代码 递归回溯 // goods[i]表示货物i的重量, c1,c2分别表示货船1和货船2的载重量 vector<vector<int>> optimalLoading(vector<int>& goods, int c1, int c2) {int n goods.size(); // 货物数量int maxSum 0; // 当前最大载货…

穿山甲报错 splashAdLoadFail data analysis error

使用swift接入穿山甲&#xff0c;未接入GroMore&#xff0c;这个时候如果代码位配置错误会导致如下错误&#xff1a; splashAdLoadFail(_:error:) Optional(“Error Domaincom.buadsdk Code98764 “data analysis error” UserInfo{NSLocalizedDescriptiondata analysis error,…

基于YOLOv8模型的海洋生物目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型的海洋生物目标检测系统可用于日常生活中检测与定位海洋生物目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与结果导出。本系统采用YOLOv8目标检测算法训…

贝类包纳米虫病诊断方法

声明 本文是学习GB-T 42821-2023 贝类包纳米虫病诊断方法. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 242 g 57.1 mL 100 mL 1000 mL A.9 1 电泳缓冲液 50电泳缓冲液 加水定容至 室温贮存。 A.10 苏木精染液的配制方法 苏木精 无水乙醇 …

第36章_瑞萨MCU零基础入门系列教程之步进电机控制实验

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写&#xff0c;需要的同学可以在这里获取&#xff1a; https://item.taobao.com/item.htm?id728461040949 配套资料获取&#xff1a;https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总&#xff1a; ht…

基于Python的UG二次开发入门

文章目录 基于Python的UG二次开发入门1 二次开发环境搭建1.1 安装UG1.2 安装Pycharm1.3 环境配置1.4 测试 2 NX Open介绍2.1 基础架构2.1.1 Sessions and Parts2.1.2 Objects and Tags2.1.3 Factory Objects&#xff08;工厂对象&#xff09;2.1.4 Builder Objects&#xff08;…

科兴未来 | 第十届中国(泰州)国际大健康产业高层次人才创新创业大赛公告

为加快推进青年和人才友好型城市建设&#xff0c;吸引和集聚更多海内外生物医药高层次人才来泰创新创业&#xff0c;推动大健康产业高质量发展&#xff0c;全力建设“健康名城、幸福泰州”&#xff0c;现举办第十届中国&#xff08;泰州&#xff09;国际大健康产业高层次人才创…

15W sip网络有源音箱,可外接15W无源副音箱

SV-7042VP 15W sip网络有源音箱,可外接15W无源副音箱 一、描述 SV-7042VP是深圳锐科达电子有限公司的一款壁挂式SIP网络有源音箱&#xff0c;具有10/100M以太网接口&#xff0c;可将网络音源通过自带的功放和喇叭输出播放&#xff0c;可达到功率15W。同时它可以外接一个15W的…

STM32WB55开发(4)----配置串口打印Debug调试信息

STM32WB55开发----4.配置串口打印Debug调试信息 概述硬件准备视频教学样品申请选择芯片型号配置时钟源配置时钟树RTC时钟配置查看开启STM32_WPAN条件配置HSEM配置IPCC配置RTC启动RF开启蓝牙开启串口调试配置蓝牙参数设置工程信息工程文件设置Keil工程配置代码配置结果演示 概述…

000_差分信号

1.什么是差分信号 差分信号又叫做差模信号&#xff0c;使用差分信号传输时&#xff0c;需要2根信号线&#xff0c;这2根信号线的振幅相等&#xff0c;相位相反&#xff0c;通过2根信号线的电压差值来表示逻辑0和逻辑1。 差分信号表示逻辑值如下图&#xff1a; 2.差分信号的特…

合并单元格中自动填充数字序列的方法详解

我们如何在Excel中将序列号填充到不同大小的合并单元格列表中?我们首先想到的是拖动“自动填充”手柄来填充合并的单元格,但在这种情况下,我们将收到以下警告消息,并且无法填充合并的单元。 有没有一种方法可以在不必手动键入数字的情况下对合并的单元格进行编号? 例如,…