【牛客网-面试必刷TOP 101】01链表

news2024/11/21 0:38:19
BM1 反转链表
解题思路
第一种方法:借助栈
1. 栈的特点是先进后出,用stack来存储链表,之后新建一个头节点,按出栈顺序拼接形成新的链表。
2. 注意,最后一个节点的next要赋值null
3. 空间复杂度O(N), 时间复杂度O(N)

JAVA代码实现

import java.util.*;
public class Solution {
    public ListNode ReverseList (ListNode head) {
        // 用栈,空间复杂度O(N),时间复杂度O(N)
        Stack<ListNode> stack = new Stack<>();
        while(head != null){
            stack.push(head);
            head = head.next;
        }
        //出栈,重新连接
        if(stack.isEmpty()){
            return null;
        }
        ListNode node = stack.pop();
        ListNode dummy = node;
        while(!stack.isEmpty()){
            ListNode tempNode = stack.pop();
            node.next = tempNode;
            node = node.next;
        }
        //最后一个下一位为空
        node.next = null;
        return dummy;
    }
}

在这里插入图片描述

第二种方法,修改链表指针指向;
指针cur表示当前要处理的节点
指针tmp用于暂存cur.next,避免后面修改指针后,断链无法继续遍历
指针pre表示当前处理节点的上一个节点,进行翻转的目的是将cur.next修改成pre
初始化时,我们将pre设为null,因为第一个节点最后变成最后一个节点,其next为null
import java.util.*;
public class Solution {
    public ListNode ReverseList (ListNode head) {
        //双指针迭代,空间复杂度O(1),时间复杂度O(N)
        //前继节点为pre,当前操作节点为cur
        //初始化cur为head,pre=null
        ListNode cur = head, pre = null;
        while(cur != null){
            ListNode nextNode = cur.next;//存储下一个节点,不然局部反转的时候会断链
            cur.next = pre;
            pre = cur;
            cur = nextNode;
        }
        return pre;
    }
}
BM2 链表内指定区间反转
解题思路
在上一题的基础上进行修改
首先,要处理的cur节点先走m步走到第m个节点
反转结束的条件不是链表遍历到末尾null时候,而是遍历到第n个节点就结束
没有结束额外空间,空间复杂度O(1), 时间复杂度O(N)
import java.util.*;
public class Solution {
    public ListNode reverseBetween (ListNode head, int m, int n) {
        // write code here
        //可以用BM1 反转链表的思想,
        ListNode  dummy = new ListNode(-1);
        dummy.next = head;
        ListNode pre = dummy;
        ListNode cur = head;
        //找m
        for (int i = 1; i < m; ++i) {
            pre = cur;
            cur = cur.next;
        }
        //反转m到n
        for (int i = m; i < n; ++i) {
            ListNode temp = cur.next;
            cur.next = temp.next;
            temp.next = pre.next;
            pre.next = temp;
        }
        return dummy.next;
    }
}
BM3 链表中的节点每k个一组翻转
解题思路
参考了官方解题,递归翻转
设置一个遍历的尾指针tail,每次反转之前找到翻转区间的最后一个节点
如果tail在k步内变成了null,则直接返回,不进行翻转(最后一段不足k个,不翻转)
之后与BM1一样,定义pre,cur,翻转结束的条件是当走到tail,停止翻转
递归的返回值是当前翻转这一分组的头结点
这道题还不是很理解,后面有新的理解再重新整理笔记
import java.util.*;
public class Solution {
    public ListNode reverseKGroup (ListNode head, int k) {
        // write code here
        ListNode tail = head;//反转的尾部
        //遍历k次到尾部
        for (int i = 0; i < k; ++i) {
            //如果不足k到了尾部,则不翻转
            if (tail == null) {
                return head;
            }
            tail = tail.next;
        }
        ListNode pre = null;
        ListNode cur = head;
        while (cur != tail) {
            //到达尾部前
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        //当前尾指向下一段要翻转的链表
        head.next = reverseKGroup(tail,k);
        return pre;
    }
}
BM4 合并两个排序的链表
解题思路
比较简单,新建一个链表,同时遍历两个链表,比较val的大小,遇到较小的就加入;
import java.util.*;
public class Solution {
    public ListNode Merge (ListNode pHead1, ListNode pHead2) {
        // write code here
        ListNode res = new ListNode(-1);
        ListNode dummy = res;
        while(pHead1 != null && pHead2 != null){
            if(pHead1.val <= pHead2.val){
                dummy.next = pHead1;
                pHead1 = pHead1.next;
            }else{
                dummy.next = pHead2;
                pHead2 = pHead2.next;
            }
            dummy = dummy.next;
        }
        if(pHead1 != null){
            dummy.next = pHead1;
        }
        if(pHead2 != null){
            dummy.next = pHead2;
        }
        return res.next;
    }
}
BM6 判断链表中是否有环
解题思路
是的,没有BM5,因为它是难题,菜的一批的我看答案也还没理解透
利用双指针,slow每次往前走一步,fast每次往前走两步,如果链表中有环,则两个指针最终会在非终点的地方相遇
因为地球是圆的,slow和fast不是平行走,总会在一个时间点遇到的,时长的问题而已     
双指针在后面很多题目中都会遇到过。
import java.util.*;
public class Solution {
    public boolean hasCycle(ListNode head) {
        //快慢指针
        ListNode fast = head;
        ListNode slow = head;
        if(head == null){
            return false;
        }
        // fast.next = head;
        // slow.next = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(slow == fast){
                return true;
            }
        }
        return false;
    }
}
BM7 链表中环的入口结点
解题思路

