【问题标题】:Sorting Algorithm - java sorting stacks排序算法 - java 排序堆栈
【发布时间】:2015-12-08 05:08:54
【问题描述】:

这是我想出的:

procedure sort(Stack S, Queue Q, sortedPosition)
    if(sortedPosition==0)
        // Sorting completed
        return;

    max = S.pop
    currentPosition = 0;

    while (!S.isEmpty()) do:
        currentPosition = currentPosition + 1;
        if(currentPosition < sortedPosition)
            current = S.pop();
            if(current > max)
                Q.add(max);
                max = current;
            else
                Q.add(current);
            end if
        end if

    end while

    S.push(max);
    currentPosition--;

    while (!Q.isEmpty()) do:
        S.push(Q.remove());
    end while


    sort(S, Q, currentPosition);
end procedure

如果我做错了什么,有人可以看看并告诉我吗?另外,最坏情况下的运行时间必须是 O(n^2)。

【问题讨论】:

  • 我认为如果您要问算法问题,那么最好发布更具解释性的伪代码。然后阅读代码并尝试理解算法将很容易理解。
  • 谢谢@Haris - 我会尝试编辑它。
  • 这是我能做到的最简单的。如果有什么不明白的地方,请告诉我,我会尽力解释。
  • 您在方法中定义队列Q,然后使用递归,这是行不通的 - 每次调用sort 都会有不同的Q
  • @MateuszDymczyk 谢谢 - 我已经改变了。请立即查看。

标签: algorithm sorting stack queue


【解决方案1】:

可以使用队列和堆栈实现自下而上的合并排序 O(n log(n))。首先,所有元素都从堆栈弹出到队列中(这将反转元素)。队列将被反向排序(反向比较的意义)。第一个合并过程从队列中弹出一个元素并将其推入堆栈,将 queue.front() 与 stack.top() 进行比较,将较小的元素弹出并推入队列,然后弹出并推入另一个元素,重复直到所有元素对合并,在队列中创建大小为 2 的排序运行。然后为下一次通过设置,队列循环以反转所有偶数运行(除非最后有一个单独的偶数运行而没有奇数运行要合并,在这种情况下它只是被复制而不是反转):来自队列的偶数运行是推入堆栈,然后从堆栈中弹出并推入队列以反转它们,奇数运行从队列中弹出并推入队列以按顺序复制它们。对于剩余的通行证,通过从队列中弹出一个偶数运行并将其推入堆栈来合并成对运行,在堆栈上“反转”它,然后在 stack.top() 的偶数运行与在 queue.front 的奇数运行合并(),将合并的运行推回队列。一旦所有对合并,运行大小加倍,如果运行大小 = queue.size() 并且队列被排序(反向)。反向排序队列被弹出并推入堆栈,创建一个排序堆栈。

我试图找出一种方法来在合并期间反转比较的意义,以避免在每次合并通过后进行反向偶数运行循环,但这是一种递归模式,我无法弄清楚如何用迭代复制。无论如何,更简单的反向偶数运行方法似乎足够快。

在我的系统上,Intel 2600K 3.4 ghz,Visual Studio release build,这种方法可以在大约 2.8 秒内对 400 万个伪随机 32 位无符号整数进行排序。

堆栈升序排序的示例代码(降序比较的反向意义)。 ll、rr 和 ee 是伪索引,以保持运行计数逻辑与数组/向量自下而上合并排序相同。实际的合并使用堆栈进行左/偶数运行,使用队列的一部分进行右/奇数运行。

typedef unsigned int uint32_t;

