算法记录——链表

news2024/12/22 19:59:45

2.链表

2.1判断是否是回文链表

1.方法一:利用栈反转链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        Stack<ListNode> listNodes = new Stack<>();
        ListNode p = head;
        //利用栈反转链表,判断是否是回文链表
        while (p != null) {//将链表中所有元素入栈
            listNodes.push(p);
            p = p.next;
        }
        while (!listNodes.empty()) {
            if (listNodes.pop().val == head.val) {//
                head = head.next;
            } else {
                return false;
            }
        }
        return true;
    }
}

2.方法2:利用快慢指针

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
    //代表快指针,一次走两步
        ListNode fast = head;
        //代表慢指针,一次走一步
        ListNode slow = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        //退出循环时,如果链表节点是奇数个,快指针在尾节点,慢指针在中点。如果是偶数个,快指针还是在尾节点,慢指针在中点前一个。
        //把右半部分链表反转
        slow = reverseList(slow.next);
        while (slow != null) {
            if (head.val != slow.val) return false;//值不相同,直接返回false
            head = head.next;
            slow = slow.next;
        }
        return true;
    }
    //反转链表
    public static ListNode reverseList(ListNode head) {
        ListNode cur = head;
        ListNode pre = null;
        while (cur != null) {
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }
}

2.2 模板题:反转链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        //cur:用于遍历链表元素的指针,代表当前遍历到的节点,初始化当然为head了
        ListNode cur = head;
        //pre:代表当前cur节点,反转后应该指向的节点。因为cur初始在head,反转以后就是尾节点了指向null,所以pre初始化为null
        ListNode pre = null;
        while(cur != null){//当元素还没遍历完的时候
            //在cur指向pre前,用于保存cur.next,防止链表找不到了。
            ListNode temp = cur.next;
            //让当前节点cur,指向pre
            cur.next = pre;
            //让pre变为反转链表的最前面一个节点
            pre = cur;
            //让cur移动到原链表的头节点
            cur = temp;
        }
        // 注意:pre的含义还是反转链表的头节点!
        return pre;
    }
}

复杂度分析:
时间复杂度 O(N)O(N)O(N) : 遍历链表使用线性大小时间。
空间复杂度 O(1)O(1)O(1) : 变量 pre 和 cur 使用常数大小额外空间。

已经是最优的解法了,还有一种递归方法就不赘述了。

2.3 分割链表(将链表分为小于某个值,等于某个值,大于某个值)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode partition(ListNode head, int x) {
if (head == null || head.next == null) return head;

        //代表小于目标值区域的头和尾
        ListNode h1 = null;
        ListNode t1 = null;
        //代表大于等于目标值的头和尾
        ListNode h2 = null;
        ListNode t2 = null;
        //用于保存head的下一个节点
        //注意:这里最后拼接好了以后,小于区域的头就是整个链表的新的头节点,因此,head可以作为遍历链表的指针。
        ListNode next = head.next;
        while (head != null) {//遍历
            next = head.next;
            head.next = null;
            if (head.val < x) {//如果当前节点的val小于目标值
                if (h1 == null) {//如果当前节点是小于区域的第一个节点
                    h1 = head;
                    t1 = head;
                } else {
                    t1.next = head;
                    t1 = head;
                }
            } else {
                if (h2 == null) {//如果当前节点是大于区域的第一个节点
                    h2 = head;
                    t2 = head;
                } else {//其他情况就把该节点尾插法插入链表中
                    t2.next = head;
                    t2 = head;
                }
            }
            head = next;
        }

        //进行小于区域链表和大于等于区域链表的拼接
        if (h2 == null) {//如果没有大于等于区域
            return h1;
        }
        if (h1 == null) {//如果没有小于区域
            return h2;
        }
        //如果两种区域都有,则让小于区域的尾指针指向大于等于区域的头指针
        t1.next = h2;

        return h1;
    }
}

2.4 随机链表的赋值

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/

class Solution {
    public Node copyRandomList(Node head) {
        //创建一个map,key为老链表的节点。val为新链表的节点
        HashMap<Node,Node> map = new HashMap<Node,Node>();
        Node cur = head;
        //遍历链表,设置map的key和value
        while(cur != null){
            map.put(cur,new Node(cur.val));
            cur = cur.next;
        }
        cur = head;
        //再次遍历老链表,给新链表设置每一个节点的next和random
        while(cur != null){
            //cur 老链表节点
            //map.get(cur) cur对应的新链表
            map.get(cur).next = map.get(cur.next);//设置新链表的next
            map.get(cur).random = map.get(cur.random);//设置新链表的random
            cur = cur.next;
        }
        return map.get(head);
    }
}

2.5环形链表的判断

方法一:利用HashSet集合。

