编程导航算法通关村第 1关 | 两个链表的第一个公共节点

news2024/11/28 2:44:00

编程导航算法通关村第 1关 | 白银挑战

剑指 Offer 52. 两个链表的第一个公共节点

在这里插入图片描述

集合/map

  • 将headA中的链表,放在一个set集合中, 依次遍历headB, headB中第一个包含在set集合中的节点就是第一个公共子节点
ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 将headA压入集合中
        Set<ListNode> statckA = new HashSet<>();
        ListNode temp = headA;
        while (temp != null) {
            statckA.add(temp);
            temp = temp.next;
        }

        ListNode tempB = headB;
        while (tempB != null) {
            if (statckA.contains(tempB)) {
                return tempB;
            }

            tempB = tempB.next;
        }
        return null;
    }

  • 将两个链表的节点分别压入栈中,然后同时出栈,当两个栈出栈的元素相等是,说明这个节点是公共子节点,当最后一个相等的元素出栈时,就是第一个公共子节点
 /**
     * 方法二:将两个链表的节点分别压入栈中,然后同时出栈,
     * 当两个栈出栈的元素相等是,
     * 说明这个节点是公共子节点,当最后一个相等的元素出栈时,就是第一个公共子节点
     */
    ListNode getIntersectionNodeByStack(ListNode headA, ListNode headB) {
        //    同时压入两个栈中
        ListNode tempA = headA;
        ListNode tempB = headB;

        Stack<ListNode> stackA = new Stack<>();
        Stack<ListNode> stackB = new Stack<>();

        while (tempA != null) {
            stackA.push(tempA);
            tempA = tempA.next;
        }


        while (tempB != null) {
            stackB.push(tempB);
            tempB = tempB.next;
        }
        ListNode result = null;
        while (stackA.size() != 0 && stackB.size() != 0) {
            ListNode popA = stackA.pop();
            ListNode popB = stackB.pop();
            if (popA == popB) {
                result = popA;
            } else {
                break;
            }
        }

        return result;
    }

拼接两个字符串

在这里插入图片描述

 /**
     * 分别遍历两个链表
     * headA+headB;
     * headB+headA
     * 找到的第一个相等的点,就是两个的第一个公共子节点
     */
    ListNode getIntersectionNodeByString(ListNode headA, ListNode headB) {
        ListNode tempA = headA;
        ListNode tempB = headB;

//        考虑链表为空的情况
        if (headA == null) {
            return null;
        }
        if (headB == null) {
            return null;
        }
        while (tempA != tempB) {
            tempA = tempA.next;
            tempB = tempB.next;

            if (tempA != tempB) {
                if (tempA == null) {
                    tempA = headB;
                }
                if (tempB == null) {
                    tempB = headA;
                }
            }
        }

        return tempA;
    }

差和双指针

  • 先各自统计链表的长度
  • 计算长度的差值
  • 较长的链表移动差值距离
  • 同时向后移动,第一个相等节点便是第一个公共子节点

 ListNode getIntersectionNodeBysubA(ListNode headA, ListNode headB) {
        ListNode tempA = headA;
        ListNode tempB = headB;

//        考虑链表为空的情况
        if (headA == null) {
            return null;
        }
        if (headB == null) {
            return null;
        }
//       计算长度
        int lengthA = 0;
        int lengthB = 0;
        while (tempA != null) {
            tempA = tempA.next;
            lengthA++;
        }

        while (tempB != null) {
            tempB = tempB.next;
            lengthB++;
        }

//        计算差值
        int sub = Math.abs(lengthA - lengthB);

        tempA = headA;
        tempB = headB;
        if (lengthA > lengthB) {
            int i = 0;
            while (i < sub) {
                tempA = tempA.next;
                i++;
            }
        } else if (lengthB > lengthA) {
            int i = 0;
            while (i < sub) {
                tempB = tempB.next;
                i++;
            }
        }
//        同时向后移动
        while (tempA != null && tempB != null) {
            if (tempA == tempB) {
                return tempA;
            }
            tempA = tempA.next;
            tempB = tempB.next;
        }
        return null;
    }

判断是否是回文序列

