具有两个嵌套循环的非递归合并排序 - 如何?

时间:2011-10-14 00:37:12

标签: c++ algorithm sorting mergesort

这里有第一个问题,是的,这是一个家庭作业问题。我们的任务是在数组上执行合并排序(我很熟悉),但在某种程度上我不确定如何做。通常我会有一个单独的合并和合并排序功能,并使用这两个。然而,听起来他想用一种方法做所有事情?我只是希望也许有人可以帮我清理一下,或者说出我能更好理解的条款。

来自作业:

  

您需要实现merge-sort的非递归版本   算法。安排两个嵌套循环来完成此任务。外面的   loop应该提供合并段的大小。内循环   应该注意选择细分的位置。内循环   应该从左边缘开始并将您的线段向右移动。   安排适当的变量值left,middle,right,这样   只需通过迭代调用即可完成排序   合并(A,左,中,右)。

我讨厌这么模糊,但我真的不明白他说的是什么。首先,“外环应该提供段的大小”是什么意思?循环如何提供任何东西?下一句话怎么样?他对段的意思是什么?数据?

我不是要求代码,但任何伪代码都会非常有用。

如果有人试图破译他的意思,我会很感激。我已经通过电子邮件向他发了电子邮件,但已经过了几个小时,我还没有收到回复。

谢谢!

4 个答案:

答案 0 :(得分:44)

这并不困难。考虑递归合并:

       +-+-+-+-+-+-+-+-+
       | | | | | | | | |
       +-+-+-+-+-+-+-+-+
            /     \               split
    +-+-+-+-+     +-+-+-+-+
    | | | | |     | | | | |
    +-+-+-+-+     +-+-+-+-+
      /   \         /  \          split
  +-+-+  +-+-+  +-+-+  +-+-+
  | | |  | | |  | | |  | | |
  +-+-+  +-+-+  +-+-+  +-+-+
  / \     / \     / \     / \     split
+-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
| | | | | | | | | | | | | | | |
+-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
  \ /     \ /     \ /     \ /     merge
  +-+-+  +-+-+  +-+-+  +-+-+
  | | |  | | |  | | |  | | |
  +-+-+  +-+-+  +-+-+  +-+-+
      \   /         \  /          merge
    +-+-+-+-+     +-+-+-+-+
    | | | | |     | | | | |
    +-+-+-+-+     +-+-+-+-+
            \     /               merge
       +-+-+-+-+-+-+-+-+
       | | | | | | | | |
       +-+-+-+-+-+-+-+-+

如果您注意到,当您分手时,您并没有真正做任何事情。您只需告诉递归函数对数组进行部分排序。对数组进行排序包括首先对两半进行排序然后合并它。所以基本上,你拥有的是:

+-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
| | | | | | | | | | | | | | | |
+-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+
  \ /     \ /     \ /     \ /     merge
  +-+-+  +-+-+  +-+-+  +-+-+
  | | |  | | |  | | |  | | |
  +-+-+  +-+-+  +-+-+  +-+-+
      \   /         \  /          merge
    +-+-+-+-+     +-+-+-+-+
    | | | | |     | | | | |
    +-+-+-+-+     +-+-+-+-+
            \     /               merge
       +-+-+-+-+-+-+-+-+
       | | | | | | | | |
       +-+-+-+-+-+-+-+-+

现在从这里开始就应该是显而易见的。你首先合并数组2的元素2,然后4乘4,然后8乘8等。那就是外for给你2,4,8,16,32 ......(这是什么它调用段的大小,因为循环的i包含该数字)并且内部for(比如迭代器j)遍历数组, i iarray[j...j+i/2-1]array[j+i/2..j+i-1]合并。{/ p>

我不会写代码,因为这是作业。

编辑:内部for如何运作的图片

想象一下,如果i是4,那么你就处于这个阶段:

  +-+-+  +-+-+  +-+-+  +-+-+
  | | |  | | |  | | |  | | |
  +-+-+  +-+-+  +-+-+  +-+-+
      \   /         \  /          merge
    +-+-+-+-+     +-+-+-+-+
    | | | | |     | | | | |
    +-+-+-+-+     +-+-+-+-+

您将拥有一个for,一次为您00*i)为j,然后为41*i })j。 (如果i为2,则j将为0,2,4,6)

