【问题标题】:Why is middle.next set to null?为什么 middle.next 设置为 null?
【发布时间】:2019-09-12 06:32:44
【问题描述】:

所以我正在尝试对这个链表进行排序,我理解代码的每一部分,除了这一点,在函数 mergeSort 下,第 9 行。 为什么middle.next 必须设置为null?我看不出这有什么必要?

这是我从哪里获得代码的链接(在 java 示例代码下):

https://www.geeksforgeeks.org/merge-sort-for-linked-list/

代码如下:

// Java program to illustrate merge sorted 
// of linkedList 

public class linkedList { 
    node head = null; 
    // node a, b; 
    static class node { 
        int val; 
        node next; 

        public node(int val) { 
            this.val = val; 
        } 
    } 

    node sortedMerge(node a, node b) { 
        node result = null; 
        /* Base cases */
        if (a == null) 
            return b; 
        if (b == null) 
            return a; 

        /* Pick either a or b, and recur */
        if (a.val <= b.val) { 
            result = a; 
            result.next = sortedMerge(a.next, b); 
        } else { 
            result = b; 
            result.next = sortedMerge(a, b.next); 
        } 
        return result; 
    } 

    node mergeSort(node h) { 
        // Base case : if head is null 
        if (h == null || h.next == null) { 
            return h; 
        } 

        // get the middle of the list 
        node middle = getMiddle(h); 
        node nextofmiddle = middle.next; 

        // set the next of middle node to null 
        middle.next = null; 

        // Apply mergeSort on left list 
        node left = mergeSort(h); 

        // Apply mergeSort on right list 
        node right = mergeSort(nextofmiddle); 

        // Merge the left and right lists 
        node sortedlist = sortedMerge(left, right); 
        return sortedlist; 
    } 

    // Utility function to get the middle of the linked list 
    node getMiddle(node h) { 
        // Base case 
        if (h == null) 
            return h; 
        node fastptr = h.next; 
        node slowptr = h; 

        // Move fastptr by two and slow ptr by one 
        // Finally slowptr will point to middle node 
        while (fastptr != null) { 
            fastptr = fastptr.next; 
            if (fastptr != null) { 
                slowptr = slowptr.next; 
                fastptr = fastptr.next; 
            } 
        } 
        return slowptr; 
    } 

    void push(int new_data) { 
        /* allocate node */
        node new_node = new node(new_data); 

        /* link the old list off the new node */
        new_node.next = head; 

        /* move the head to point to the new node */
        head = new_node; 
    } 

    // Utility function to print the linked list 
    void printList(node headref) { 
        while (headref != null) { 
            System.out.print(headref.val + " "); 
            headref = headref.next; 
        } 
    } 

    public static void main(String[] args) { 

        linkedList li = new linkedList(); 
        /* 
         * Let us create a unsorted linked list to test the functions 
         * created. The list shall be a: 2->3->20->5->10->15 
         */
        li.push(15); 
        li.push(10); 
        li.push(5); 
        li.push(20); 
        li.push(3); 
        li.push(2); 

        // Apply merge Sort 
        li.head = li.mergeSort(li.head); 
        System.out.print("\n Sorted Linked List is: \n"); 
        li.printList(li.head); 
    } 
} 

// This code is contributed by Rishabh Mahrsee 

【问题讨论】:

  • 听起来像是将列表分成两个较小的列表,所以它可以递归调用自己
  • 请注意,bottom up iterative merge sort for linked list 消除了扫描拆分列表的需要。相反,它使用一个小的(26 到 32 个)指针数组或对节点的引用。
  • @AlanKamali:您可以通过点击分数下方的灰色复选标记来接受其中一个答案。

标签: java linked-list mergesort


【解决方案1】:

mergeSort 将中间节点的next 成员设置为null 以将列表h 拆分为两个单独的列表hnextofmiddle,它通过递归调用自身进行排序,然后合并生成sortedList.

但是请注意,此代码有一个主要问题:方法sortedMerge 也是递归的,其堆栈深度是两个列表的组合长度,可能很大。编译器无法将这种递归简化为尾递归,因此使用此代码对长列表进行排序可能会崩溃。

【讨论】:

    【解决方案2】:

    middle.next = null 的目的是将列表分成两部分。链表中最后一个节点之后的值应该始终为NULL,因此作者将middle节点的next节点设置为NULL值。作者正在创建一个包含从headmiddle 的节点的子列表和第二个包含从nextofmiddle 到列表末尾的节点的子列表。每次调用此函数时,子列表都会被分成更小的部分,这是归并排序过程的一部分。

    【讨论】:

      【解决方案3】:

      所以如果你看到整个代码,mergeSort() 会被递归调用,

       // Apply mergeSort on left list 
          node left = mergeSort(h); 
      
       // Apply mergeSort on right list 
          node right = mergeSort(nextofmiddle); 
      

      而且通过放middle.next = null,其实就是打破了调用方法在方法栈中的递归。所以当我们递归调用同一个方法时,它会返回当前节点,

      if (h == null || h.next == null) { 
          return h; 
      }
      

      所以如果你看到伪代码,

      MergeSort(headRef)
      1) If the head is NULL or there is only one element in the Linked List 
         then return.
      2) Else divide the linked list into two halves.  
         FrontBackSplit(head, &a, &b); /* a and b are two halves */
      3) Sort the two halves a and b.
          MergeSort(a);
          MergeSort(b);
      4) Merge the sorted a and b (using SortedMerge() discussed here) 
         and update the head pointer using headRef.
         *headRef = SortedMerge(a, b);
      

      它有效地将列表分成两半,并通过调用相同的方法分别对它们进行 MergeSort。为了达到同样的效果,首先复制nextofmiddle 中的下一个值,然后他们制作middle.next = null; 以打破递归循环。

      我希望这能澄清你的疑问。

      【讨论】:

        【解决方案4】:

        所以看起来作者是在node getMiddle(node h)方法中寻找列表的中点。这将返回中间节点,但您希望将列表分成两部分,左侧和右侧。

        设置middle.next = null 将列表分成两部分。

        【讨论】:

          【解决方案5】:

          就是将列表一分为二:当找到中间元素时,第一部分会在中间元素处结束,所以middle.next设置为null。 middle.next 的先前值是第二部分的开始。

          【讨论】:

            猜你喜欢
            • 2021-11-22
            • 2010-10-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2020-05-13
            • 2013-05-09
            • 1970-01-01
            相关资源
            最近更新 更多