使用栈

  • 将链表中的元素全部压入栈中,遍历链表,与栈中弹出的元素是否相等,如何全部相等,则为回文序列
    public boolean isPalindrome(ListNode head) {
        Stack<ListNode> listNodes = new Stack<>();

        ListNode temp = head;

        while (temp != null) {
            listNodes.push(temp);
            temp = temp.next;
        }

//
        temp = head;
        while (temp != null) {

            ListNode pop = listNodes.pop();
            if (temp.val != pop.val) {
                return false;
            }
            temp=temp.next;
        }

        return true;


    }

优化:只遍历一半,将前一半与后一半进行对比

    public boolean isPalindrome(ListNode head) {
        Stack<ListNode> listNodes = new Stack<>();

        ListNode temp = head;

        int length = 0;
        while (temp != null) {
            listNodes.push(temp);
            temp = temp.next;
            length++;
        }

//
//        计算一半的值

        int cout = length / 2;
        temp = head;
        int i = 0;
        while (temp != null && i < cout) {

            ListNode pop = listNodes.pop();
            if (temp.val != pop.val) {
                return false;
            }
            temp = temp.next;
            i++;
        }

        return true;


    }

快慢指针+一半反转法

  • 首先,如果链表为空或者只有一个节点,那么它肯定是回文的,所以直接返回true。

  • 创建两个指针slow和fast,都指向链表的头部。slow指针每次移动一步,fast指针每次移动两步。这样,当fast指针到达链表的末尾时,slow指针就会在链表的中间。

  • 同时,我们还创建了pre和prepre两个指针,用于反转链表的前半部分。pre指针始终指向slow指针的前一个节点,prepre指针始终指向pre指针的前一个节点。在每次循环中,我们都将pre指针的next指向prepre,然后将prepre和pre分别更新为pre和slow,这样就可以反转链表的前半部分。

  • 如果链表的长度是奇数,那么fast指针会停在最后一个节点上,此时slow指针指向的是链表的中间节点,我们需要将slow指针向后移动一步,跳过中间节点。

  • 最后,我们同时遍历反转后的前半部分链表(从pre指针开始)和后半部分链表(从slow指针开始),如果发现有节点的值不相等,那么就返回false。如果所有节点的值都相等,那么就返回true。

  • 这个函数的时间复杂度是O(n),空间复杂度是O(1),其中n是链表的长度。

    public static boolean isPalindromeByFastAndSlow(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;

//        当fast到达末尾时,slow正好到达中间
        ListNode pre = null;
        while (fast != null && fast.next != null) {
            ListNode next = slow;
            fast = fast.next.next;
            slow = slow.next;

//            进行翻转
            next.next = pre;
            pre = next;
//            next = slow;

        }
        System.out.println(pre.val);
        System.out.println(slow.val);
        if (fast != null) {
            slow = slow.next;
        }
//        此时pre就是翻转后链表的头结点,
//        slow是后半部分的头结点

        while (pre != null && slow != null) {
            if (pre.val != slow.val) {
                return false;
            }
            pre = pre.next;
            slow = slow.next;
        }

        return true;

//
    }

删除倒数第N个元素

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

链表中结点的数目为 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz

进阶:你能尝试使用一趟扫描实现吗?

Related Topics 链表 双指针 👍 2586 👎 0

public ListNode removeNthFromEnd(ListNode head, int n) {

        ListNode listNode = new ListNode(0);
        listNode.next = head;
        if (head == null) {
            return null;
        }

//        if ( head.next == null &&  n == 1 ) {
//            return null;
//        }

        ListNode fast = head;
        ListNode slow = listNode;

//
        int i = 0;
        while (i < n) {
            fast = fast.next;
            i++;
        }

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


//        删除slow 下一个节点的位置
        slow.next = slow.next.next;

        return listNode.next;

    }

删除重复元素

  • 设置虚拟头结点,防止出现删除head节点的情况
  • 保留重复的值,直接删除
 /**
     *
     * 删除重复元素
     * */
    public ListNode deleteDuplicatesNo(ListNode head) {
        ListNode doumoNode = new ListNode(0);
        doumoNode.next = head;
        ListNode temp = doumoNode;
        while (temp != null && temp.next != null && temp.next.next != null) {
            if (temp.next.val == temp.next.next.val) {
                int x = temp.next.val;
                while (temp.next != null && temp.next.val == x) {
                    temp.next = temp.next.next;
                }
            } else {
                temp = temp.next;
            }
        }
        return doumoNode.next;
    }

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

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