在这里插入图片描述

还有借助双指针slow和fast,之后要寻找规律
假设双指针在结点C相遇,且slow是第一次走到C,A->B的距离为X,B->C的距离为Y
则slow走了X+Y,fast每次走的是slow的两倍,所以fast走了2*(X+Y)
fast走的2*(X+Y)实际路径是A->B B->C C->B B->C,我们不知道的是C->B的距离,根据上述条件,可以知道
C->B的距离= 2*(X+Y)-(A->B)-(B->C) -(B->C)= 2*(X+Y)-X-Y-Y=X
噢,原理规律就是,相遇点C->B(环入口)的距离,竟然等于从头结点到环入口的距离
那好办了,slow还是在C开始走,fast回到头结点,和slow一起一步一步走,走到他俩遇见的结点,就是环的入口结点。破案。
import java.util.*;
public class Solution {
    public ListNode EntryNodeOfLoop(ListNode pHead) {
        //双指针 空间O(1) 时间O(N)
        if(pHead == null || pHead.next == null){
            return null;
        }
        ListNode fast = pHead;
        ListNode slow = pHead;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            //有环,双指针会在环中相遇
            if(slow == fast){
                break;
            }
        }
        if(fast == null || slow == null){
            return null;
        }
        fast = pHead;
        //与第一次相遇的节点相同速度出发,相遇节点为入口结点
        while(fast != slow){
            fast = fast.next;
            slow = slow.next;
        }
        return fast;

    }
}
还有一种办法是,不断的遍历链表,借助集合,存储遍历到的结点;
每遍历一个结点判断当前集合中是否已经有这个结点,有的话直接return。
    public ListNode EntryNodeOfLoop(ListNode pHead) {
        //hash 空间O(N) 时间O(N)
        HashSet<ListNode> set = new HashSet<>();
        while(pHead != null){
            if(set.contains(pHead)){
                return pHead;
            }
            set.add(pHead);
            pHead = pHead.next;
        }
        return null;
    }
BM8 链表中倒数最后k个结点
解题思路
两次循环,先统计长度,之后遍历到count-k处,return
 public ListNode FindKthToTail (ListNode pHead, int k) {
        // 最简单的想法,但是需要两次遍历
        //先遍历,看链表长度,然后遍历count-k次,return
        ListNode dummy = pHead;
        if(pHead == null){
            return pHead;
        }
        int count = 1;
        while(dummy.next != null){
            count++;
            dummy = dummy.next;
        }
        if(count < k){
            return null;
        }
        dummy = pHead;
        while(count > k){
            dummy = dummy.next;
            count --;
        }

        return dummy;

    }
