力扣刷題記錄(一):鏈表

雙指針問題

兩個鏈條求交點,每一條鏈條到達尾端的時候,執行一次鏈條的切換,可以最終使兩個鏈條達到同步運行的效果。這裏注意到達尾部的條件是爲null,而不是下一個結點爲null

public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
    ListNode pointA = headA, pointB = headB;
    while (pointA != pointB) {
        // 到達尾部的時候才需要進行切換鏈條
        pointA = (pointA == null) ? headB : pointA.next;
        pointB = (pointB == null) ? headA : pointB.next;
    }
    return pointA;
}

反轉鏈表

總結:需要替換什麼的時候,一定要給這個結點做好備份

public ListNode reverseList(ListNode head) {
	ListNode cur = head, prev = null;
    while(cur != null){
        // 將下一個結點做一個備份
        ListNode temp = cur.next;
        cur.next = prev;
        prev = cur;
        // 切換到下一個結點
        cur = temp;
    }
    return prev;
}

有序鏈表的歸併

採用一個優先隊列進行存儲;注意不能直接使用優先隊列的toArray方法,而需要依次執行出入隊來獲取元素。

同時哦注意對於輸入鏈表本身的判空。還需要注意對最後一個鏈表的尾部置位null

public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
    if(l1 == null && l2 == null) return null;

    PriorityQueue<ListNode> queue = new PriorityQueue<>((o1, o2) -> o1.val - o2.val);

    while (l1 != null) {
        queue.offer(l1);
        l1 = l1.next;
    }

    while (l2 != null) {
        queue.offer(l2);
        l2 = l2.next;
    }

    ListNode[] nodes = new ListNode[queue.size()];

    int i = 0;
    while (!queue.isEmpty()) {
        nodes[i++] = queue.poll();
    }

    for (int j = 0; j < nodes.length - 1; j++) {
        nodes[j].next = nodes[j + 1];
    }

    nodes[nodes.length - 1].next = null;

    return nodes[0];
}

更優解:藉助了本身有序的特性,以及遞歸的思想:

public ListNode mergeTwoListsTraverse(ListNode l1, ListNode l2) {
    	// 遞歸邊界
        if (l1 == null) return l2;
        if (l2 == null) return l1;
        if (l1.val < l2.val) {
            // 如果l1的值更小,滿足升序,這時只需要將l1的下個結點與l2進行比較即可
            // l1的下一個鏈表鏈條即是兩個鏈表結合過後的鏈條
            l1.next = mergeTwoListsTraverse(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoListsTraverse(l1, l2.next);
            return l2;
        }
    }

刪除鏈表的重複結點

重點: 遞歸條件來源 – 當遍歷到達尾部的時候停止

中間操作: 刪除下一個結點的重複結點 — 這個是從尾部到頭部

返回的確認:當有相等的值,則將head.next壓棧,進行刪除操作,直到最後出棧頭部結點

遞歸是一個入棧的操作

public ListNode deleteDuplicates(ListNode head) {
    if(head == null || head.next == null) return head;
    head.next = deleteDuplicates(head.next);
    return head.val == head.next.val ? head.next : head;
}

刪除倒數第N個結點

注意:

  • 啞結點的作用 – 避免列表只有一個結點的情況出現。用於存儲一個頭結點
  • 第二次遍歷的時候,從啞結點開始遍歷
public ListNode removeNthFromEnd2(ListNode head, int n) {
    // 啞結點--避免出現列表只有一個結點的情況
    ListNode dummy = new ListNode(0);
    dummy.next = head;
    int length = 0;
    ListNode first = head;
    while (first != null) {
        length++;
        first = first.next;
    }
    first = dummy;
    length -= n;
    while (length > 0) {
        length--;
        first = first.next;
    }
    first.next = first.next.next;
    // 頭結點
    return dummy.next;
}

還有一次遍歷法 – 方法類似雙指針

交換鏈表中的相鄰結點

重點:

  • 啞結點的使用,用在頭部,使頭部結點可以更換
  • 中間結點儲存結果
  • 如果鏈接一個頭部變換過的節點
public ListNode swapPairs(ListNode head) {
    // 啞結點代替頭部,可以更換頭部結點
    ListNode dummy = new ListNode(-1);
    dummy.next = head;

    ListNode pre = dummy;
    while (pre.next != null && pre.next.next != null) {
        ListNode l1 = pre.next, l2 = pre.next.next;

        // 中間結點, 用於存儲節點的轉移位置
        ListNode next = l2.next;
		// 執行結點翻轉操作
        l1.next = next;
        l2.next = l1;

        // 指針重接
        pre.next = l2;
        pre = l1;
    }
    return dummy.next;
}

鏈表求和

tips:一般需要逆序求解的時候都需要用到來進行輔助。

  • 中間鏈表插入結點,需要熟記,凡是最後需要返回頭結點的,都需要有一個dummy結點作爲固定指針位
  • 鏈表的逆序求解,需要藉助棧進行
public ListNode addTwoNumbers2(ListNode l1, ListNode l2) {
    Stack<Integer> stack1 = buildStack(l1);
    Stack<Integer> stack2 = buildStack(l2);
    // 頭部固定指針位
    ListNode head = new ListNode(-1);
    // 計算進位
    int carry = 0;
    while (!stack1.isEmpty() || !stack2.isEmpty() || carry != 0) {
        // 通過三元表達式判定,當棧爲空時,返回0,否則棧頂出棧
        int num1 = stack1.isEmpty() ? 0 : stack1.pop();
        int num2 = stack2.isEmpty() ? 0 : stack2.pop();
        int sum = num1 + num2 + carry;
        ListNode node = new ListNode(sum % 10);
        // 插入鏈表結點
        node.next = head.next;
        head.next = node;
        // 進位計算
        carry = sum / 10;
    }
    return head.next;
}

// 鏈表只能從左至右添加,但是需要從右至左遍歷的時候,需要藉助棧
private Stack<Integer> buildStack(ListNode node) {
    Stack<Integer> stack = new Stack<>();
    while (node != null) {
        stack.push(node.val);
        node = node.next;
    }
    return stack;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章