现在,您需要将array[0..1]array[2..3]合并(由array[j..j+i/2-1]array[j+i/2..j+i-1]j = 0合并)然后{{1} array[4..5] {由array[6..7]array[j...j+i/2-1]制定,因为现在array[j+i/2..j+i-1])即:

j = 4

希望至少有一点清楚。


帮助:如果你真的不知道i = 4: +-+-+-+-+-+-+-+-+ | | | | | | | | | +-+-+-+-+-+-+-+-+ ^ ^ ^ ^ ^ ^ ^ ^ | | | | | | | | / / / / \ \ \ \ (j = 0) (j = 4) | | | | | | | | j | | | j | | | | | | j+i-1 | | | j+i-1 | | j+i/2 | | j+i/2 | j+i/2-1 | j+i/2-1 | | | | | | | | | | | | | | | | \ / \ / \ / \ / v v v v merge merge 是如何运作的,那就是一个提示:

for

就像写作

for (statement1; condition; statement2)
{
    // processing
}

所以,如果你总是写

statement1;
while (condition)
{
    // processing
    statement2;
}

它意味着从0开始,而for (int i = 0; i < 10; ++i) 小于10,使用i执行某些操作然后递增它。现在,如果您希望i以不同方式进行更改,则只需更改i,例如:

statement2

(尝试根据我写给你的等效for (int i = 1; i < 1024; i *= 2) 了解最终for的工作原理。

答案 1 :(得分:2)

这是我使用std::merge的懒惰,迭代/自下而上的合并排序实现:

template<class InIt, class OutIt>
OutIt mergesort(InIt begin, InIt const end, OutIt o /* auxiliary buffer */)
{
    ptrdiff_t j;
    for (j = 0; begin != end; ++begin, ++j)
    {
        for (ptrdiff_t n = 1; n <= j && j % (n * 2) == 0; n *= 2)
        {
            o = std::merge(o - n * 2, o - n, o - n, o, begin - n * 2);
            o = std::swap_ranges(begin - n * 2, begin, o - n * 2);
        }
        *o = *begin;
        ++o;
    }
    --j;
    for (ptrdiff_t m = 1, n = 1; n <= j; n *= 2)
    {
        if (j & n)
        {
            o = std::merge(o - (m + n), o - m, o - m, o, o - (m + n));
            o = std::swap_ranges(begin - (m + n), begin, o - (m + n));
            m += n;
        }
    }
    return o;
}

这是一个使用std::inplace_merge

的就地版本
template<class InIt>
InIt inplace_mergesort(InIt begin, InIt const end)
{
    ptrdiff_t j;
    for (j = 0; begin != end; ++begin, ++j)
    {
        for (ptrdiff_t n = 1; n <= j && j % (n * 2) == 0; n *= 2)
        { std::inplace_merge(begin - n * 2, begin - n, begin); }
    }
    --j;
    for (ptrdiff_t m = 1, n = 1; n <= j; n *= 2)
    {
        if (j & n)
        {
            std::inplace_merge(begin - (m + n), begin - m, begin);
            m += n;
        }
    }
    return begin;
}

答案 2 :(得分:0)

这是自下而上合并的C#版本(有关更多详细信息,请参阅我的博客@ http://dream-e-r.blogspot.com/2014/07/mergesort-arrays-and-lists.html

void BottomUpMergesort(int[] a)
        {
            int[] temp = new int[a.Length];
            for (int runWidth = 1; runWidth < a.Length; runWidth = 2 * runWidth)
            {
                for (int eachRunStart = 0; eachRunStart < a.Length; 
                    eachRunStart = eachRunStart + 2 * runWidth)
                {
                    int start = eachRunStart;
                    int mid = eachRunStart + (runWidth - 1);
                    if(mid >= a.Length)
                    {
                        mid = a.Length - 1;
                    }
                    int end = eachRunStart + ((2 * runWidth) - 1);
                    if(end >= a.Length)
                    {
                        end = a.Length - 1;
                    }

                    this.Merge(a, start, mid, end, temp);
                }
                for (int i = 0; i < a.Length; i++)
                {
                    a[i] = temp[i];
                }
            }

合并定义为:

void Merge(int[] a, int start, int mid, int end, int[] temp)
        {
            int i = start, j = mid+1, k = start;
            while((i<=mid) && (j<=end))
            {
                if(a[i] <= a[j])
                {
                    temp[k] = a[i];
                    i++;
                }
                else
                {
                    temp[k] = a[j];
                    j++;
                }
                k++;
            }
            while(i<=mid)
            {
                temp[k] = a[i];
                i++;
                k++;
            }
            while (j <= end)
            {
                temp[k] = a[j];
                j++;
                k++;
            }
            Assert.IsTrue(k == end+1);
            Assert.IsTrue(i == mid+1);
            Assert.IsTrue(j == end+1);
        }

        }

这里仅供参考:TopDownMergesort:

void TopDownMergesort(int[] a, int[] temp, int start, int end)
        {
            if(start==end)
            {
                //run size of '1'
                return;
            }
            int mid = (start + end) / 2;
            this.TopDownMergesort(a, temp, start, mid);
            this.TopDownMergesort(a, temp, mid + 1, end);
            this.Merge(a, start, mid, end, temp);
            for(int i = start;i<=end;i++)
            {
                a[i] = temp[i];
            }
        }

<强>单元测试

[TestMethod]
        public void BottomUpMergesortTests()
        {
            int[] a = { 13, 4, 1, 3, 8, 11, 9, 10 };
            this.BottomUpMergesort(a);
            int[] b = { 1, 3, 4, 8, 9, 10, 11, 13 };
            Assert.IsTrue(a.Length == b.Length);
            for (int i = 0; i < a.Length; i++)
            {
                Assert.IsTrue(a[i] == b[i]);
            }
            List<int> l = new List<int>();
            for (int i = 10; i >= 1; i--)
            {
                l.Add(i);
            }
            var la = l.ToArray();
            this.BottomUpMergesort(la);
            for (int i = 1; i <= 10; i++)
            {
                Assert.IsTrue(la[i - 1] == i);
            }
            l.Clear();
            for (int i = 16; i >= 1; i--)
            {
                l.Add(i);
            }
            la = l.ToArray();
            this.BottomUpMergesort(la);
            for (int i = 1; i <= l.Count; i++)
            {
                Assert.IsTrue(la[i - 1] == i);
            }
        }

答案 3 :(得分:0)

这是Java实现

public static <T extends Comparable<? super T>> void iterativeMergeSort(T[] seed) {

    for (int i = 1; i <seed.length; i=i+i)
    {
        for (int j = 0; j < seed.length - i; j = j + i+i)
        {
            inPlaceMerge(seed, j, j + i-1, Math.min(j+i + i -1, seed.length -1));
        }
    }       
}
public static <T extends Comparable<? super T>>  void inPlaceMerge(T[] collection, int low, int mid, int high) {
    int left = low;
    int right = mid + 1;

    if(collection[mid].equals(collection[right])) {
        return ;//Skip the merge if required
    }
    while (left <= mid && right <= high) {          
        // Select from left:  no change, just advance left
        if (collection[left].compareTo(collection[right]) <= 0) {
            left ++;
        } else { // Select from right:  rotate [left..right] and correct
            T tmp = collection[right]; // Will move to [left]
            rotateRight(collection, left, right - left);
            collection[left] = tmp;
            // EVERYTHING has moved up by one
            left ++; right ++; mid ++;
        }
    }       
}

这是单元测试     private Integer [] seed;

@Before
public void doBeforeEachTestCase() {
    this.seed = new Integer[]{4,2,3,1,5,8,7,6};
}
@Test
public void iterativeMergeSortFirstTest() {
    ArrayUtils.<Integer>iterativeMergeSort(seed);
    Integer[] result = new Integer[]{1,2,3,4,5,6,7,8};
    assertThat(seed, equalTo(result));  
}