双指针
fast先走k步
之后fast和slow一起走,当fast走到链表尾部时,slow所在的结点刚好是倒数第k个
public class Solution {
     public ListNode FindKthToTail (ListNode pHead, int k) {
        //快慢指针
        if(pHead == null){
            return pHead;
        }
        ListNode fast = pHead;
        ListNode slow = pHead;
        while(k-- > 0){
            if(fast != null){
                fast = fast.next;
            }else{
                return null;
            }
        }
        while(fast != null){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
     }
}
BM9 删除链表的倒数第n个节点
解题思路
双指针
注意,有可能删除第1个结点,所以定义一个虚拟头结点res
fast从res开始,先走n步
实际上就是从head开始走了n-1步
当fast走到链表尾部时,slow就走到了倒数第n-1个结点,跳过第n个结点即可:slow.next = slow.next.next
import java.util.*;
public class Solution {
    public ListNode removeNthFromEnd (ListNode head, int n) {
        // 双指针
        //注意有可能删除第一个,所以使用一个头节点
        if(head == null){
            return head;
        }
        ListNode res = new ListNode(-1);
        res.next = head;

        ListNode slow = res;
        ListNode fast = res;

        //fast先走n步
        for(int i=0;i<n;i++){
            fast = fast.next;
        }
        while(fast.next != null){
            fast = fast.next;
            slow = slow.next;   
        }
        slow.next = slow.next.next;
        return res.next;
    }
}
BM10 两个链表的第一个公共结点
解题思路
第一种方法是先遍历两个链表的长度len1和len2;
如果len1>len2, 链表1先走len1-len2步,因为公共部分在尾部,要尾部对齐;
之后同时遍历链表1和链表2,如果遇到两者相等,直接return(有公共结点会返回,没有会一起走到尾部,返回null)
代码较为繁琐,也可能是我太菜了,不会优化
 public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
            //1.先统计两个链表的长度len1,len2
            ListNode dummy1 = pHead1;
            ListNode dummy2 = pHead2;
            int len1 = 0;
            while(dummy1 != null){
                dummy1 = dummy1.next;
                len1++;
            }
            int len2 = 0;
            while(dummy2 != null){
                dummy2 = dummy2.next;
                len2++;
            }
            //2,较长的链表,指针先走|len1-len2|,因为链表的公共部分在尾部,尾部对齐
            dummy1 = pHead1;
            dummy2 = pHead2;
            if(len1 > len2){
                while(len1 - len2 > 0){
                    dummy1 = dummy1.next;
                    len1--;
                }
            }
            if(len1 < len2){
                while(len2 - len1 > 0){
                    dummy2 = dummy2.next;
                    len2--;
                }
            }
            //3.两个链表同时移动,如果遇到节点相同,return,如果有一方已经=null,直接return null
            while(dummy1 != null && dummy2 != null){
                if(dummy1 == dummy2){
                   return dummy1;
                }else{
                    dummy1 = dummy1.next;
                    dummy2 = dummy2.next;
                }
            }
            return null;
        }