思路:遍历当前链表,每次遍历判断当前节点是否已经存在于set集合中。如果不存在,则把当前节点放入集合。如果已经存在,说明当前节点就是第一个入环节点。

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public boolean hasCycle(ListNode head) {
        //创建一个set,用于存放链表中已经遍历了的节点
        HashSet<ListNode> set = new HashSet<>();
        while(head != null){
            //如果当前节点已经存在于set,说明存在环形结构
            if(set.contains(head)) return true;
            set.add(head);
            head = head.next;
        }
        return false;
    }
}

方法二:快慢指针

开始时,快慢指针都在头节点的位置。快指针一次走两步,慢指针一次走一步。

如果没有环结构,快指针一定先走到尾节点。

如果有环结构,快慢指针会在换里相遇。而相遇所要走的卷数不会大于两圈。

相遇以后,快指针/慢指针到头节点的位置。两个指针开始一次走一步。最终两个指针会在第一次入换节点相遇!(原理就不证明了)

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head == null || head.next == null || head.next.next == null) return null;
        //定义慢指针,一次走一步
        ListNode n1 = head.next;
        //定义快指针,一次走两步
        ListNode n2 = head.next.next;
        while(n1 != n2){//当n1 n2不相遇时循环,所以我开始时没有把两个指针都设置在头节点的位置
            if(n2.next == null || n2.next.next == null){//说明没有环结构,直接返回空
                return null;
            }
            n1 = n1.next;//慢指针一次走一步
            n2 = n2.next.next;//慢指针一次走两步
        }
        //快指针移到头节点,开始一次走一步
        n2 = head;
        while(n1 != n2){//当两个指针相遇时,就走到了第一个入环节点
            n1 = n1.next;
            n2 = n2.next;
        }
        return n1;
    }
}

2.6 链表相交

思路:两个单链表,如何判断有没有相交点呢?

1.先遍历两个链表,到尾节点时停止。如果这时候,两个链表的尾节点都不想等。说明二者不相交。

2.如果二者尾节点是同一个,则计算二者链表长度的差值。让长的链表先走差值个距离。然后,短的链表从头开始走,二者一定会在相交点相遇!

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //定义两个指针用于遍历两条链表
        ListNode cur1 = headA;
        ListNode cur2 = headB;
        int n = 0;//用于记录两条链表的差值
        while(cur1.next != null){
            cur1 = cur1.next;
            n++;
        }
        while(cur2.next != null){
            cur2 = cur2.next;
            n--;
        }
        if(cur1 != cur2){//尾节点都不想等,说明二者不相交
            return null;
        }
        //这样遍历完两条链表,n就是两条链表的长度差
        cur1 = n > 0 ? headA : headB;//让cur1指向两条链表中长的那一条
        cur2 = cur1 == headA ? headB : headA;//让cur2指向两条链表中短的那一条
        n = Math.abs(n);//n取绝对值
        while(n != 0){//让长的那条链表先移动两条链表差值的距离,再一起走,就会在相交部分汇合!
            cur1 = cur1.next;
            n--;
        }
        while(cur1 != cur2){
            cur1 = cur1.next;
            cur2 = cur2.next;
        }
        return cur1;
    }
}

2.7.两数相加

思路:

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
   public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            ListNode res = new ListNode();
            ListNode cur = res;
            int carry = 0;
            //当l1、l2中有一个不是空节点,或者还有进位,就继续循环
            while (l1 != null || l2 != null || carry != 0) {
                if (l1 != null) carry += l1.val;
                if (l2 != null) carry += l2.val;
                cur.next = new ListNode(carry % 10);//carry%10 就是该点的val
                cur = cur.next;
                carry = carry / 10;// carry/10 就是下一个点的进位
                if (l1 != null) l1 = l1.next;//l1 没有遍历完
                if (l2 != null) l2 = l2.next;
            }
            return res.next;
        }
}

2.8.合并两个有序链表

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
            ListNode res = new ListNode();
            ListNode cur = res;

            while (list1 != null && list2 != null) {
                if (list1.val <= list2.val) {//如果l1链表的值更小
                    cur = cur.next = list1;
                    list1 = list1.next;
                } else {
                    cur = cur.next = list2;
                    list2 = list2.next;
                }
            }
            while (list1 != null) {//如果1还没遍历完
                cur = cur.next = list1;
                list1 = list1.next;
            }
            while (list2 != null) {//如果2还没遍历完
                cur = cur.next = list2;
                list2 = list2.next;
            }
            return res.next;
        }
}

2.9 反转链表2

