Top101【01-09】

157 阅读3分钟

BM1 反转链表

题目

image.png

思路

  1. 借助栈
  2. 头插法
  3. 递归法

代码实现

public ListNode ReverseList1 (ListNode head) {
    // write code here
    Stack<ListNode> stack = new Stack<>();
    while (head != null) {
        stack.push(head);
        head = head.next;
    }

    ListNode newHead = new ListNode(-1);
    ListNode cur = newHead;
    while (!stack.isEmpty()) {
        cur.next = stack.pop();
        cur = cur.next;
    }

    cur.next = null;
    return newHead;
}

public ListNode ReverseList2 (ListNode head) {
    if(head == null || head.next == null) {
        return head;
    }

    ListNode cur = head;
    ListNode pre = null;
    while (cur != null) {
        ListNode curNext = cur.next;
        cur.next = pre;
        pre = cur;
        cur = curNext;
    }

    return pre;
}

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

BM2 链表内指定区间反转

题目

image.png

思路

基于上题的基础之上

需要虚拟头结点,来确定返回的新的头结点【因为可能从头开始逆序,头结点就会发生改变】,具体思路:记录反转开始的前一个节点,然后反转指定区间的节点,最后改变节点的指向【记录节点的next,逆序链表的头结点的next】

代码实现

public ListNode reverseBetween (ListNode head, int m, int n) {
    ListNode dummyNode = new ListNode(-1);
    dummyNode.next = head;

    ListNode front = dummyNode;
    int count = n - m + 1;
    // 找到要反转节点的前一个节点
    while (m - 1 > 0) {
        front = front.next;
        m--;
    }

    ListNode cur = front.next;
    ListNode pre = null;
    while (count > 0) {
        ListNode curNext = cur.next;
        cur.next = pre;
        pre = cur;
        cur = curNext;
        count--;
    }

    front.next.next = cur;
    front.next = pre;

    return dummyNode.next;
}

BM3 链表中的节点每k个一组翻转

题目

image.png

思路

递归的方式来进行反转,每趟递归反转一个k长度的链表,每趟递归返回该趟逆序链表的头结点,然后 每趟递归中逆序后,head 就表示该组链表中的尾巴节点,这样就可以通过 head.next = 下一趟递归返回的头结点来进行链表的连接了

代码实现

public ListNode reverseKGroup (ListNode head, int k) {
    // write code here
    ListNode tail = head;
    for (int i = 0; i < k; i++) {
        if(tail == null) {
            return head;
        }
        tail = tail.next;
    }

    // 反转 [head,tail)
    ListNode cur = head;
    ListNode pre = null;
    while (cur != tail) {
        ListNode curNext = cur.next;
        cur.next = pre;
        pre = cur;
        cur = curNext;
    }
    head.next = reverseKGroup(tail,k);
    return pre;
}

BM4 合并两个排序的链表

题目

image.png

思路

  1. 遍历合并
  2. 递归【每一趟递归合并一个数】

代码实现

public ListNode Merge (ListNode pHead1, ListNode pHead2) {
    if (pHead1 == null) {
        return pHead2;
    }

    if (pHead2 == null) {
        return pHead1;
    }

    ListNode head = null;
    if (pHead1.val > pHead2.val) {
        head = pHead2;
        pHead2 = pHead2.next;
    } else {
        head = pHead1;
        pHead1 = pHead1.next;
    }

    head.next = Merge(pHead1,pHead2);

    return head;
}

public ListNode Merge1 (ListNode pHead1, ListNode pHead2) {
    // write code here
    if (pHead1 == null) {
        return pHead2;
    }

    if (pHead2 == null) {
        return pHead1;
    }

    ListNode head = null;
    ListNode tail = null;
    if (pHead1.val > pHead2.val) {
        head = tail = pHead2;
        pHead2 = pHead2.next;
    } else {
        head = tail = pHead1;
        pHead1 = pHead1.next;
    }

    while (pHead1 != null && pHead2 != null) {
        if (pHead1.val > pHead2.val) {
            tail.next = pHead2;
            pHead2 = pHead2.next;
        } else {
            tail.next = pHead1;
            pHead1 = pHead1.next;
        }
        tail = tail.next;
    }

    tail.next = pHead1 == null ? pHead2 : pHead1;

    return head;
}