第二种方法,还是双指针,找规律
链表1的长度为len1,链表2的长度为len2,
指针n1和指针n2,一个从链表1出发,一个从链表2出发,两者都走len1+len2个长度
如果有公共结点,如样例{1,2,3}{4,5}{6,7}
/n1走:1,2,3,6,7,null,4,5,6
n2走:4,5,6,7,null,1,2,3,6
走到相等的时候就是公共起始点
没有公共节点如{1}{2,3},{},n1:1,2,3,null  n2:2,3,1,null 走到最后相等都为空
public class Solution {
    public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
        ListNode n1 = pHead1;
        ListNode n2 = pHead2;
        while(n1 != n2){
            n1 = (n1 == null)? pHead2 : n1.next;//n1第一次走到空的时候是phead1遍历完
            n2 = (n2 == null)? pHead1 : n2.next;
         }
        return n1;
    }
}
BM11链表相加(二)
解题思路
先把两个链表进行翻转,这样就可以按位加
每个链表结点存储的值在0-9,所以如果计算出来超过9,需要借助变量isTen记录超过9的十位数是多少
链表结点的值取当前位置两个值相加+isTen之后的个位数
import java.util.*;
public class Solution {
    public ListNode addInList (ListNode head1, ListNode head2) {
        // write code here
        head1 = reverse(head1);
        head2 = reverse(head2); 
        ListNode ans = new ListNode(-1);
        ListNode dummy = ans;
        int isTen = 0;
        int val=0;
        while(!(head1 == null && head2 == null)){
            val = isTen;
            if(head1 != null){
                val += head1.val;
                head1 = head1.next;
            }
            if(head2 != null){
                val += head2.val;
                head2 = head2.next;
            }
            isTen = val/10;
            dummy.next = new ListNode(val%10);
            dummy = dummy.next;
        }
        if(isTen > 0){
            dummy.next = new ListNode(isTen);
        }
        return reverse(ans.next);
    }

    public ListNode reverse(ListNode head){
        if(head == null){
            return head;
        }
        ListNode cur = head;
        ListNode per = null;
        while(cur!= null){
            ListNode tmp = cur.next;
            cur.next = per;
            per = cur;
            cur = tmp;
        }
        return per;
    }
}
BM12 单链表排序
解题思路
借助数组,存储当前链表的所有结点值
对数组进行排序
重新建立链表返回
public class Solution {
    public ListNode sortInList (ListNode head) {
        // 借助数组
        ListNode dummy = head;
        List<Integer> list = new ArrayList<>();
        while(dummy != null){
            list.add(dummy.val);
            dummy = dummy.next;
        }
        Collections.sort(list);
        dummy = head;
        for(int i=0;i<list.size();++i){
            dummy.val = list.get(i);
            dummy = dummy.next;
        }
        return head;
    }
}
BM13 判断一个链表是否为回文结构
解题思路
借助栈,先将链表的节点值依次入栈
之后,从头遍历链表,将其值与栈顶元素相比,如果相等则继续遍历,栈顶元素出栈;否则直接return false
    public boolean isPail (ListNode head) {
        // write code here
        //借助一个栈,空间复杂度O(N),时间复杂度O(N)
        Stack<Integer> stack = new Stack();
        if(head == null){
            return true;
        }
        ListNode dummy = head;
        while(dummy != null){
            stack.push(dummy.val);
            dummy = dummy.next;
        }
        dummy = head;
        while(!stack.isEmpty()){
            if(stack.pop() != dummy.val){
                return false;
            }
            dummy  = dummy.next;
        }
        return true;
    }
方法二,快慢指针
slow每次走一步,fast每次走两步
fast走到链表末尾时,slow走到链表中间
将slow到fast的子链表进行翻转,翻转后的第一个结点由slow指向
之后fast从原始链表头结点开始遍历,判断slow与fast是否相等
public class Solution {

   public boolean isPail (ListNode head) {
       // write code here
       //双指针,slow往后一步,fast走两步,当fast走到末尾时,slow走到中间
       //后半段进行反转
       //时间复杂度O(N) 空间复杂度O(1)
       if (head == null || head.next == null) {
           return true;
       }
       ListNode slow = head;
       ListNode fast = head;
       while (fast != null && fast.next != null) {
           fast = fast.next.next;
           slow = slow.next;
       }
       slow = reverse(slow);
       fast = head;
       while (slow != null && fast != null) {
           if (slow.val != fast.val) {
               return false;
           }
           slow = slow.next;
           fast = fast.next;
       }

       return true;
   }
   public ListNode reverse(ListNode head) {
       ListNode per = null;
       ListNode cur = head;
       while (cur != null) {
           ListNode tmp = cur.next;
           cur.next = per;
           per = cur;
           cur = tmp;
       }
       return per;
   }
}

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

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

相关文章

竞赛选题 深度学习 opencv python 公式识别(图像识别 机器视觉)

文章目录 0 前言1 课题说明2 效果展示3 具体实现4 关键代码实现5 算法综合效果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于深度学习的数学公式识别算法实现 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学…