题解参考:leetcode

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseBetween(ListNode head, int left, int right) {
        ListNode dummy = new ListNode(0, head);
        ListNode g = dummy;
        ListNode p = dummy;
        //g指向的下一个节点就是要开始反转的节点
        for (int i = 0; i < left - 1; i++) {
            g = g.next;
            p = p.next;
        }
        //p指向第left个节点
        p = p.next;
        for (int i = 0; i < right - left; i++) {
            ListNode temp = p.next;
            p.next = p.next.next;
            temp.next = g.next;
            g.next = temp;
        }

        return dummy.next;
    }
}

2.10.K个一组反转链表

        本题思路和上一题差不多。举一反三,还是主要用g、p两个指针反转链表。

        每组链表反转之前,g的next指向的都是待反转链表的第一个节点,p指向的就是待反转链表的第一个节点。
        要注意的就是每次反转完链表p指针指向的就是反转后链表的最后一个元素,同时它的next也是下一组待反转链表的第一个元素,所以每次每组反转完以后,都要把p赋值给g。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
         ListNode dummy = new ListNode(0, head);
        ListNode g = head;
        //计算一共有多少个节点,用来算要反转几组链表
        int count = 0;
        while (g != null) {
            g = g.next;
            count++;
        }
        g = dummy;
        ListNode p = g.next;
        //遍历
        for (int i = 0; i < count / k; i++) {
            p = g.next;
            //反转的每组链表
            for (int j = 1; j < k; j++) {
                ListNode temp = p.next;
                p.next = p.next.next;
                temp.next = g.next;
                g.next = temp;
            }
            //每组链表反转完,让cur的next指向下一组待反转链表第一个
            g = p;
        }

        return dummy.next;
    }
}

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

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

相关文章

IO(输入输出流)

1.IO a.介绍 i.IO是指Input和Output&#xff0c;即输入和输出&#xff0c;以内存为中心&#xff1a; 1.Input是指从外部读入数据到内存。 2.Output是指把数据从内存输出到外部。 ii.IO流是一种顺序读写数据的模式&#xff0c;它的特点是单向流动。数据类似自…

【AIGC】ChatGPT提示词解析:如何生成爆款标题、节日热点文案与完美文字排版

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;情绪化的吸睛爆款标题提示词使用方法 &#x1f4af;紧跟节日热点选题文案提示词使用方法 &#x1f4af;高效文字排版技巧提示词使用方法 &#x1f4af;小结 &#x1f4af…

python-获取浏览器静态/动态素材

f12浏览器中 1&#xff1a;静态爬取 2.动态资源图片获取。斗鱼 3获取视频-抖音 一长串&#xff0c;最后一个http就是视频

Unity-物理系统-碰撞检测-物理材质

物理材质的作用&#xff1a;改变碰撞效果 因为碰撞的过程是相互的&#xff0c;所以在碰撞双方都要加相同的物理材质才能实现效果 物理材质创建 参数

【FPGA】IO电平标准

【FPGA】IO 电平标准 1 LVCMOS&#xff08;低压CMOS&#xff09;1.1 TTL、CMOS、LVTTL、LVCMOS逻辑电平定义1.2 ZYNQ-7000 PS、PL IO Level示例 2 LVTTL&#xff08;低压TTL&#xff09;3 HSTL&#xff08;高速TTL&#xff09;4 SSTL&#xff08;高速&#xff09;5 LVDS&#x…

Pytest测试实战|Conftest.py详解

Pytest测试实战 本文章主要详细地阐述下Pytest测试框架中Conftest.py特性。 Conftest.py实战 Fixture强大的特性在实际的工作中是非常有价值并且是实用的&#xff0c;这样可以根据需求&#xff0c;在对应的测试模块中编写Fixture函数来达到测试需求的目的。但是这样也产生了…

【面经合集】Java基础精选面试题(三)

最近&#xff0c;小编整理了不少Java领域面试题&#xff0c;如有需要&#xff0c;点击关注&#xff0c;回复【面试题】&#xff0c;即可下载。 31 、说说List,Set,Map三者的区别&#xff1f; List、Set、Map是三种不同数据结构的集合&#xff0c;它们的主要区别体现在存储方式…

Linux命令:用来列出当前系统内核加载的所有模块的工具lsmod详解

目录 一、概述 二、 使用方法 三、 输出格式 四、 示例 五、 相关命令 六、 高级用法 1、结合管道符 | 和 grep 命令 2、结合其他命令使用 七、应用和注意 一、概述 lsmod 是一个 Linux 命令行工具&#xff0c;用来列出当前内核加载的所有模块。这个命令对于了解哪些模…

分布式计算技术是什么?在数据集成值得作用?

数据是现代科技技术的基础&#xff0c;面对爆炸性数据的增长&#xff0c;要求计算能力要求更高、数据整合和处理更有效&#xff0c;如何应对数据集成带来的挑战&#xff1f;本文将探讨分布式计算技术在数据集成中的优化作用。 一 分布式计算技术。 定义&#xff1a;分布式计算…

