您好,欢迎来到华佗小知识。
搜索
您的当前位置:首页Java的链表算法一

Java的链表算法一

来源:华佗小知识

链表算法一

160相交链表

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

图示两个链表在节点 c1 开始相交**:**

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

  • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
  • listA - 第一个链表
  • listB - 第二个链表
  • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
  • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headAheadB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。

示例 2:

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。
1.解题方法一:

解题思路:

所以说我们的时间复杂度为O(n+m) 这个是可以化简的也就相当于O(L)

空间复杂度为O(1)

/**
 * 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) {
        if(headA == null || headB == null){
            return null;
        }
        ListNode curA = headA;
        ListNode curB = headB;
        // 这个循环里面的两个if语句判断把headA的长度和headB的长度之和遍历一遍
        while(curA != curB){
            if(curA == null){
                curA = headB;
            }else{
                curA = curA.next;
            }
            if(curB == null){
                curB = headA;
            }else{
                curB = curB.next;
            }
        }
        return curA;
    }
}
2.解题方法二:

解题思路:

看到这个题,我的第一想法就是使用Set集合,因为添加元素的函数add会返回一个boolean类型的数据类型,当添加的元素没有重复的时候就会添加成功返回true , 如果添加的元素重复了就会显示添加失败,然后会返回false

n为headA的长度 , m为headB的长度

所以说我们的时间复杂度为O(n+m) 这个是可以化简的也就相当于O(L)

空间复杂度为O(n)

/**
 * 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) {

        if(headA == null || headB == null) return null;
        Set<ListNode> listA = new HashSet<>();
        ListNode temp = headA;
        ListNode Ttemp = headB;
        while(temp != null){
            listA.add(temp);
            temp = temp.next;
        }

        while( Ttemp != null){
            if(listA.contains(Ttemp)) return Ttemp;
            Ttemp = Ttemp.next;
        }

        return null;

    }
}

206反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

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

示例 2:

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

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

**进阶:**链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

1.解题方法一:

解题思路:

这段代码的核心思想是通过迭代,不断地修改节点的 next 指针,将链表中的节点连接方向反转。最终,链表的头节点变成了尾节点,整个链表得以反转。这是一种常见的反转链表的解决方法,时间复杂度为 O(n),其中 n 是链表的长度。这种方法没有使用额外的空间,因此是一种空间复杂度为 O(1) 的解法。

时间复杂度为 O(n)

空间复杂度为 O(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 ListNode reverseList(ListNode head) {

        // 方法一:迭代
        // if(head == null) return null;
        ListNode p = null , q = head;
        while(q != null){
            ListNode next = q.next;
            q.next = p;
            p = q;
            q = next;
        }
        return p;

    }
}
2.解题方法二:

解题思路:

对于递归这类算法我们需要注意两个点,一是递归的结束条件,如下代码中if(head == null || head.next == null) return head;就是结束条件,当然其还有另一层含义,判断头节点是否为空是否只有一个头节点,这样就不需要反转了。二是注意往下递归根据业务需要进行的操作,这个就是反转链表。

    head.next.next = head;
    head.next = null;

这两段代码是较难理解的他们做的就是反转操作,如下图:

/**
 * 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) {

        // 方法二:递归
        if(head == null || head.next == null) return head;

        ListNode nextNode = reverseList(head.next);
        // 这个是递归进行的反转操作
        head.next.next = head;
        head.next = null;
        return nextNode; // 这个是把操作完的链表返回到上一步继续操作

    }
}

234回文链表

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false

示例 1:

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

示例 2:

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

提示:

  • 链表中节点数目在范围[1, 105]
  • 0 <= Node.val <= 9

**进阶:**你能否用 O(n) 时间复杂度和 O(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) {
        // 方法一:利用栈先进后出的特性
        ListNode tNode = head;
        Stack<Integer> stack = new Stack();
        while(tNode != null){
            stack.push(tNode.val);
            tNode = tNode.next;
        }
        tNode = head;
        while(tNode != null){
            if(tNode.val != stack.pop()) return false;
            tNode = tNode.next;
        }
        return true;
        
    }

}
2.解题方法二:

解题思路:

既然关于中心对称我们就找到中间的元素,然后让中间前面元素或后面元素其中一个进行反转与没有反转的链表进行比较。如果遍历结束后发现没有不相同的,就是回文数。

奇数个节点和偶数个节点还不一样,当为奇数个节点的时候慢节点就会走到奇数节点的中间然后这个时候反转的话就会比左边部分多出一个中间节点,所以判断为奇数个节点的时候让中间节点往前移一位。

!!!!!避坑

不知道大家有没有这种错误的想法,我们为什么不模仿栈就把整个链表进行反转与原链表进行比较,而是要用更麻烦的找到中间节点之后反转一般呢,刚开始想出来的时候我还沾沾自喜,后面尝试的时候就发现不对劲了,怎么一个这样的1 1 2 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) {

        //方法二:利用快慢节点
        ListNode low = head , fast = head;
        while(fast != null && fast.next != null){
            fast = fast.next.next;
            low = low.next;
        }
        // 判断节点个数是偶数还是奇数
        if(fast != null){
            low = low.next;
        }
        
        low = reverse(low);
        fast = head;
        while(low != null){
            if(fast.val != low.val) return false;
            fast = fast.next;
            low = low.next;
        }
        return true;

    }
    public ListNode reverse(ListNode head) {
        ListNode prev = null;
        while (head != null) {
            ListNode next = head.next;
            head.next = prev;
            prev = head;
            head = next;
        }
        return prev;
    }

}

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- huatuo0.cn 版权所有 湘ICP备2023017654号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务