uCOSIII实时操作系统 二 同步与通信

目录 同步概念&#xff1a; 互斥概念&#xff1a; 临界区概念&#xff1a; 任务时间概念&#xff1a; 信号量概念&#xff1a; 互斥信号量概念&#xff1a; 事件标志组概念&#xff1a; 消息邮箱和消息梯队概念&#xff1a; 内存管理概念&#xff1a; 如何从裸机开发…

Linux网络编程1-简单的CS通信程序

Linux网络编程1-简单的CS通信程序 1.Socket相关API说明1.1字节序转换函数&#xff1a;用于ip和port转换1.2sockaddr结构1.3socket函数 以及两个队列1.4bind listen connect accept1.5收发数据 2.服务器和客户端程序代码流程3.服务器端4.客户端5.测试accept不是建立连接而是从已…

1803_ChibiOS网络书籍阅读_嵌入式RTOS介绍

全部学习汇总&#xff1a; GreyZhang/g_ChibiOS: I found a new RTOS called ChibiOS and it seems interesting! (github.com) 1. RTOS指的是实时性操作系统&#xff0c;但是并不是只有嵌入式领域使用RTOS。然而&#xff0c;嵌入式是RTOS的主要使用领域。 2. 一般的RTOS有一组…

国庆中秋宅家自省:偷偷尝鲜

Python3中类的高级语法及实战 Python3(基础|高级)语法实战(|多线程|多进程|线程池|进程池技术)|多线程安全问题解决方案 Python3数据科学包系列(一):数据分析实战 Python3数据科学包系列(二):数据分析实战 Python3数据科学包系列(三):数据分析实战 国庆中秋宅家自省: Pytho…

【visual studio 小技巧】项目属性->生成->事件

需求 我们有时会用到一些dll&#xff0c;需要把这些dll和我们生成的exe放到一起&#xff0c;一般我们是手动自己copy&#xff0c; 这样发布的时候&#xff0c;有时会忘记拷贝这个dll&#xff0c;导致程序运行出错。学会这个小技巧&#xff0c;就能实现自动copy&#xff0c;非…

Ubuntu1804 安装后无法使用root登录解决方法

1. 给root用户设置密码 sudo passwd root2. 确认是否安装ssh服务 (在安装Ubuntu 的时候可以勾选安装ssh 远程服务),没有安装的话执行以下命令(Ubuntu可以连接互联网) sudo apt-get instll openssh-server3. 设置允许root 用户进行远程连接 sudo vim /etc/ssh/sshd_config 在…

10.3 调试事件转存进程内存

我们继续延申调试事件的话题&#xff0c;实现进程转存功能&#xff0c;进程转储功能是指通过调试API使获得了目标进程控制权的进程&#xff0c;将目标进程的内存中的数据完整地转存到本地磁盘上&#xff0c;对于加壳软件&#xff0c;通常会通过加密、压缩等手段来保护其代码和数…

【Ubuntu】基于C++实现人脸识别

人脸识别考勤机 文章目录 人脸识别考勤机概述第一章 搭建Ubuntu环境1.1 什么是物联网1.2 物联网应该怎么学1.3 Linux开发环境搭建1.4 Linux基本使用1.5 Ubuntu网络配置 第二章 “hello,world!”程序2.1 什么是程序2.2 “hello,world!”程序2.3 C语法扩展2.4 常见错误调试 第三章…

Numpy科学计算基础库--numpy基础知识

对数组执行数学运算和逻辑运算时&#xff0c;Numpy 是非常有用的。在用 Python 对 n 维数组和矩阵进行运算时&#xff0c;Numpy 库提供了大量有用特征。Numpy 库数组有两种形式&#xff1a;向量和矩阵。严格地讲&#xff0c;向量是一维数组&#xff0c;矩阵是多维数组。在某些情…

Redis-数据过期策略

