LeetCode刷题1(链表)

链表是一种线性数据结构,其中每个节点都包含数据部分和指向下一个节点的指针。对于单向链表来说,一旦你知道了第一个节点(也就是头节点),你就可以通过跟随每个节点的 next 指针找到链表中的所有其他节点,直到你到达一个 next 指针为 null 的节点,这表示你已经到达了链表的末尾。

这里有几个关键点需要注意:

  1. 访问顺序:在单向链表中,你只能从第一个节点开始,顺序地访问到链表的末尾。你不能随机访问链表中间的节点,除非你从头节点开始遍历。

  2. 头节点的重要性:头节点是链表的入口点。如果头节点的引用丢失,整个链表可能就会丢失,除非你有其他方式可以访问到链表中的某个节点。

  3. 链表的遍历:要访问链表中的所有节点,你需要从头节点开始,然后依次访问每个节点的 next 节点,直到 nextnull

  4. 链表的操作:在链表上执行的大多数操作,如插入、删除节点等,都需要从头节点开始,或者至少需要某个特定节点的前一个节点的引用。

  5. 双向链表:在双向链表中,每个节点还有一个指向前一个节点的指针,这允许你从任一节点开始,向两个方向遍历链表。

  6. 循环链表:在循环链表中,最后一个节点的 next 指针指向头节点,形成一个闭环。这意味着你可以从头节点开始,无限次地遍历整个链表。

因此,对于单向链表来说,确实只要知道了第一个节点,就能通过逐个节点的 next 指针找到所有其他节点。这也是为什么在很多链表操作中,头节点的引用是非常重要的。 

1)给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

你可以按任意顺序返回答案。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入:nums = [3,2,4], target = 6
输出:[1,2]

示例 3:

输入:nums = [3,3], target = 6
输出:[0,1]
import java.util.HashMap;
import java.util.Map;

public class Solution {

    //暴力破解
    /*public int[] twoSum(int[] nums, int target) {
        for (int i = 0; i < nums.length; i++) {
            for (int j = i+1; j < nums.length; j++) {
                if (nums[i] + nums[j] == target) {
                    return new int[]{i, j};
                }
            }
        }
        throw new IllegalArgumentException("No two sum solution");
    }*/

    public static void main(String[] args) {
        Solution solution = new Solution();


        int[] nums1 = {3, 7, 11, 15, 5};
        int target1 = 18;
        int[] result1 = solution.twoSum(nums1, target1);
        System.out.println("[" + result1[0] + "," + result1[1] + "]");
    }

    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> numToIndex = new HashMap<>();


        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];

            if (numToIndex.containsKey(complement)) {

                return new int[]{numToIndex.get(complement), i};
            }
            numToIndex.put(nums[i], i);

        }
    

        throw new IllegalArgumentException("No two sum solution");
    }
}


(2)给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:
输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

提示:
每个链表中的节点数在范围 [1, 100] 内
0 <= Node.val <= 9
题目数据保证列表表示的数字不含前导零
package leetcode;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Solution {
    Logger logger = LoggerFactory.getLogger(Logger.class);

    public static void main(String[] args) {
        Solution solution = new Solution();
        //创建第一个链表l1 = [2,4,3]
        ListNode l1 = new ListNode(2);
        l1.next = new ListNode(4);
        l1.next.next = new ListNode(3);

        //创建第二个链表l2 = [5,6,4]
        ListNode l2 = new ListNode(5);
        l2.next = new ListNode(6);
        l2.next.next = new ListNode(4);

        ListNode result = solution.addTwoNumbers(l1, l2);
        printList(result);

    }

    public static void printList(ListNode head) {
        ListNode current = head;
        while (current != null) {
            System.out.print(current.val + "->");
            current = current.next;
        }
        System.out.println("null");
    }

    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode result = new ListNode();
        ListNode current = result;
        int carry = 0;

        while (l1 != null || l2 != null) {
            int x = (l1 != null) ? l1.val : 0;
            int y = (l2 != null) ? l2.val : 0;
            int sum = x + y + carry;
            carry = sum / 10;
            current.next = new ListNode(sum % 10);
            current = current.next;
            if (l1 != null) {
                l1 = l1.next;
            }
            if (l2 != null) {
                l2 = l2.next;
            }
        }
        if (carry > 0) {
            current.next = new ListNode(carry);
        }
        return result.next;
    }
}

283)给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。
示例 1:

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]
示例 2:

输入: nums = [0]
输出: [0]
package leetcode;

public class Solution  {
    public static void main(String[] args) {
        Solution solution = new Solution();

        int[] nums = {0, 1, 0, 3, 12};
        solution.moveZeroes(nums);

        for (int num : nums) {
            System.out.println(num);
        }
    }
        public void moveZeroes ( int[] nums){
            int insertPos = 0;//非0元素插入的位置

            for (int i = 0; i < nums.length; i++) {
                if (nums[i] != 0) {
                    nums[insertPos] = nums[i];
                    insertPos++;
                }
            }
            while (insertPos < nums.length) {
                nums[insertPos] = 0;
                insertPos++;
            }
        }
    }


19)给你一个链表,删除链表的倒数第 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]

package leetcode;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Solution {
    Logger logger = LoggerFactory.getLogger(Logger.class);

    public static void main(String[] args) {
        Solution solution = new Solution();
        ListNode head = new ListNode(1);
        head.next = new ListNode(2);
        head.next.next = new ListNode(3);
        head.next.next.next = new ListNode(4);
        head.next.next.next.next = new ListNode(5);
        int n = 2;
        ListNode result = solution.removeNthFromEnd(head, n);
        ListPrint(result);

    }

    public static void ListPrint(ListNode head) {
        ListNode result = head;
        while (result != null) {
            System.out.print(result.val + ",");
            result = result.next;
        }
        System.out.println("null");
    }

    public ListNode removeNthFromEnd(ListNode head, int n) {

        //列表为空和n不符合则无操作
        if (head == null || n <= 0) {
            return head;
        }

        ListNode dummy = new ListNode(0);//创建一个哑结点
        dummy.next = head;
        //同时定义两个指针
        ListNode first = dummy;
        ListNode second = dummy;


        //遍历链表first,first指针移动了n+1次
        for (int i = 0; i < n + 1; i++) {
            first = first.next;
        }

        //如果first为空,则删除的元素是链表的头部
        if (first == null) {
            head = head.next;
            return head;
        }


        /*如果first不为空,就将first移动至链表结尾,
        同时second同步移动,当first到达结尾时,second则是倒数第n+1位*/
        while (first != null) {
            first = first.next;
            second = second.next;
        }

        //删除倒数第n个节点,将倒数n+1位的next指向倒数n-1位
        second.next = second.next.next;
        return dummy.next;
    }

}



LCR 023. 相交链表

给定两个单链表的头节点 headA 和 headB ,请找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

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

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

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

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '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
输出:Intersected at '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 。

 方法一:

这段代码是用来找到两个链表的交点的。我们可以将这个过程想象成两个人在两条不同的跑道上跑步,他们从各自的起点同时出发,当他们跑到各自跑道的尽头时,会转向对方的跑道继续跑。如果他们在某个点相遇,那么这个点就是两个跑道的交点。

以下是代码的通俗解释:

  1. 我们有两个链表,分别用 headAheadB 表示它们的头节点。

  2. 我们定义两个指针 ab,它们分别指向 headAheadB

  3. 然后我们开始一个循环,这个循环会一直进行,直到 ab 都指向 null,也就是说,直到两个指针都到达了各自链表的末尾。

  4. 在循环中,我们首先检查 ab 是否指向了同一个节点。如果是,那么我们找到了交点,返回这个节点。

  5. 如果 a 指向的节点是 null(也就是说,它已经到达了第一个链表的末尾),我们就让 a 指向 headB,这样它就会开始沿着第二个链表运行。

  6. 同样,如果 b 指向的节点是 null(也就是说,它已经到达了第二个链表的末尾),我们就让 b 指向 headA,这样它就会开始沿着第一个链表运行。

  7. 如果循环结束后都没有找到交点,我们就返回 null

这个方法的关键在于,如果两个链表有交点,那么这两个指针最终一定会相遇在这个交点上。如果没有交点,那么这两个指针最终都会到达 null,循环结束。

这个方法的优点是它只需要一次遍历,不需要预先计算两个链表的长度,也不需要额外的存储空间。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode listA = headA;
        ListNode listB = headB;

        while (listA != null || listB != null) {
            if (listA == listB) {
                return listA;
            }

            listA = (listA == null) ? headB : listA.next;
            listB = (listB == null) ? headA : listB.next;
        }
        return null;
    }
}

 方法二:

将不同起点的两个人移动到相同长度的跑道,再同时起跑,如果相遇说明有相交的跑道

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if (headA == null || headB == null) return null;
        ListNode currentA = headA;
        ListNode currentB = headB;

        int countA = 0;
        int countB = 0;

        while (currentA != null) {
            countA++;
            currentA = currentA.next;
        }
        while (currentB != null) {
            countB++;
            currentB = currentB.next;
        }
        if (countA > countB) {
            for (int i = 0; i < countA - countB; i++) {
                headA = headA.next;
            }
        } else {
            for (int i = 0; i < countB - countA; i++) {
                headB = headB.next;
            }
        }
        while (headA != null || headB != null) {
            if (headA == headB) {
                return headA;
            }
            headA = headA.next;
            headB = headB.next;
        }
        return null;
    }

141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例 1:

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

示例 2:

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

示例 3:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
import java.util.HashSet;
import java.util.Set;

public class Solution {


    public static void main(String[] args) {
        Solution solution = new Solution();

        ListNode listA = new ListNode(3);
        listA.next = new ListNode(2);
        listA.next.next = new ListNode(0);
        listA.next.next.next = new ListNode(-4);
        listA.next.next.next.next = listA.next;

        boolean b = solution.hasCycle(listA);
        System.out.println(b);
    }

    /*输入:head = [3,2,0,-4], pos = 1
    输出:true
    解释:链表中有一个环,其尾部连接到第二个节点。*/
    public boolean hasCycle(ListNode head) {
        Set<ListNode> seem = new HashSet<>();
        ListNode current = head;
        while (current != null) {
            if (!seem.add(current)) {
                return true;
            }
            current = current.next;
        }
        return false;
    }
}

 龟兔赛跑法

public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }

        ListNode fast = head;
        ListNode slow = head;

        while (fast != null){
            fast  = fast.next.next;
            slow = slow.next;
            if (fast == slow){
                return true;
            }
            if (fast == null || fast.next == null) {
                return false;
            }
        }
        return false;
    }

707. 设计链表

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

  • MyLinkedList() 初始化 MyLinkedList 对象。
  • int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
  • void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
  • void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
  • void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
  • void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。

示例:

输入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]

解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2);    // 链表变为 1->2->3
myLinkedList.get(1);              // 返回 2
myLinkedList.deleteAtIndex(1);    // 现在,链表变为 1->3
myLinkedList.get(1);              // 返回 3
public class MyLinkedList {
    int size;
    Node head;
    public MyLinkedList() {
        size = 0;
        head = new Node(0);//设置哑结点
    }
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addAtHead(1);
        myLinkedList.addAtTail(3);
        myLinkedList.addAtIndex(1, 2);
        System.out.println(myLinkedList.get(1));
        myLinkedList.deleteAtIndex(1);
    }

    public int get(int index) {
        if (index < 0 || index >= size) return -1;

        Node pred = head;
        for (int i = 0; i < index + 1; i++) {
            pred = pred.next;
        }
        return pred.val;
    }

    public void addAtHead(int val) {

        addAtIndex(0, val);
    }

    public void addAtTail(int val) {
        addAtIndex(size, val);
    }

    public void addAtIndex(int index, int val) {
        if (index > size) {
            return;
        }
        size++;

        Node pred = head;
        for (int i = 0; i < index; i++) {
            pred = pred.next;

        }
        Node newNode = new Node(val);
        newNode.next = pred.next;
        pred.next = newNode;
    }

    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;
        size--;
        Node pred = head;
        for (int i = 0; i < index; i++) {
            pred = pred.next;
        }
        pred.next = pred.next.next;
    }

    public static class Node {
        int val;
        Node next;

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

138. 随机链表的复制

中等

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

  • val:一个表示 Node.val 的整数。
  • random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为  null 。

你的代码  接受原链表的头节点 head 作为传入参数。

示例 1:

输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

示例 2:

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

示例 3:

输入:head = [[3,null],[3,0],[3,null]]
输出:[[3,null],[3,0],[3,null]]
public Node copyRandomList(Node head) {

        if (head == null) return null;

        //第一次遍历,复制每个节点,按照顺序连接
        Map<Node, Node> map = new HashMap<>();
        Node copy = head;
        while (copy != null) {
            map.put(copy, new Node(copy.val));
            copy = copy.next;
        }

        //第二次遍历,复制next和random指针
        copy = head;//重用copy变量,从链表开头开始
        while (copy != null) {
            Node nodeCopy = map.get(copy);
            nodeCopy.next = copy.next != null ? map.get(copy.next) : null;
            nodeCopy.random = copy.random != null ? map.get(copy.random) : null;
            copy = copy.next;
        }
        return map.get(head);
    }

 方法二:递归法

public Node copyRandomList(Node head) {
        if (head == null || cachedNodes.containsKey(head)) {
            return cachedNodes.get(head);
        }

        //为头结点创建一个复制节点储存在程序中
        Node newNode  = new Node(head.val);
        cachedNodes.put(head,newNode);

        newNode.next = copyRandomList(head.next);
        newNode.random = copyRandomList(head.random);

        //返回复制节点
        return newNode;
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值