首页 > 编程语言 >算法之链表

算法之链表

时间:2024-12-16 11:44:52浏览次数:3  
标签:head ListNode next 链表 算法 null 节点

链表

移除链表元素

对于链表来说,删除头节点和中间节点具体操作不一样是因为想要删除一个中间节点,必须要知道该节点的前一个节点,而头节点没有前一个节点。

  使用虚拟头节点,统一节点的删除操作,用一个虚拟头节点的next指向head,这个链表中的每个元素都会有前一个节点,从而对所有节点都可以统一使用前一个节点进行删除。

最后是返回原虚拟头节点的下一个节点,才是新的修改过的链表,而不能直接返回原先的head。

找到原因:如果删除的是头节点,head指向的节点并不会被删除,更新的是p指向的链表,所有综合起来直接返回虚拟头节点的下一个节点

public ListNode removeElements1(ListNode head, int val) {
    ListNode p=new ListNode();
    ListNode q=new ListNode();//是虚拟头节点
    p.next=head;//临时指针 遍历链表(包括虚拟头节点)
    q=p;
    while (p.next!=null){
        if (p.next.val==val){
            p.next=p.next.next;
        }else {
            p=p.next;
        }
    }
    return q.next; //不能直接返回head,调试后发现p指向的链表会改变,但head所在的链表并没有随之改
                  //变,只有再添加一个node节点让它等于p
}

 

设计链表

对于链表的操作需要在节点的基础上增加一个虚拟头节点dummy node,它的next指向链表的头节点,再创建一个当前节点curr=dummy node,使用当前节点对链表进行操作,增删之后再使得head=dunmmy node.next。

为什么增删后要执行head=dunmmy node.next?因为对于链表的增删操作很有可能会删除头节点或者将头节点后移,此时光靠头节点是没办法得到最新的链表的,但虚拟头节点的位置是不会变的,所以要根据虚拟头节点找到head。

查看代码
 public class MyLinkedList {
    int length;
    listNode head;

    public MyLinkedList() {
    }

    public MyLinkedList(int length, listNode head) {
        this.length = length;
        this.head = head;
    }

    public int get(int index){
        listNode p=head;
        if (index<length){
            for (int i = 0; i < index; i++) {
                p=p.next;
            }
            return p.val;
        }else {
            return -1;
        }

    }

    public void addAtHead(int val){
        listNode newNode=new listNode();
        newNode.val=val;
        newNode.next=head;
        head=newNode;//这里直接用了head
        length=length+1;
    }

    public void addAtTail(int val){
        listNode newNode=new listNode();
        newNode.val=val;
        listNode p=new listNode();
        p.next=head;
        listNode q=new listNode();
        q=p;
        while (p.next!=null){//找到插入节点的前一个节点
            p=p.next;
        }
        p.next=newNode;
        head=q.next;//如果此时链表为空,加在末尾的也是头节点,但此时head经过上面的操作并没有发生改变,因此要更新
        length+=1;
    }
    public void addAtIndex(int index,int val){
        listNode newNode=new listNode(val);

        listNode p=new listNode();
        p.next=head;

        listNode q=p;

        if (index<=length){
            for (int i = 0; i < index; i++) {
                p=p.next;//得到前一个
            }
            newNode.next=p.next;
            p.next=newNode;
            head=q.next;//必须要加上这句,如果此时插入的是头节点,head节点会放到新插入节点的后面,根据head并不能返回最新的链表 但如果插入的是中间节点,则直接用head即可
            length=length+1;
        }



    }
    public void deleteAtIndex(int index){
        listNode p=new listNode();
        p.next=head;
        listNode q=new listNode();
        q=p;

        if (index<length){
            for (int i = 0; i < index; i++) {
                p=p.next;
            }
            p.next=p.next.next;
            head=p.next;//同理 如果删除的是头节点的话 必须要同步更新
            length-=1;
        }

    }

}

 

翻转链表

使用双指针思想,curr指向头节点,pre为null,再用一个临时节点temp保存curr.next.

查看代码
  public ListNode reverseList(ListNode head) {
        ListNode pre=null;
        ListNode curr=head;
        ListNode temp=new ListNode();

        while (curr!=null){
            temp=curr.next;
            curr.next=pre;
            pre=curr;
            curr=temp;
        }
        return pre;
    }

 

两两交换链表中的节点

问题:没有将虚拟头节点的指针重新指向排序后的头节点,造成排序后第一个节点消失。

查看代码
  public ListNode swapPairs(ListNode head) {
         ListNode dummyNode=new ListNode();
        dummyNode.next=head;

        ListNode pre=dummyNode;
        ListNode curr1=pre.next;
        ListNode curr2=null;
        if (curr1!=null &&curr1.next!=null){
            curr2=curr1.next;
        }

        ListNode temp;
        while (curr1!=null && curr2!=null){

            temp=curr2.next;

            pre.next=curr2;
            curr2.next=curr1;
            curr1.next=temp;

            pre=curr1;
            curr1=temp;
            if (temp!=null){
                curr2=temp.next;
            }

        }
        return dummyNode.next;
    }

删除链表的倒数第N个节点

思路:使用前后指针,中间间隔节点数为倒数节点个数-1。

查看代码
 public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummyNode=new ListNode();
        dummyNode.next=head;

        ListNode preN=dummyNode;
        ListNode curr=dummyNode;
        while (n-->0){
            curr=curr.next;
        }
        while(curr.next!=null){
            preN=preN.next;
            curr=curr.next;
        }
        preN.next= preN.next.next;
        return dummyNode.next;
    }

其中在对链表节点进行增加时,不能直接新建节点来接收更新的list.head值,否则list中的head是没有更新的

MyLinkedList list=new MyLinkedList();
        int[] arr=new int[]{1,2,3,4,5};
        for (int i = 0; i < arr.length; i++) {
            list.head = list.addLinkedlist(arr[i]);//不能新建一个ListNode为head 而是把这个链表的head值进行更新
        }
//        list.printLinkedlist(list.head);

       list.head = removeNthFromEnd(list.head, 1);
        list.printLinkedlist(list.head);

链表相交

该题的初始化无法使用简单的增加链表节点,如果让输入的两个数组独立进行链表初始化,即使值相同但并不能指向同一节点。

思路:将两个链表进行等长操作,具体为求出两个链表的长度,然后将长链表的当前指向节点往后移动直到和短链表的头节点位置相同,然后开始顺序比较每个节点是否相同(注意:不是节点的值,而是节点!!)

查看代码
         int lengthA=0;
        int lengthB=0;
        ListNode currA=headA;
        ListNode currB=headB;
        while (currA!=null || currB!=null){
            if (currA!=null){
                lengthA+=1;
                currA=currA.next;
            }
            if (currB!=null){
                lengthB+=1;
                currB=currB.next;
            }
        }
        currA=headA;
        currB=headB;
        int gap=Math.abs(lengthA-lengthB);
        while (gap-->0){
            if (lengthA>lengthB){
                currA=currA.next;
            }else {
                currB=currB.next;
            }

        }

        while (currA!=null && currB!=null){
            if (currA==currB){
                return currA;
            }
            currA=currA.next;
            currB=currB.next;
        }
        return null;

环形链表II

要掌握它们之间的数学关系,才能解此题,两个关键点:第一是通过快慢指针,当相遇时一定在环内,得到相遇节点;第二是根据数学公式(x+y)*2=x+y+n(y+z),得到x=(n-1)(y+z)+z,即当指针从头节点和相遇节点同时出发时,一定会再次相遇,再次相遇的节点为环形入口节点。

