力扣链表题目精选(快速理解链表)

力扣链表题目精选

  • LeetCode19.删除链表的倒数第N个结点
  • LeetCode148.排序链表
  • LeetCode160.相交链表
  • LeetCode206.反转链表

链表题–经典解法:1、哑巴结点,也叫虚拟结点 2、栈 3、双指针,比如经典的快慢指针 4、递归

LeetCode19.删除链表的倒数第N个结点

链接地址:LeetCode19.删除链表的倒数第N个结点
力扣链表题目精选(快速理解链表)_第1张图片
想到利用双指针找规律

由于头指针没有前驱节点,使用一个虚拟节点dummy指向头节点,将left初始指向dummy,right初始指向头结点。首先right走n步,使得left与right两个指针相隔n个结点,然后循环两个节点同时走(next),可以发现当right指向null时,left的下一个结点就是题目所要求的

coding如下:

/**
 * 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 removeNthFromEnd(ListNode head, int n) {
        ListNode dummy = new ListNode(0,head);
        ListNode left = dummy;
        ListNode right = head;
        for(int i = 0;i < n;i++){
            right = right.next;
        }
        while(right != null){
            left = left.next;
            right = right.next;
        }
        left.next = left.next.next;
        return dummy.next;
    }
}

思考:使用栈

LeetCode148.排序链表

链接地址:LeetCode148.排序链表

注意:题目要求时间空间复杂度分别为 O(nlogn)和 O(1)

力扣链表题目精选(快速理解链表)_第2张图片
想到时间复杂度的排序算法有快速排序(Quick Sort)、归并排序(Merge Sort)、堆排序(Heap Sort)、希尔排序(Shell Sort)
很明显 这道题要采用分治的思想 需要采用归并排序

对链表自顶向下归并排序的过程如下

1.找到链表的中点,以中点为分界,将链表拆分成两个子链表。寻找链表的中点可以使用快慢指针的做法,快指针每次移动 2 步,慢指针每次移动 1 步,当快指针到达链表末尾时,慢指针指向的链表节点即为链表的中点。

2.对两个子链表分别排序。

3.将两个排序后的子链表合并,得到完整的排序后的链表。可以使用21.合并两个有序链表的做法,将两个有序的子链表进行合并

也可以通过递归实现。递归的终止条件是链表的节点个数小于或等于 1,即当链表为空或者链表只包含 1个节点时,不需要对链表进行拆分和排序。

coding实现如下

class Solution {
    public ListNode sortList(ListNode head) {
        return sortList(head, null);
    }

    public ListNode sortList(ListNode head, ListNode tail) {
        if (head == null) {
            return head;
        }
        if (head.next == tail) {
            head.next = null;
            return head;
        }
        ListNode slow = head, fast = head;
        while (fast != tail) {
            slow = slow.next;
            fast = fast.next;
            if (fast != tail) {
                fast = fast.next;
            }
        }
        ListNode mid = slow;
        ListNode list1 = sortList(head, mid);
        ListNode list2 = sortList(mid, tail);
        ListNode sorted = merge(list1, list2);
        return sorted;
    }
	//合并链表
    public ListNode merge(ListNode head1, ListNode head2) {
        ListNode dummyHead = new ListNode(0);
        ListNode temp = dummyHead, temp1 = head1, temp2 = head2;
        while (temp1 != null && temp2 != null) {
            if (temp1.val <= temp2.val) {
                temp.next = temp1;
                temp1 = temp1.next;
            } else {
                temp.next = temp2;
                temp2 = temp2.next;
            }
            temp = temp.next;
        }
        if (temp1 != null) {
            temp.next = temp1;
        } else if (temp2 != null) {
            temp.next = temp2;
        }
        return dummyHead.next;
    }
}

LeetCode160.相交链表

链接地址:LeetCode160.相交链表
力扣链表题目精选(快速理解链表)_第3张图片

如果相交,链表A: a+c, 链表B : b+c. a+c+b+c = b+c+a+c 。则会在公共处c起点相遇。若不相交,a +b = b+a 。因此相遇处是NULL

即数学原理:当两个链表相交时,两个指针分别遍历完自己的链表再遍历对方的链表,这样使得它们遍历的总长度相等

coding如下:

//设A的长度为a+c,B的长度为b+c;其中c为A、B的公共部分;
// 拼接AB、BA:A+B=a+c+b+c B+A=b+c+a+c;由于a+c+b=b+c+a,因此二者必定在c的起始点处相遇
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    if (headA == null || headB == null) return null;
    ListNode pA = headA, pB = headB;
    while (pA != pB) {
        pA = pA == null ? headB : pA.next;
        pB = pB == null ? headA : pB.next;
    }
    return pA;
}

LeetCode206.反转链表

链接地址:LeetCode206.反转链表

力扣链表题目精选(快速理解链表)_第4张图片
方法一:循环遍历(官方题解):

在遍历链表时,将当前节点的 next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用

/**
 * 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 pre = null;
        ListNode cur = head;
        while(cur != null){
        	//保存当前节点的后续节点
            ListNode next = cur.next;
            //将pre赋值给当前节点的下一个节点
            cur.next = pre;
            //更新两个节点
            pre = cur;
            cur = next;
        }
        return pre;
    }
}

方法二:递归

主要是node.next.next=node的使用 即当前节点的下一个节点的下一个节点指向自己 即可实现当前节点的反转

上官方图解释
力扣链表题目精选(快速理解链表)_第5张图片

coding实现如下:

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;
    }
}

你可能感兴趣的:(leetcode,链表,算法,java)