相关文章

网络安全专业必考的三个证书,初学者建议收藏!

学习网络安全&#xff0c;有三个必考证书&#xff0c;最后一个人手一本&#xff01; 一&#xff0c;NISP证书 NISP即国家信息安全水平考试&#xff0c;由中国信息安全测评中心发证&#xff0c;属于网络安全专业的必考证书。 只有考取NISP一级证书才有机会考取二级&#xff0…

【动手学深度学习】--10.卷积神经网络

文章目录 卷积神经网络1.从全连接层到卷积1.1全连接层1.2平移不变性1.3局部性1.4卷积 2.卷积层2.1互相关计算2.2卷积层2.3图像中目标的边缘检测2.4学习卷积核 3.填充和步幅3.1填充3.2步幅 4.多输入多输出通道4.1多输入通道4.2多输出通道4.3 11卷积层 5.池化层5.1池化层5.2填充和…

设计准则必备:学习UI设计规范的五大黄金法则

看到好看的设计&#xff0c;随手保存起来&#xff0c;这是设计师的基本习惯。但是如果只是好看&#xff0c;并不能驱使受众真正去了解产品。如果想要用户动心&#xff0c;还是需要了解一些设计心理学&#xff0c;从用户心理去引导用户行为。今天给大家分享一些常用的设计法则帮…

对话网易伏羲赵增:开源VS自研?哪条路是通向AIGC的捷径?|WAIC2023

点击关注 文 | 郝鑫、黄小艺 从去年底到现在&#xff0c;国内外肉眼可见地涌现出了一批文生图公司&#xff0c;这背后与基础架构开源有很大关系。 2022年8月&#xff0c;Stability.AI在GitHub上公开开源了Stable Diffusion模型的代码和训练数据集&#xff1b;当月底&#xf…

linux系统编程-文件系统

目录 1文件存储 1.1 inode 1.2 dentry 2.文件系统 2.1 stat函数 2.2 lstat函数 2.3特殊权限位、黏住位 2.4 access函数 2.5 chmod函数 2.6 truncate函数 2.7 link函数 2.8 unlink函数、 2.9 隐式回收 2.10 symlink函数 2.11 readlink函数 2.12 rename函数 2.1…

Java 常用的重构技巧指南 v1.0

前段时间&#xff0c;leader 在 review 代码的时候发现了代码中 存在的一部分的问题&#xff0c;导致 代码的复杂度太高了&#xff0c;包括大部分的sql 都是属于慢sql &#xff0c;还是在建立了索引的情况下 , 代码的流程过于臃肿&#xff0c;而且本人编码的习惯&#xff0c;习…

Mybatis-Plus(一)--Mybatis-Plus介绍与快速入门

阅读这篇文章之前确保你已经学过springboot和mybatis 一.Mybtis-Plus介绍 【1】Mybatis-Puls&#xff08;简称MP&#xff09;是一个MyBatis的增强工具&#xff0c;在MyBatis的基础上只做增强不做改变&#xff0c;为简化开发&#xff0c;提高效率而生。 简单说就是其实很多sql语…

IntelliJ IDEA下载安装教程

目录 友情提醒第一章、IDEA软件下载1.1&#xff09;官网下载 第二章、IDEA软件安装2.1&#xff09;以"ideaIU-2018.3.3.exe"为例3.2&#xff09;启动IDEA软件 友情提醒 点击文章目录可以跳转 第一章、IDEA软件下载 IDEA 全称 IntelliJ IDEA。在智能代码助手、代码…

字节跳动春招研发部分编程题汇总

状压dp(不会) http://t.csdn.cn/W9Pi2 #include <iostream> #include<string.h> #include<math.h> using namespace std; char a[1005]; char c[1005]; int main() {int n;scanf("%d",&n);for(int i1;i<n;i){scanf("%s",a);int l…

Pytorch学习笔记(1)

目录 1. 张量&#xff08;Tensors&#xff09; 2. 自动求导&#xff08;Automatic Differentiation&#xff09; 3. 神经网络的构建和训练 送书活动 PyTorch是一个基于Python的开源机器学习库&#xff0c;广泛应用于深度学习和人工智能领域。它提供了丰富的工具和函数&…

什么是域服务(NETBASE第十一课)

域服务(NETBASE第十一课) web虚拟主机(一台服务器运行多个WEB站点) Web虚拟主机的实现方法&#xff1a; 1&#xff09;同IP不同端口&#xff08;基于端口的虚拟主机&#xff09; 2&#xff09;同端不同IP&#xff08;基于IP的虚拟主机&#xff09; 3&#xff09;同端口同I…

使用typora+PicGo+Gitee简单实现图片上传功能

本文通过配置PicGoGitee来实现typora图片上传功能&#xff0c;系统是window 注意下载的清单有&#xff1a;PicGo&#xff0c;node.js&#xff0c;配置有&#xff1a;PicGo&#xff0c;node.js&#xff0c;gitee&#xff0c;typora 看着复杂实际上并不难&#xff0c;只是繁琐&am…

OLLVM虚假控制流源码分析

文章目录 runOnFunction函数bogus函数目前源码&#xff1a;addBogusFlow函数1createAlteredBasicBlock函数原基本块&#xff1a;copy的基本块&#xff1a; addBogusFlow函数2 runOnFunction函数 if (ObfTimes < 0) {errs()<<"BogusControlFlow application numb…

缓存淘汰策略

LRU 与 LFU 缓存策略及其实现。 应用层缓存 鉴于磁盘和内存读写的差异性&#xff0c;DB 中低频写、高频读的数据适合放入内存中&#xff0c;直接供应用层读写。在项目中读取用户资料时就使用到了 LRU&#xff0c;而非放到 Redis 中。 缓存的 2 个基本实现 Set(key string, v…

RandLA-Net 复现

GPU3090 CUDA12 1、代码 [github地址] git clone --depth1 https://github.com/QingyongHu/RandLA-Net && cd RandLA-Net 2、虚拟环境中配置&#xff1a; 在跑代码的时候出现错误&#xff1a;open3d.so文件中函数报错。查看open3d版本发现不是要求的0.3版本&#xff…

基于PyQt5的UI界面开发——信号与槽

信号与槽的机制 PyQt5采用了一种被称为“信号与槽”机制的编程模式&#xff0c;用于处理对象间的通信和事件处理。在PyQt5中&#xff0c;信号&#xff08;signal&#xff09;是对象发出的特定事件&#xff0c;例如按钮被点击、文本被修改等。而槽&#xff08;slot&#xff09;…

攻不下dfs不参加比赛(十七)

标题 为什么练dfs题目为什么练dfs 相信学过数据结构的朋友都知道dfs(深度优先搜索)是里面相当重要的一种搜索算法,可能直接说大家感受不到有条件的大家可以去看看一些算法比赛。这些比赛中每一届或多或少都会牵扯到dfs,可能提到dfs大家都知道但是我们为了避免眼高手低有的东…

WooCommerce企业级电子商务需要了解的事情

建立成功的企业业务变得比以往任何时候都容易得多。借助各种可用的平台&#xff0c;将您的想法付诸实践是绝对可行的。 “WooCommerce 是最知名的 WordPress 网站电子商务平台之一。” 它于 2011 年推出&#xff0c;自此受到大型和小型企业的欢迎。它的流行主要归功于其各种免费…

【接口流程分析】唯品会WEB端

唯品会WEB端 来看看唯品会是怎么回事&#xff0c; 地址&#xff1a;aHR0cHM6Ly93d3cudmlwLmNvbS8 https://github.com/Guapisansan/gpss_learn_reverse 代码在这里&#xff0c;会持续更新逆向案例 免责声明&#xff1a; 此文档&#xff0c;以及脚本&#xff0c;仅用来对技术的…

七年老程序员的五六月总结:十一件有意义的事

你好&#xff0c;我是拭心&#xff0c;一名工作七年的安卓开发。 每两个月我会做一次总结&#xff0c;记下这段时间里有意义的事和值得反复看的内容&#xff0c;为的是留一些回忆、评估自己的行为、沉淀有价值的信息。 最近两周的我一直处于“战斗“状态&#xff0c;同时做好…