void sqsort(std::stack <uint32_t> &s , std::queue <uint32_t> &q)
{
    size_t n = s.size();
    while(!s.empty()){                      // pop/push s to q
        q.push(s.top());
        s.pop();
    }
    // merge sort usign stack and queue
    size_t r = 1;
    while(1){
        size_t ee = 0;                      // pseudo end index
        // merge pass
        while(ee < n){
            size_t ll = ee;                 // ll = pseudo start of left  run
            size_t rr = ll+r;               // rr = pseudo start of right run
            if(rr >= n){                    // if only left run
                while(ll++ < n){            //   copy it and break
                    q.push(q.front());
                    q.pop();
                }
                break;
            }
            ee = rr+r;                      // ee = pseudo end of right run
            if(ee > n)
                ee = n;
            // merge a pair of runs
            // stack == left / even run
            // queue == right / odd run
            size_t m = rr - ll;             // m = # elements in left run
            while(m--){                     //  pop/push left run to stack
                s.push(q.front());
                q.pop();
            }
            m = ee - rr;                    // m = # elements in right run
            while(1){
                if(q.front() > s.top()){    // (reverse for descending)
                    q.push(q.front());
                    q.pop();
                    if(--m == 0){           // if end right run
                        while(!s.empty()){  //   copy rest of left run
                            q.push(s.top());
                            s.pop();
                        }
                        break;              //   and break
                    }
                } else {
                    q.push(s.top());
                    s.pop();
                    if(s.empty()){          // if end left run
                        while(m--){         //   copy rest of right run
                            q.push(q.front());
                            q.pop();
                        }
                        break;              // and break
                    }
                }
            }                               // end merge pair
        }                                   // end merge pass
        r *= 2;                             // double run size
        if(r >= n)                          //  break if sort done
            break;
        // reverse left/even runs in q
        ee = 0;                             // pseudo end index
        while(ee < n){
            size_t ll = ee;                 // ll = pseudo start of left  run
            size_t rr = ll+r;               // rr = pseudo start of right run
            if(rr >= n){                    // if only left run
                while(ll++ < n){            //   copy it and break
                    q.push(q.front());
                    q.pop();
                }
                break;
            }
            ee = rr+r;                      // ee = pseudo end of right run
            if(ee > n)
                ee = n;
            size_t m = rr - ll;             // m = # elements in left run
            while(m--){                     // pop/push left run to s
                s.push(q.front());
                q.pop();
            }
            while(!s.empty()){              // pop/push s to q
                q.push(s.top());            //  (reverse left/even run)
                s.pop();
            }
            m = ee - rr;                    // m = # elements in right run
            while(m--){                     // copy odd run to q
                q.push(q.front());
                q.pop();
            }
        }                                   // end reverse left/even runs
    }                                       // end merge sort
    while(!q.empty()){                      // pop/push q to s
        s.push(q.front());
        q.pop();
    }
}

【讨论】:

  • @TiyebBellal - 添加了 C++ 示例代码。问题已编辑。最初它提到了一个关于如何仅使用堆栈和队列以及根据需要使用局部变量对堆栈进行排序的面试问题。
  • 好吧,为努力+1。
【解决方案2】:

这个问题很好。 我亲自检查了您的解决方案,它进入了无限循环,但您的方法非常好。我稍微修改了你使用的逻辑。我尽量保留你的方法

我更改的逻辑是首先我将所有堆栈元素复制到队列中。然后我使用我的登录来过滤队列,并将最高的元素一个一个插入堆栈。一旦一个元素(按排序顺序)被压入堆栈,相应的元素就会从队列中删除。

请检查以下我在 java 上完成的解决方案。希望对你有帮助。。

您必须(第一次)使用参数 i 调用排序方法。主堆栈,ii。一个空队列,以及 iii.元素的编号(堆栈的大小)..

Stack sort(Stack<Integer> s,Queue<Integer> q,int length)
    {

        if(length==0)
        {
            return s;
        }

        if(q.isEmpty())
        {
            while(!s.empty())
            {
                q.add(s.pop());
            }
        }

        int stckElement;
        int qElement;
        s.push(q.remove());

        int count=0;
        while(count<length-1)
        {
            stckElement=s.pop();
            qElement=q.remove();
            if(stckElement<qElement)
            {
                s.push(qElement);
                q.add(stckElement);
                count=0;
            }else
            {
                s.push(stckElement);
                q.add(qElement);
                count++;
            }
        }

        length--;
        return sort(s,q,length);
    }

【讨论】:

  • 谢谢 - 看起来好多了。所以最坏情况下的运行时间是 O(n^2) 对吧?
【解决方案3】:

如果您随时使用小于堆栈中元素数量的sortedPosition 调用该过程,则您的算法包含一个无限循环,因为:在增加currentPosition 的次数相等后,您将无法达到current = S.pop();sortedPosition,那么你将违反if条件并且堆栈不为空,那么你将继续增加currentPositionm,这是肯定的。

while之后,你应该设置sortedPosition--;而不是sortedPosition--;,然后调用sort for sortedPosition而不是currentPosition,在第一次调用中,它应该等于元素的数量。

正确的实现应该是:

procedure sort(Stack S, Queue Q, sortedPosition)
if(sortedPosition==0)
    // Sorting completed
    return;

max = S.pop
currentPosition = 0;

while (currentPosition < sortedPosition) do:
    if(currentPosition < sortedPosition)
        current = S.pop();
        if(current > max)
            Q.add(max);
            max = current;
        else
            Q.add(current);
        end if
    end if
    currentPosition = currentPosition + 1;
end while

S.push(max);
sortedPosition--;

while (!Q.isEmpty()) do:
    S.push(Q.remove());
end while


sort(S, Q, sortedPosition);
end procedure

【讨论】: