【问题标题】:Non-recursive merge sort with two nested loops - how?具有两个嵌套循环的非递归合并排序 - 如何?
【发布时间】:2011-12-07 09:34:35
【问题描述】:

这里的第一个问题,是的,这是一个家庭作业问题。我们的任务是对数组(我很熟悉)执行合并排序,但在某种程度上我不确定该怎么做。通常我会有一个单独的合并和合并排序功能,并使用两者。但是,听起来他想要一种方法中的所有东西?我只是希望也许有人可以帮我理清思路,或者用我更容易理解的方式来表达。

来自作业:

您将需要实现非递归版本的合并排序 算法。安排两个嵌套循环来完成此任务。外层 循环应该提供用于合并的段的大小。内循环 应该注意选择段的位置。内循环 应该从左边缘开始并将您的段向右移动。 将变量的适当值左、中、右排列,使 排序是通过迭代调用来完成的 合并(一,左,中,右)。

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

我不是要代码,但任何伪代码都会很有帮助。

如果有人能尝试破译他的意思,我将不胜感激。我已经给他发了电子邮件,但已经过去了几个小时,我还没有收到回复。

谢谢!

【问题讨论】:

  • 我认为“提供”他的意思是在外部循环的顶部会有代码计算段大小并将其存储在局部变量中,然后可以通过内循环。 “段”可能是指数组的子部分。

标签: c++ algorithm sorting mergesort


【解决方案1】:

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

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

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

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

现在从这里应该很明显了。您首先将数组的元素合并为 2 x 2,然后合并 4 x 4,然后合并 8 x 8 等。即外部 for 为您提供 2、4、8、16、32,...(这就是它所说的段的大小,因为循环的i 包含该数字)和内部for(例如使用迭代器j)遍历数组,i 通过i 合并array[j...j+i/2-1]array[j+i/2..j+i-1]

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

编辑:内部for 工作原理的图片

想象如果i 是 4,那么你就在这个阶段:

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

你将有一个for,它曾经给你0(即0*i)作为j,然后4(即1*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 组成),然后将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;
}

所以,如果你总是写

for (int i = 0; i < 10; ++i)

意思是从0开始,而i小于10,用i做一些事情,然后递增。现在如果你想让i改变不同,你只需改变statement2比如:

for (int i = 1; i < 1024; i *= 2)

(尝试根据我写给你的等效 while 来理解最终的 for 是如何工作的)

【讨论】:

  • @GWW 用闪闪发光的星星更换+s,你想要的任何女孩都是你的:D
  • 感谢您的帮助!不过我还是有点困惑。这就是我翻译你所说的话的一种方式:for (int i=1; i &lt; ??/*when should this stop?*/; i*=2_{ for (int j=0; j &lt; sizeofArray; j++){ merge //confused here as well - what 2 arrays am I merging? } } 抱歉,评论代码太丑陋了。有什么办法可以解决吗?
  • 好吧,想想看,i 显示了什么?必须分成两半并合并的段的大小。需要分成两半合并的最大部分是什么?
  • 关于jj 应该显示长度为i 的部分的开头。长度为i 的部分的开头是什么?它们是0i2*i3*i4*i 等。所以j 不是一比一,而是ii
  • 对于我来说,我认为需要分成两半并合并的最大段是原始数组。所以如果 n 是原始数组的大小,在 i = n 处停止?
【解决方案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;
}

【讨论】:

    【解决方案3】:

    这里是 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);
                }
            }
    

    【讨论】:

      【解决方案4】:

      这里是 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 ++;
              }
          }       
      }
      

      这是单元测试 私有整数[] 种子;

      @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));  
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-06
        • 1970-01-01
        • 1970-01-01
        • 2019-09-09
        • 2018-01-04
        • 2014-09-14
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多