BM5 合并k个已排序的链表

题目

image.png

思路

多路归并:使用优先级队列来存储每个链表的头结点,然后每次都弹出栈顶元素【弹出后入栈当前元素.next - 进行判空】,最后优先级队列为空即排完

代码实现

public ListNode mergeKLists1 (ArrayList<ListNode> lists) {
    // write code here
    PriorityQueue<ListNode> queue = new PriorityQueue<>(new Comparator<ListNode>() {
        @Override
        public int compare(ListNode o1, ListNode o2) {
            return o1.val - o2.val;
        }
    });
    // 将头结点都放入queue中
    for(ListNode node : lists) {
        if(node != null) {
            queue.offer(node);
        }
    }

    // 开始多路归并
    ListNode head = null;
    ListNode tail = null;
    while (!queue.isEmpty()) {
        ListNode node = queue.poll();
        if(head == null) {
            head = tail = node;
        } else {
            tail.next = node;
            tail = tail.next;
        }

        if(node.next != null) {
            node = node.next;
            queue.offer(node);
        }
    }

    if(tail != null) {
        tail.next = null;
    }

    return head;
}

BM6 判断链表中是否有环

题目

image.png

思路

快慢指针,同时出发,快的走两步,慢的走一步,如果快的为空则表示没有环,如果两个遇到了就表示有环 a 快的只能走两步:走两步的话相当于每次追1步,如果有环肯定会追到的;但是如果走三步的话,每次追两步可能会错过

代码实现

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

    ListNode slow = head;
    ListNode fast = head;
    while (fast.next != null && fast.next.next != null) {
        slow = slow.next;
        fast = fast.next.next;

        if(slow == fast) {
            return true;
        }
    }
    return false;
}

BM7 链表中环的入口结点

题目

image.png

思路

快慢指针 + 找规律

image.png

代码实现

public ListNode EntryNodeOfLoop(ListNode pHead) {
    if (pHead == null) {
        return null;
    }

    ListNode slow = pHead;
    ListNode fast = pHead;
    while (fast.next != null && fast.next.next != null) {
        slow = slow.next;
        fast = fast.next.next;

        if (slow == fast) {
            ListNode meet = slow;
            while (meet != pHead) {
                meet = meet.next;
                pHead = pHead.next;
            }
            return meet;
        }
    }
    return null;
}

BM8 链表中倒数最后k个结点

题目

image.png

思路

快指针先走k步,然后快慢指针一起走,快指针到结尾,慢指针就是结果了 防止 k > 链表长度

代码实现

public ListNode FindKthToTail (ListNode pHead, int k) {
    // fast先走k步
    ListNode slow = pHead;
    ListNode fast = pHead;
    while (k > 0) {
        if(fast == null) {
            return null;
        }

        fast = fast.next;
        k--;
    }

    while (fast != null) {
        slow = slow.next;
        fast = fast.next;
    }

    return slow;
}

BM9 删除链表的倒数第n个节点

题目

image.png

思路

搞一个虚拟节点,然后像上题一样找倒数第k个节点,但是多一个pre节点,记录着慢指针的前一个节点,最后删除

代码实现

public ListNode removeNthFromEnd (ListNode pHead, int k) {
    // write code here
    if (pHead == null) {
        return pHead;
    }

    if (k == 0) {
        return pHead;
    }
    ListNode dummyNode = new ListNode(-1);
    dummyNode.next = pHead;

    ListNode fast = pHead;
    ListNode slow = pHead;
    while (k > 0) {
        if (fast == null) {
            return null;
        }
        fast = fast.next;
        k--;
    }

    ListNode pre = dummyNode;
    while (fast != null) {
        pre = slow;
        slow = slow.next;
        fast = fast.next;
    }

    // 删除slow
    pre.next = slow.next;

    return dummyNode.next;
}