《机器学习by周志华》学习笔记-神经网络-02感知机与多层网络

1、感知机 1.1、概念 感知机(Perceptron)由2层神经元模型组织,如下图所示: 「输入层神经元」接收外界输入信号后,传递给「输出层神经元 」 「输出层神经元」是「M-P神经元」,亦称「阈值逻辑单元(threshold logic unit)」 1.2、作用 「感知机」能够容易的实现逻辑「与…

初识前端监控

以下笔记来源&#xff1a;黑马程序员 背景 思考一下&#xff0c;我们的项目代码在上线之后是不是就不用管了呢&#xff1f; 并不是&#xff0c;作为前端开发工程师&#xff0c;我们是直接跟用户打交道的&#xff0c;一个应用的用户留存率跟这个应用的稳定性有很大的关系&…

NXP i.MX8系列平台开发讲解 - 4.2.1 摄像头篇(一) - 认识摄像头模组

专栏文章目录传送门&#xff1a;返回专栏目录 Hi, 我是你们的老朋友&#xff0c;主要专注于嵌入式软件开发&#xff0c;有兴趣不要忘记点击关注【码思途远】 文章目录 目录 1. 引言 2. 嵌入式系统中的CCM应用 3. 摄像头模组的基本组成 4. 摄像头模组的封装工艺 5. 摄像头…

GORM入门

ORM框架 什么是ORM ORM优缺点 GORM介绍 Github GORM 中文官方网站 安装 go get -u github.com/jinzhu/gorm连接数据库 连接不同的数据库都需要导入对应数据的驱动程序&#xff0c;GORM已经包装了一些驱动程序&#xff0c;只需要按如下方式导入需要的数据库驱动即可&#…

Python 方法传参详解

参数 位置参数 和关键字参数 data{error: str(e)}&#xff1a;传递给了 __init__ 方法中的 data 参数&#xff0c;表示需要返回给客户端的 JSON 数据。status500&#xff1a;通过 **kwargs 传递给了父类 HttpResponse&#xff0c;并设置了响应状态码为 500。 位置参数 和关键字…

redis Redis-Cluster常用命令与Redis性能监控

起因&#xff1a;随着项目的进一步推广&#xff0c;数据量的增大&#xff0c;直接访问mysql数据库获取数据所使用的时间越来越长&#xff0c;为解决当前主要矛盾&#xff0c;决定引入redis非关系型数据库作为缓存层&#xff0c;使得数据并不能直接命中数据库&#xff0c;减少访…

重生之我在代码随想录刷算法第十三天 | 110.平衡二叉树、257. 二叉树的所有路径、404.左叶子之和、222.完全二叉树的节点个数

参考文献链接&#xff1a;代码随想录 本人代码是Java版本的&#xff0c;如有别的版本需要请上代码随想录网站查看。 110.平衡二叉树 力扣题目链接 解题思路 这道题目刚看到以为和二叉树的最大深度差不多&#xff0c;上来写了一堆迭代求深度的代码结果发现不对劲。 看了题…

非标精密五金加工的技术要求

非标精密五金加工在现代制造业中占据着重要地位&#xff0c;其对于产品的精度、质量和性能有着较高的要求。以下是时利和整理的其具体的技术要求&#xff1a; 一、高精度的加工设备 非标精密五金加工需要先进的加工设备来保证加工精度。例如&#xff0c;高精度的数控机床是必不…

【越学学糊涂的Linux系统】Linux指令篇(2)

一、echo指令&#xff1a; ✔️✔️在终端中显示文本内容或向文件中写入文本 Ⅰ.基本用法&#xff1a; 0x00打印字符串&#xff1a; 打印字符串/显示文本内容&#xff1b;可以用双引号作为文本内容⬇️⬇️更推荐用单引号 这里我将字符串打印出来了。和printf的功能一样&…

【Linux实践】实验五:用户和组群账户管理

【Linux实践】实验五&#xff1a;用户和组群账户管理 实验目的实验内容实验步骤及结果1. 创建用户2. 切换用户3. 修改用户4. 删除用户5. 创建组群6. 修改组群补充&#xff1a;删除组群 实验目的 1、掌握字符界面下用户账户的设置&#xff0c;包括命令useradd、usermod、userde…

深入解析 Apache Kylin

以下是关于 Kylin 概述 部分的内容&#xff0c;你可以在技术博客中使用&#xff1a; 1. Kylin 概述 什么是 Apache Kylin&#xff1f; Apache Kylin 是一个开源的分布式分析引擎&#xff0c;主要为大数据场景下的 OLAP&#xff08;Online Analytical Processing&#xff09;提…