LeetCode - 按标签分类刷题(链表题解)

19.删除链表的倒数第N个节点

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

示例:

给定一个链表: 1->2->3->4->5, 和 n = 2.

当删除了倒数第二个节点后,链表变为 1->2->3->5.

双指针:

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if(head == null){
            return null;
        }
        ListNode pre = new ListNode(0);
        pre.next = head;
        ListNode start = pre,end = pre;
        while(n != 0){
            start = start.next;
            n--;
        }
        while(start.next != null){
            start = start.next;
            end = end.next;
        }
        end.next = end.next.next;
        return pre.next;
    }
}

# 203. 移除链表元素
删除链表中等于给定值 val 的所有节点。

示例:

输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head == null){
            return null;
        }
        ListNode pre = new ListNode(0);
        pre.next = head;
        
        while(pre.next != null){
            if(pre.next.val == val){
                pre.next = pre.next.next;
            }else{
                pre = pre.next;
            }

        }
        return pre;
    }
}

82. 删除排序链表中的重复元素 II

  1. 删除排序链表中的重复元素 II

给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

示例 1:

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

示例 2:

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

这里我们使用双指针的方式,定义a,b两个指针。
考虑到一些边界条件,比如1->1->1->2这种情况,需要把开头的几个1给去掉,我们增加一个哑结点,方便边界处理。

初始的两个指针如下:

将a指针指向哑结点
将b指针指向head(哑结点的下一个节点)
如果a指向的值不等于b指向的值,则两个指针都前进一位
否则,就单独移动b,b不断往前走,直到a指向的值不等于b指向的值。

注意,这里不是直接比较a.val==b.val,这么比较不对,因为初始的时候,a指向的是哑结点,所以比较逻辑应该是这样:

a.next.val == b.next.val

当两个指针指向的值相等时,b不断往前移动,这里是通过一个while循环判断的,因为要过滤掉1->2->2->2->3重复的2。
那么整个逻辑就是两个while,但时间复杂度不是O(N^2),而是O(N),空间上也只是常数级别。

class Solution_82 {
    //递归版本,删除链表的节点
    /*
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null ||head.next == null)
            return head;

        if (head.val == head.next.val){
             while(head.next != null && head.val == head.next.val){
                 head = head.next;
             }
             return deleteDuplicates(head.next);
        }else {
            head.next = deleteDuplicates(head.next);
            return head;
        }
    }


     */


    public ListNode deleteDuplicates(ListNode head) {
        if(head==null || head.next==null) {
            return head;
        }
        ListNode dummy = new ListNode(-1);
        dummy.next = head;
        ListNode a = dummy;
        ListNode b = head;
        while(b!=null && b.next!=null) {
            //初始化的时a指向的是哑结点,所以比较逻辑应该是a的下一个节点和b的下一个节点
            if(a.next.val!=b.next.val) {
                a = a.next;
                b = b.next;
            }else {
                //如果a、b指向的节点值相等,就不断移动b,直到a、b指向的值不相等
                while(b!=null && b.next!=null && a.next.val==b.next.val) {
                    b = b.next;
                }
                a.next = b.next;
                b = b.next;
            }
        }
        return dummy.next;

    }
}

83. 删除排序链表中的重复元素

  1. 删除排序链表中的重复元素

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

示例 1:

输入: 1->1->2
输出: 1->2
示例 2:

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

public ListNode deleteDuplicates(ListNode head) {
    ListNode current = head;
    while (current != null && current.next != null) {
        if (current.next.val == current.val) {
            current.next = current.next.next;
        } else {
            current = current.next;
        }
    }
    return head;
}

138 .复制带随机指针的链表

138 .复制带随机指针的链表

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

示例 1:
在这里插入图片描述

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

方法一:哈希表

利用哈希表的查询特点,考虑构建 原链表节点 和 新链表对应节点 的键值对映射关系,再遍历构建新链表各节点的 next 和 random 引用指向即可。

算法流程:

  1. 若头节点 head 为空节点,直接返回 null ;
  2. 初始化: 哈希表 dic , 节点 cur 指向头节点;
  3. 复制链表:
    1. 建立新节点,并向 dic 添加键值对 (原 cur 节点, 新 cur 节点) ;
    2. cur 遍历至原链表下一节点;
  4. 构建新链表的引用指向:
    1. 构建新节点的 next 和 random 引用指向;
    2. cur 遍历至原链表下一节点;
  5. 返回值: 新链表的头节点 dic[cur] ;
public Node copyRandomList(Node head) {
        if (head == null) return null;
        Node cur = head;
        Map<Node,Node> map = new HashMap<Node,Node>();
        //3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射
        while(cur != null){
            //存储put:<key,value1>
            map.put(cur,new Node(cur.val)); //顺序遍历,存储老结点和新结点(先存储新创建的结点值)
            cur = cur.next;
        }
        cur = head;
        //4. 构建新链表的next和random指向
        while (cur != null){
            //得到get:<key>.value2,3
            map.get(cur).next = map.get(cur.next);//新结点next指向同旧结点的next指向
            map.get(cur).random = map.get(cur.random);//新结点random指向同旧结点的random指向
            cur = cur.next;
        }
        //5.返回新链表的头节点
        return map.get(head);
    }

206. 反转链表

  1. 反转链表
    反转一个单链表。

示例:

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

public class 翻转链表 {
    public ListNode ReverseList(ListNode head){
        if (head ==null || head.next==null)
            return head;
        //初始化pre指针,用于记录当前结点的前一个结点地址
        ListNode pre = null;
        //初始化p指针,用于记录当前结点的下一个结点地址
        ListNode p = null;
        //head指向null时,循环终止。
        while(head != null){
            //先用p指针记录当前结点的下一个结点地址。
            p = head.next;
            //让被当前结点与链表断开并指向前一个结点pre。
            head.next = pre;
            //pre指针指向当前结点
            pre = head;
            //head指向p(保存着原链表中head的下一个结点地址)
            head = p;
        }
        return pre;//当循环结束时,pre所指的就是反转链表的头结点
    }

}

237. 删除链表中的节点

  1. 删除链表中的节点

请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为 要被删除的节点 。

现有一个链表 – head = [4,5,1,9],它可以表示为:
在这里插入图片描述

示例 1:

输入:head = [4,5,1,9], node = 5
输出:[4,1,9]
解释:给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:

输入:head = [4,5,1,9], node = 1
输出:[4,5,9]
解释:给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

剑指 Offer 06. 从尾到头打印链表(数组的形式打印)

剑指 Offer 06. 从尾到头打印链表(数组的形式打印)
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:

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

方法一:栈

栈的特点是后进先出,即最后压入栈的元素最先弹出。考虑到栈的这一特点,使用栈将链表元素顺序倒置。从链表的头节点开始,依次将每个节点压入栈内,然后依次弹出栈内的元素并存储到数组中。

  • 创建一个栈,用于存储链表的节点

  • 创建一个指针,初始时指向链表的头节点

  • 当指针指向的元素非空时,重复下列操作:

    • 将指针指向的节点压入栈内

    • 将指针移到当前节点的下一个节点

  • 获得栈的大小 size,创建一个数组 print,其大小为 size

  • 创建下标并初始化 index = 0

  • 重复 size 次下列操作:

    • 从栈内弹出一个节点,将该节点的值存到 print[index]
    • 将 index 的值加 1
  • 返回 print

class Solution {
    public int[] reversePrint(ListNode head) {
        //使用栈,栈是先进后出,正好实现从尾到头打印
        Stack<ListNode> stack = new Stack<ListNode>();
        ListNode temp = head;
        while (temp != null){
            stack.push(temp);
            temp = temp.next;
        }
        int size = stack.size();
        int[] print = new int[size];
        for (int i = 0; i < size; i++) {
            print[i] = stack.pop().val;
        }
        return print;
    }
}

剑指 Offer 22. 链表中倒数第k个节点

剑指 Offer 22. 链表中倒数第k个节点

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        if(head == null){
            return null;
        }
        ListNode pre = new ListNode(0);
        pre.next = head;
        ListNode start = pre,end = pre;
        while(k != 0){
            start = start.next;
            k--;
        }
        while(start.next != null){
            start = start.next;
            end = end.next;
        }
        return end.next;
    }
}
posted @ 2020-12-29 22:12  your_棒棒糖  阅读(48)  评论(0)    收藏  举报