当前位置:   article > 正文

【LeetCode】6 链表重排序

链表重排序

码上生花,ECharts 作品展示赛正式启动!>>> hot3.png

题目

将给定的单链表L: L 0→L 1→…→L n-1→L n,

重新排序为: L 0→L n →L 1→L n-1→L 2→L n-2→…

要求使用原地算法,并且不改变节点的值

例如:

对于给定的单链表{1,2,3,4},将其重新排序为{1,4,2,3}.

以下来自优质解答

解法一 存储

链表的缺点就是不能随机存储,当我们想取末尾元素的时候,只能从头遍历一遍,很耗费时间。第二次取末尾元素的时候,又得遍历一遍。

所以先来个简单粗暴的想法,把链表存储到线性表中,然后用双指针依次从头尾取元素即可。

  1. public void reorderList(ListNode head) {
  2. if (head == null) {
  3. return;
  4. }
  5. //存到 list 中去
  6. List<ListNode> list = new ArrayList<>();
  7. while (head != null) {
  8. list.add(head);
  9. head = head.next;
  10. }
  11. //头尾指针依次取元素
  12. int i = 0, j = list.size() - 1;
  13. while (i < j) {
  14. list.get(i).next = list.get(j);
  15. i++;
  16. //偶数个节点的情况,会提前相遇
  17. if (i == j) {
  18. break;
  19. }
  20. list.get(j).next = list.get(i);
  21. j--;
  22. }
  23. list.get(i).next = null;
  24. }

解法二 递归

解法一中也说到了,我们的问题就是取尾元素的时候,需要遍历一遍链表。

如果我们的递归函数能够返回当前头元素对应的尾元素,并且将头元素和尾元素之间的链表按要求完成,那就变得简单了。

img

如上图,我们只需要将 head 指向 tail,tail 指向处理完的链表头即可。

img

img

然后我们把之前的 tail.next返回就是外层 head对应的 tail 了。

递归出口的话,如果只有一个节点,那么我们只需要将head.next 返回。

  1. if (len == 1) {
  2. ListNode outTail = head.next;
  3. head.next = null;
  4. return outTail;
  5. }

如果是两个节点,我们需要将 head.next.next 返回。

  1. if (len == 2) {
  2. ListNode outTail = head.next.next;
  3. head.next.next = null;
  4. return outTail;
  5. }

然后总体的代码就是下边的样子

  1. public void reorderList(ListNode head) {
  2. if (head == null || head.next == null || head.next.next == null) {
  3. return;
  4. }
  5. int len = 0;
  6. ListNode h = head;
  7. //求出节点数
  8. while (h != null) {
  9. len++;
  10. h = h.next;
  11. }
  12. reorderListHelper(head, len);
  13. }
  14. private ListNode reorderListHelper(ListNode head, int len) {
  15. if (len == 1) {
  16. ListNode outTail = head.next;
  17. head.next = null;
  18. return outTail;
  19. }
  20. if (len == 2) {
  21. ListNode outTail = head.next.next;
  22. head.next.next = null;
  23. return outTail;
  24. }
  25. //得到对应的尾节点,并且将头结点和尾节点之间的链表通过递归处理
  26. ListNode tail = reorderListHelper(head.next, len - 2);
  27. ListNode subHead = head.next;//中间链表的头结点
  28. head.next = tail;
  29. ListNode outTail = tail.next; //上一层 head 对应的 tail
  30. tail.next = subHead;
  31. return outTail;
  32. }

解法三

主要是利用到一头一尾取元素的特性。

主要是三步,举个例子。

  1. 1 -> 2 -> 3 -> 4 -> 5 -> 6
  2. 第一步,将链表平均分成两半
  3. 1 -> 2 -> 3
  4. 4 -> 5 -> 6
  5. 第二步,将第二个链表逆序
  6. 1 -> 2 -> 3
  7. 6 -> 5 -> 4
  8. 第三步,依次连接两个链表
  9. 1 -> 6 -> 2 -> 5 -> 3 -> 4

第一步找中点的话,可以应用快慢指针。快指针一次走两步,慢指针一次走一步,当快指针走到终点的话,慢指针会刚好到中点。如果节点个数是偶数的话,slow 走到的是左端点,利用这一点,我们可以把奇数和偶数的情况合并,不需要分开考虑。

第二步链表逆序的话,有迭代和递归的两种方式,迭代的话主要利用两个指针,依次逆转。

第三步的话就很简单了,两个指针分别向后移动就可以。

  1. public void reorderList(ListNode head) {
  2. if (head == null || head.next == null || head.next.next == null) {
  3. return;
  4. }
  5. //找中点,链表分成两个
  6. ListNode slow = head;
  7. ListNode fast = head;
  8. while (fast.next != null && fast.next.next != null) {
  9. slow = slow.next;
  10. fast = fast.next.next;
  11. }
  12. ListNode newHead = slow.next;
  13. slow.next = null;
  14. //第二个链表倒置
  15. newHead = reverseList(newHead);
  16. //链表节点依次连接
  17. while (newHead != null) {
  18. ListNode temp = newHead.next;
  19. newHead.next = head.next;
  20. head.next = newHead;
  21. head = newHead.next;
  22. newHead = temp;
  23. }
  24. }
  25. private ListNode reverseList(ListNode head) {
  26. if (head == null) {
  27. return null;
  28. }
  29. ListNode tail = head;
  30. head = head.next;
  31. tail.next = null;
  32. while (head != null) {
  33. ListNode temp = head.next;
  34. head.next = tail;
  35. tail = head;
  36. head = temp;
  37. }
  38. return tail;
  39. }

总结

解法一利用空间去存储就很简单了,解法二递归的思想也很经典,判断当前 length 定义递归出口很巧妙。解法三主要就是对题目的理解,关键就是利用一头一尾取元素的特性。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/226630
推荐阅读
相关标签
  

闽ICP备14008679号