查看代码
 public ListNode detectCycle(ListNode head) {
        
        ListNode fast=head;
        ListNode slow=head;

        while(fast!=null &&  fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            if(slow==fast){
                ListNode index1=fast;
                ListNode index2=head;
                while(index1!=index2){
                index1=index1.next;
                index2=index2.next;
              }
              return index1;
            }
            
        }
        return null;

标签:head,ListNode,next,链表,算法,null,节点
From: https://www.cnblogs.com/exo123/p/18609667

相关文章

  • 当前,inux中的内存页面调度算法
    用户:请详细介绍,目前linux中内存页面调度算法的相关信息讯飞星火:Linux内存页面调度算法是操作系统内存管理中的核心组件之一,它负责在有限的物理内存资源中高效地管理不同进程的页面,以减少页面置换的频率并提高系统性能。以下是对Linux内存页面调度算法的详细介绍:###1.概述Lin......
  • 计算机毕业设计项目源码 大数据深度学习 基于聚类算法实现的二手房价分析及可视化系统
    标题:基于聚类算法实现的二手房价分析及可视化系统基于聚类算法实现的二手房价分析及可视化系统可以具备以下主要功能:数据采集与预处理:从各大二手房平台抓取房源信息,包括房价、面积、房型、位置等属性。数据清洗,处理缺失值、异常值和重复数据。数据标准化和归一化,以便于后......
  • 计算机毕设源码 大数据深度学习 基于聚类算法实现的房屋数据分析及可视化系统
    标题:基于聚类算法实现的房屋数据分析及可视化系统基于聚类算法的房屋数据分析及可视化系统主要功能可以包括以下几个方面:数据采集与预处理:收集房屋销售相关的数据,如房屋价格、面积、房间数量、位置、建造年份等。数据清洗,处理缺失值、异常值,进行标准化或归一化。聚类分析......
  • 两数相加——链表
    题目链接思路链表的方式,模拟加法的计算过程,用一个变量保存进位信息即可。代码classSolution{public:ListNode*addTwoNumbers(ListNode*l1,ListNode*l2){//有一种办法可以节省空间,直接将结果放在l1或者l2上//但是这样就改变了原来的......
  • 毕业设计:python车牌识别系统 CNN算法 卷积神经网络网络 深度学习 tensorflow(源码)✅
    python车牌识别系统CNN算法卷积神经网络网络深度学习tensorflow(源码)1、项目介绍技术栈:Python语言、CNN算法、tensorflow和keras、深度学习、opencv、pyqt5图形界面2、项目界面(1)上传图像进行车牌识别1(2)上传图像进行车牌识别2(3)上传图像进行车牌识别3(4)上传视......
  • 毕业设计:NBA球员数据分析及预测系统+可视化 +Flask框架 篮球数据分析 时间序列预测算
    毕业设计:NBA球员数据分析及预测系统+可视化+Flask框架篮球数据分析时间序列预测算法大数据毕业设计✅1、项目介绍NBA球员数据分析及预测系统技术栈Python语言、Flask框架、requests爬虫、statsmodels中的ARIMA时间序列预测算法、Echarts可视化2、项目界面(1)球员数......
  • 如何判断链表是否有环?
    在前端开发中,虽然链表不是最常用的数据结构,但在处理某些问题时,它仍然是一个有用的工具。判断链表是否有环是一个常见的链表相关问题。以下是一个简单而有效的方法来判断链表是否有环:使用快慢指针(Floyd'sCycle-FindingAlgorithm)初始化两个指针,一个快指针(每次移动两个节点)和一......
  • 【数据结构与算法】Java描述:JDK17新增常用特性
    前言:从springboot3.0开始,已经不支持JDK8了。参考资料,来自官方博客:https://spring.io/blog/2022/01/20/spring-boot-3-0-0-m1-is-nowavailable?spm=a2c6h.12873639.article-detail.24.766d46b40LM1IV从3.0开始,转变为JDK17。JDK17是LTS(长期支持版),可以免费商用到2029......
  • 【初阶数据结构和算法】八大排序算法之插入排序(直接插入排序、希尔排序及其对比)
    文章目录一、常见排序算法分类一、直接插入排序二、希尔排序三、直接插入排序和希尔排序性能对比一、常见排序算法分类   常见的排序算法有八种,我们简单盘点一下插入排序:直接插入排序、希尔排序选择排序:直接选择排序、堆排序交换排序:冒泡排序、快排希尔排序计数......
  • 三维无人机航迹算法的目标函数如何确定
    一、定义目标函数        在三维无人机航迹算法中,目标函数的确定通常基于具体的任务需求和飞行约束。以下是一个简单的例子,展示了如何为三维无人机航迹规划定义一个目标函数。        例子:最小化飞行时间和避障的三维无人机航迹规划        1.任务......