LeetCode - 刷题记录(3)

这几道题是LeetCode上标签“链表”、列表“热题Top100”、难度为“简单”的几道题目,题解有官方题解也有个人题解,有的地方意思可能表达得不是很清楚也可能存在错误,有问题请提出,感谢

一.环形链表

题目描述

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是
-1,则在该链表中没有环。

示例 1

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

在这里插入图片描述

示例 2

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

在这里插入图片描述

示例 3

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

在这里插入图片描述

进阶

你能用 O(1)(即,常量)内存解决此问题吗?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-cycle

题解

1.之前在别的地方做过这道题😄,看到题目就直接用双指针了,O(1)内存解决。
像跑操场,first指针比second指针快走两个节点,若链表有环,两个指针一定会在某个节点相遇。

/**
 * 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) {
        if (head == null || head.next == null) { return false; }
        ListNode first = head;
        ListNode second = head.next.next; 
        while (first != second ) {
            if (second == null || second.next == null) {
                return false;
            }
            first = first.next;
            second = second.next.next;
        }
        return true;
    }
}

在这里插入图片描述
2.O(n)的额外空间,用一个集合存放遍历过的节点,当遍历的节点已存在集合中,则证明链表有环。

/**
 * 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<ListNode> containMap = new HashSet();
        while (head != null) {
            if (containMap.contains(head)) { return true; }
            containMap.add(head);
            head = head.next;
        }
        return false;
    }
}

在这里插入图片描述

二.相交链表

题目描述

编写一个程序,找到两个单链表相交的起始节点。

如下面的两个链表:

在节点 c1 开始相交。

示例 1

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

示例 2

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Reference of the node with value = 2
输入解释:相交节点的值为 2
(注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [0,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。

注意

如果两个链表没有交点,返回 null.
在返回结果后,两个链表仍须保持原有的结构。
可假定整个链表结构中没有循环。 程序尽量满足 O(n)时间复杂度,且仅用 O(1) 内存。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists

题解

这道题可以直接暴力解,循环链表A的每个节点看链表B的每个节点是否有同个节点的情况,双重循环(注意记得先保存第二重循环的链表的头部,以免循环过程中头部引用丢失)。下面列出另外两种解法。

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) {
        Set<ListNode> containSet = new HashSet();
        if (headA == null || headB == null) { return null; }
        while (headA != null) {
            containSet.add(headA);
            headA = headA.next;
        }
        while (headB != null) {
            if (containSet.contains(headB)) { return headB; }
            headB = headB.next;
        }
        return null;
    }
}

三.反转链表

题目描述

反转一个单链表。

示例:

输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL

进阶:

你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-linked-list

题解

1.迭代方式反转链表,
假设curNode是当前遍历节点,preNode是上一个节点,nextNode是下一个节点。
若要反转,curNode.next = preNode,然后再引用nextNode节点。
因此在每次遍历过程中,需保存上一个节点preNode和下一个节点nextNode

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
public ListNode reverseList(ListNode head) {
    ListNode preNode = null;
    ListNode currentNode = head;
    ListNode nextNode = null;
    while (currentNode != null) {
        nextNode = currentNode.next;
        currentNode.next = preNode;
        // 对于下个节点来说,当前节点为上个节点
        preNode = currentNode;
        currentNode = nextNode;
    }
    return preNode;
    }
}

2.递归方式反转列表。递归操作可以让我们直接走到链表的最后一个节点进行操作,再一步步退回到头节点。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
public ListNode reverseList(ListNode head) {
	// 递归结束条件,到达最后一个节点则返回该节点的引用
    if (head == null || head.next == null) { return head; }
    ListNode newHead = reverseList(head.next);
    // 反转
    head.next.next = head;
    head.next = null;
    return newHead;
	}
}

四.回文链表

题目描述

请判断一个链表是否为回文链表。

示例 1:

输入: 1->2
输出: false

示例 2:

输入: 1->2->2->1
输出: true

进阶

你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

题解

1.刚开始没啥好的想法,想不到按进阶的要求完成题目。就还是新建了个栈,遍历一遍链表将节点放入栈。再遍历一遍链表并从栈中取节点值判断是否相同。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public boolean isPalindrome(ListNode head) {
        ListNode indexHead = head;
        Stack<ListNode> stack = new Stack();
        while (head != null) {
            stack.push(head);
            head = head.next;
        }
        while ( !stack.isEmpty() ) {
            if (stack.pop().val != indexHead.val ) { return false; }
            indexHead = indexHead.next;
        }
        return true;
    }
}

2.官方题解:用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题,反转后半部分链表,判断前后部分链表值是否一致(判断是否回文),最后要恢复链表

class Solution {

    public boolean isPalindrome(ListNode head) {

        if (head == null) return true;
        
        ListNode firstHalfEnd = endOfFirstHalf(head);
        ListNode secondHalfStart = reverseList(firstHalfEnd.next);

        ListNode p1 = head;
        ListNode p2 = secondHalfStart;
        boolean result = true;
        // 因为需要先恢复链表后才能返回结果,所以用result暂存结果
        while (result && p2 != null) {
            if (p1.val != p2.val) result = false;
            p1 = p1.next;
            p2 = p2.next;
        }        
        // 恢复链表
        firstHalfEnd.next = reverseList(secondHalfStart);
        return result;
    }
    
	public ListNode reverseList(ListNode head) {
    	ListNode preNode = null;
    	ListNode currentNode = head;
    	ListNode nextNode = null;
    	while (currentNode != null) {
        	nextNode = currentNode.next;
        	currentNode.next = preNode;
        	// 对于下个节点来说,当前节点为上个节点
        	preNode = currentNode;
        	currentNode = nextNode;
    	}
    	return preNode;
    }
	
	// 快指针一次走两步,慢指针一次走一步,当快指针到达尾部时,慢指针处于
    private ListNode endOfFirstHalf(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值