数据过期策略 惰性删除策略优点&#xff1a;对cpu比较友好&#xff0c;在用到该key的时候才去进行判断&#xff0c;对于很多用不到key不用浪费时间去检查是否过期缺点&#xff1a;对内存不友好&#xff0c;如果一个key过期了&#xff0c;但是我们又一直没有用到该key&#xff0…

基于YOLOv8的安全帽检测系统(4):EMA基于跨空间学习的高效多尺度注意力、效果优于ECA、CBAM、CA,助力行为检测 | ICASSP2023

目录 1.Yolov8介绍 2.安全帽数据集介绍 3.EMA介绍 4.训练结果分析 5.系列篇 1.Yolov8介绍 Ultralytics YOLOv8是Ultralytics公司开发的YOLO目标检测和图像分割模型的最新版本。YOLOv8是一种尖端的、最先进的&#xff08;SOTA&#xff09;模型&#xff0c;它建立在先前YOLO…

Win11 安装 Vim

安装包&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1Ru7HhTSotz9mteHug-Yhpw?pwd6666 提取码&#xff1a;6666 双击安装包&#xff0c;一直下一步。 配置环境变量&#xff1a; 先配置系统变量中的path&#xff1a; 接着配置用户变量&#xff1a; 在 cmd 中输入…

MySQL 事务隔离级别与锁机制详解

目录 一、前言二、事务及其ACID属性三、并发事务处理带来的问题四、事务隔离级别4.1、隔离级别分类4.2、查看当前数据库的事务隔离级别:4.3、临时修改数据库隔离级别&#xff08;重启MySQL后恢复到配置中的级别&#xff09; 五、表数据准备六、MySQL常见锁介绍5.1、锁分类5.2、…

【网络安全 --- XSS漏洞利用实战】你知道如何利用XSS漏洞进行cookie获取,钓鱼以及键盘监听吗?--- XSS实战篇

一&#xff0c;XSS 实战 以pikachu靶场为例 1-1 盗取cookie 过程&#xff1a;想要盗取别人的cookie信息的话有一个前提条件&#xff0c;就是你应该在别人触发你的xss攻击时&#xff0c;你的代码应该将收集的cookie信息发送给你的平台来接收&#xff0c;这样才获取到了数据 …

图的广度遍历-邻接矩阵实现

description 本题要求实现邻接矩阵存储图的广度优先遍历。 函数接口定义&#xff1a; void BFS(MGraph G,Vertex i); 其中MGraph是邻接矩阵存储的图&#xff0c;定义如下&#xff1a; #define MaxVertexNum 10 /定义最大顶点数/ typedef int Vertex;/* 用顶点下标表示顶点,…

1799_GNU pdf阅读器evince_windows系统下编译尝试

全部学习汇总&#xff1a; GreyZhang/g_GNU: After some years I found that I do need some free air, so dive into GNU again! (github.com) 从网上下载下来了evince的代码&#xff0c;尝试做一个windows下的编译。 这应该是autotools的构建系统&#xff0c;先尝试运行confi…

java做个qq机器人

前置的条件 机器人是基于mirai框架实现的。根据官方的文档&#xff0c;建议使用openjdk11。 我这里使用的编辑工具是idea2023 在idea中新建一个maven项目&#xff0c;虽然可以使用gradle进行构建&#xff0c;不过我这里由于网络问题没有跑通。 pom.xml <dependency>&l…

提示msvcp140.dll丢失的5个解决方法,msvcp140.dll丢失问题全面分析

在我们的日常生活和工作中&#xff0c;电脑已经成为不可或缺的工具。然而&#xff0c;在使用电脑的过程中&#xff0c;我们经常会遇到各种问题&#xff0c;其中就包括提示 msvcp140.dll 丢失的问题。msvcp140.dll 是 Visual C Redistributable for Visual Studio 2015 的运行时…

堆--数组中第K大元素

如果对于堆不是太认识&#xff0c;请点击&#xff1a;堆的初步认识-CSDN博客 解题思路&#xff1a; /*** <h3>求数组中第 K 大的元素</h3>* <p>* 解体思路* <ol>* 1.向小顶堆放入前k个元素* 2.剩余元素* 若 < 堆顶元素, 则略过* …