2014-02-21 42 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

相关问题