python经典面试算法题1.4:如何对链表进行重新排序

本题目摘自《Python程序员面试算法宝典》,我会每天做一道这本书上的题目,并分享出来,统一放在我博客内,收集在一个分类中。

1.4 对链表按照如下要求重新排序
【微软笔试题】

难度系数:⭐⭐⭐
考察频率:⭐⭐⭐⭐

题目描述:

给定链表L0 -> L1 -> L2 -> … -> Ln-1 -> Ln ,把链表重新排序为 L0 -> Ln -> L1 -> Ln-1 -> L2 … 。要求:(1)在原来链表的基础上进行排序,即不能申请新的结点;(2)只能修改结点的next域,不能修改数据域。

分析解答:
当我们在笔试过程中遇到这种问题,一下子是没有什么思路的,我们要做的方法是把复杂问题拆解为简单问题。
就这一题而言我们可以把它分为三个步骤,(1)首先找到链表的中间结点;(2)对链表的后半部分子链表进行逆序;(3)把链表的前半部分子链表与逆序后的后半部分子链表进行合并,这里合并的思路是分别从两个链表各取一个结点进行合并。

步骤一
我们使用两个指针从链表的第一个结点开始同时遍历,快指针每次走两步,慢指针每次走一步,当快指针到达链表尾部时,慢指针恰好到达链表中部。

def find_mid_node(head):
    if head is None or head.next is None:
        return head
    fast = head
    slow = head
    prev = slow
    while fast is not None and fast.next is not None:
        fast = fast.next.next
        prev = slow
        slow = slow.next
    prev.next = None
    return slow  # 下半段的head

步骤二
这一步在我前面的博客文章中有写,可以点击此处跳转

def reverse(head):  #
    p = head
    if p is None or p.next is None:
        return p
    q = p
    p = p.next
    while p is not None:
        tmp = p.next
        p.next = q
        if q == head:
            q.next = None
        q = p
        p = tmp
    return q

步骤三
这个函数传入前半部分的第一个结点和后半部分的第一个结点,cur1指向前半部分的第一个结点,cur2指向后半部分的第一个结点,tmp指向cur1后面的结点,然后令cur1的next指向cur2,接着cur1后移指向此时的tmp,然后让tmp再指向记住cur2的后面一个结点,令cur2的next指向此时的cur1,这样我们就完成了后半部分的第一个结点插入到前半部分,就这样一直往后,直到cur1到达最后一个结点,全部结点插入完成。

# 插入
def reorder(head1, head2):
    cur1 = head1
    cur2 = head2
    tmp = None
    while cur1.next is not None:
        tmp = cur1.next
        cur1.next = cur2
        cur1 = tmp

        tmp = cur2.next
        cur2.next = cur1
        cur2 = tmp

    cur1.next = cur2  # 无论cur2是不是None,cur的next都应该指向cur2

把三个函数封装到一个类中
我们定义一个类,把上面三个函数都放在类中,并且令类的实例变成可调用对象。

class ReorderLink:
    @staticmethod
    def find_mid_node(head):
        if head is None or head.next is None:
            return head
        fast = head
        slow = head
        prev = slow
        while fast is not None and fast.next is not None:
            fast = fast.next.next
            prev = slow
            slow = slow.next
        prev.next = None
        return slow  # 下半段的head

    @staticmethod
    def reverse(head):  #
        p = head
        if p is None or p.next is None:
            return p
        q = p
        p = p.next
        while p is not None:
            tmp = p.next
            p.next = q
            if q == head:
                q.next = None
            q = p
            p = tmp
        return q

    @staticmethod
    def reorder(head1, head2):
        cur1 = head1
        cur2 = head2
        tmp = None
        while cur1.next is not None:
            tmp = cur1.next
            cur1.next = cur2
            cur1 = tmp

            tmp = cur2.next
            cur2.next = cur1
            cur2 = tmp

        cur1.next = cur2  # 无论cur2是不是None,cur的next都应该指向cur2

    def __call__(self, link_head):
        before = link_head
        rest =self.find_mid_node(before)
        rest = self.reverse(rest)  # 反转
        self.reorder(before, rest)
        return link_head

测试
构造一个链表,创建一个类的实例,调用类的实例。

class Node:  # 结点类
    def __init__(self, data=None):
        self.data = data
        self.next = None

class LinkList: # 链表
    def __init__(self):
        self.head = None

    def append(self, x):
        if self.head is None:
            self.head = Node(x)
            return self
        p = self.head
        while p.next is not None:
            p = p.next
        p.next = Node(x)
        return self


link1 = LinkList()
link1.append(1).append(2).append(3).append(4).append(5).append(6)

head9 = link1.head

p = head9
print("排序前: ", end=" ")
while p is not None:
    print(p.data, end="\t")
    p = p.next

instance = ReorderLink()  # 实例
head9 = instance(head9)  # 实例可调用
print()

p = head9
print("排序后: ", end=" ")
while p is not None:
    print(p.data, end="\t")
    p = p.next


最后:

if hasattr(reader, "QQ"):
	print(f"请{reader}加入交流群:6259 88679 !")
posted @ 2019-10-28 13:00  段明  阅读(557)  评论(0编辑  收藏  举报