2014-02-21 39 views
4

一個堆棧可以實現爲一個鏈表。 鏈表可以用歸併排序來排序: 爲O(n log n)的時間 O(n)的空間是否可以使用合併排序對堆棧進行排序?

這是有道理的,以便能夠使用排序合併排序堆。

如果是這樣的話,代碼將是什麼樣子?

(在網絡上快速搜索後,我才發現蠻力算法爲O(n^2))

+1

你能只使用pop()方法和推()方法?或者你將棧看作一個玻璃盒,你可以根據需要操作內部組件嗎? – amit

+0

@amit我認爲第一件事。否則 - 與數組(一般)有什麼區別? –

+0

流行,推,窺視,isEmpty只允許 – Raul

回答

6

是的,我們能做到。技巧是理解當棧被排序時,頭是最大的元素 - 我們希望將它從低到高迭代。但是我們可以在O(n)中反轉棧。現在

reverse(stack): 
    s <- new stack 
    while stack.isEmpty() == false: 
     s.push(stack.pop) 
    return s 

,使用它,並假設你可以使用大小()爲好,這是非常容易實現,對於大多數堆棧實現標準的 - 我們可以實現合併排序:

僞代碼:

mergeSort(stack): 
    if stack.isEmpty(): 
     return stack 
    s1 <- new stack 
    s2 <- new stack 
    // O(n): 
    while s1.size() < stack.size(): 
     s1.push(stack.pop()) 
    while (stack.isEmpty() == false): 
     s2.push(stack.pop())   
    mergeSort(s1) //half the size of stack 
    mergeSort(s2) //half the size of stack 
    //head of s1 and s2 is the largest element 
    s1 <- s1.reverse() //easily implemented in O(n) 
    s2 <- s2.reverse() 
    //now head of s1 and s2 is the smallest element 
    while (s1.isEmpty() == false and s2.isEmpty() == false): 
     if (s1.peek() < s2.peek()): 
      stack.push(s1.pop()) 
     else: 
      stack.push(s2.pop()) 
    //the 'tail' of one stack: 
    while (s1.isEmpty() == false): 
     stack.push(s1.pop()) 
    while (s2.isEmpty() == false): 
     stack.push(s2.pop()) 
    //head is the largest, stacks are sorted 
    return stack 

正確性:
基地:停止子句是一個空棧,其排序。
假設:s1和s2被排序。
步驟:反轉後,當使用pop()方法取出元素時,s1和s2基本上按照lower-> higher的順序遍歷。由於我們總是從每個堆棧中插入較小的元素,並且我們將每個堆棧從低到高遍歷 - 我們得到的結果堆棧是有序的。

複雜性:
剔除遞歸調用,每一步都是O(stack.size()) = O(n)。這與常規合併排序的行爲相同,其餘複雜性遵循原始合併排序的相同步驟以獲得O(nlogn)

+0

很好的解釋。 –

1

也許我錯過了這一點,但我會做這種方式:

void mergesortStack(Stack input) { 
    if (input.isEmpty()) { 
     return; 
    } 

    Stack stackLeft = new Stack(); 
    Stack stackRight = new Stack(); 

    // split 
    while (!input.isEmpty()) { 
     stackLeft.push(input.pop()); 
     if (!input.isEmpty()) { 
      stackRight.push(input.pop()); 
     } 
    } 

    // recurse 
    if (!stackLeft.isEmpty() && !stackRight.isEmpty()) { 
     mergesortStack(stackLeft); 
     mergesortStack(stackRight); 
    } 

    // merge 
    Stack tmpStack = new Stack(); 
    while (!stackLeft.isEmpty() || !stackRight.isEmpty()) { 
     if (stackLeft.isEmpty()) { 
      tmpStack.push(stackRight.pop()); 
     } else if (stackRight.isEmpty()) { 
      tmpStack.push(stackLeft.pop()); 
      // set an appropriate compare method 
     } else if (stackLeft.peek().compareTo(stackRight.peek()) >= 0) { 
      tmpStack.push(stackLeft.pop()); 
     } else { 
      tmpStack.push(stackRight.pop()); 
     } 
    } 

    // reverse stack 
    while (!tmpStack.isEmpty()) { 
     input.push(tmpStack.pop()); 
    } 
} 
+0

你錯過了一些非常重要的東西。假設遞歸調用的正確性,每個較小的堆棧都將具有最大值。但是你會先推它。它會導致堆棧順序錯誤 - 因爲頭部不再是最大的元素。這與算法的正確性相矛盾。它可以很容易地解決 - 但它是這個問題中最重要的方面,IMO。 – amit

+0

@amit感謝您指出,修復代碼 –

+0

在遞歸階段isEmpty檢查似乎是多餘的 – Raul

相關問題