NC2:重排链表

重排链表

http://www.nowcoder.com/questionTerminal/3d281dc0b3704347846a110bf561ef6b

- 解法1:划分+逆序+拼接;
- 解法2:线性表;

解法1:划分+逆序+拼接

图片说明

eg: 1->2->3->4->5->6
第一步:将链表分为两个链表
​ 1->2->3 4->5->6
第二步:将第二个链表逆序
​ 1->2->3 6->5->4
第三步:依次连接两个链表 连接形式如下
​ 1->6->2->5->3->4

时间复杂度:O(N),其中 N 是链表中的节点数
空间复杂度:O(1)

第一步 找链表的中间节点
思路:可以利用 快慢指针,快指针一次走两步,慢指针依次走一步,当快指针走到终点的时候,此时如果链表的长度为偶数时,此时中间节点有两个,慢指针则走到了左端点。反之,慢指针则走到中间节点。(这两种情况在我们这道题目的总代吗,我们可以直接合并为一种,则第二个链表的长度比第一个链表的长度小1)

public ListNode middleNode(ListNode head) {
        if(head == null)
            return head;
        ListNode slow = head;
        ListNode fast = head;
        while(fast.next != null && fast.next.next != null){
            slow = slow.next;
            fast = fast.next.next;
        }
        if(fast.next == null)  // 长度为奇数
            return slow;
        else
            return slow.next;  // 长度为偶数
    }

第二步 反转链表
初始化:3个指针
1)pre指针指向已经反转好的链表的最后一个节点,最开始没有反转,所以指向nullptr
2)cur指针指向待反转链表的第一个节点,最开始第一个节点待反转,所以指向head
3)nex指针指向待反转链表的第二个节点,目的是保存链表,因为cur改变指向后,后面的链表则失效了,所以需要保存
接下来,循环执行以下三个操作
1)nex = cur->next, 保存作用
2)cur->next = pre 未反转链表的第一个节点的下个指针指向已反转链表的最后一个节点
3)pre = cur, cur = nex; 指针后移,操作下一个未反转链表的第一个节点
循环条件,当然是cur != nullptr
循环结束后,cur当然为nullptr,所以返回pre,即为反转后的头结点

    public ListNode ReverseList(ListNode head) {
        ListNode pre=null;
        ListNode cur=head;
        while(cur!=null){
            ListNode tmp=cur.next;
            cur.next=pre;
            pre=cur;
            cur=tmp;
        }
        return pre;
    }

第三步 合并两个链表
思路:直接两个指针往后遍历即可

        newHead = reverseList(newHead);
        while(newHead != null){
            ListNode temp = newHead.next;
            newHead.next = head.next;
            head.next = newHead;
            head = newHead.next;
            newHead = temp;
        }

汇总代码1:

public void reorderList(ListNode head) {
        if (head == null) {
            return;
        }
        ListNode mid = middleNode(head);
        ListNode l1 = head;
        ListNode l2 = mid.next;
        mid.next = null;
        l2 = reverseList(l2);
        mergeList(l1, l2);
    }

    public ListNode middleNode(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        while (fast.next != null && fast.next.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

    public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode curr = head;
        while (curr != null) {
            ListNode nextTemp = curr.next;
            curr.next = prev;
            prev = curr;
            curr = nextTemp;
        }
        return prev;
    }

    public void mergeList(ListNode l1, ListNode l2) {
        ListNode l1_tmp;
        ListNode l2_tmp;
        while (l1 != null && l2 != null) {
            l1_tmp = l1.next;
            l2_tmp = l2.next;

            l1.next = l2;
            l1 = l1_tmp;

            l2.next = l1;
            l2 = l2_tmp;
        }
    }

代码2:

解法2:线性表
因为链表不支持下标访问,所以我们无法随机访问链表中任意位置的元素。
因此比较容易想到的一个方法是,我们利用线性表存储该链表,然后利用线性表可以下标访问的特点,直接按顺序访问指定元素,重建该链表即可

public void reorderList(ListNode head) {
        if (head == null) {
            return;
        }
        List<ListNode> list = new ArrayList<ListNode>();
        ListNode node = head;
        while (node != null) {
            list.add(node);
            node = node.next;
        }
        int i = 0, j = list.size() - 1;
        while (i < j) {
            list.get(i).next = list.get(j);
            i++;
            if (i == j) {
                break;
            }
            list.get(j).next = list.get(i);
            j--;
        }
        list.get(i).next = null;
    }
名企高频面试算法题解 文章被收录于专栏

牛客题霸 - 程序员面试高频题 - 题解

全部评论

相关推荐

不愿透露姓名的神秘牛友
11-21 19:05
面试官_我太想进步了:混学生会的,难怪简历这么水
点赞 评论 收藏
分享
10-09 09:39
门头沟学院 C++
HHHHaos:这也太虚了,工资就一半是真的
点赞 评论 收藏
分享
评论
4
3
分享
牛客网
牛客企业服务