【问题标题】:How to go about making a "Sorted Array to Balanced BST" recursion algorithm to an iterative one?如何将“排序数组到平衡 BST”递归算法变为迭代算法?
【发布时间】:2018-10-11 19:45:48
【问题描述】:

我已经四处搜索,但无法真正理解或找到帮助,因为这种迭代算法将需要两个堆栈(包含 left_Index 和 right_Index)。

主要的递归方式涉及将其放在一侧直到 left_Index >= right_Index,并递归地对两侧和每个小节执行此操作(如果这有意义的话),我不知道该怎么做,因为我' m 维护两个堆栈,需要查看它们之间的确切关系。

这个问题主要是因为我不理解普通的递归方法词的方式,虽然当我并排看它们以了解如何处理它时,我总是卡在该怎么做。


关于我为什么这样做的背景故事: 试图解决从 A 到 B 的字梯问题,并决定制作一个 BST,其中连接通过奇异字符差异和长度连接。我从包含大量字典的文本文件中获取单词,并且由于我使用 BST 作为具有所有顶点的主列表,因此这是字典的事实意味着每个插入都将按顺序排列,因此树是右倾(因此插入 O(n^2) 的速度很慢,这是一个 障碍)。我计划将数据存储在一个数组中,然后从中制作一个平衡的 BST,因为我相信速度应该会更快,因为插入将是 O(n*logn),这看起来很棒。这样做的问题是我不能使用递归方法,因为有很多数据会导致堆栈溢出,所以我需要使用堆栈和循环来迭代,但我发现它太难了。


我一开始的错误尝试:

而 (lindx.the_front() {
中 =(lindx.the_front() + rindx.the_back()) / 2;
dictionary.addVertex(vector[mid]);
std::cout }


这基本上是从我制作的链接堆栈中获取程序左半部分的 1/2。 “the_front()”是第一次插入,“the_back()”是最后/最新插入到列表中。我遇到的主要问题是了解如何让它每半场重复一次以获得所有值。


我需要找到我过去做过的作业,但代码类似于...

void array2balanced(int array[], int lIndex, int rIndex) 
{  
  //base case
  if(lIndex > rIndex) 
  {
    return; 
  } 
  //recursive cals
  else 
  {  
    mid = (lIndex+rIndex)/2;  
    tree.insert(array[mid]);  
    array2balanced(array, lIndex, mid-1);  
    array2balanced(array, mid+1, rIndex); 
  } 
}

更新: 目前的进展

void balancedTree(std::vector<std::string> vector, dictionaryGraph &dictionary) // divide and conquer into tree?
{
    linkedStack<int> lindx, rindx, midX;
    unsigned int l_Index{ 0 }, r_Index{ vector.size() - 1 }, mid{ (l_Index + r_Index) / 2 };;
    lindx.push(l_Index);
    rindx.push(r_Index);
    midX.push(mid);
    int testCount{ 0 };
    std::cout << "There are " << vector.size() << " words.\n";

    while (!midX.empty())
    {
        mid = midX.pop();
        l_Index = lindx.pop();
        r_Index = rindx.pop();
        std::cout << "inputted " << vector[mid] << '\n';
        dictionary.addVertex(vector[mid]);
        testCount++;

        if (r_Index > l_Index)
        {

            midX.push((l_Index + mid) / 2);
            lindx.push(l_Index);
            rindx.push(mid - 1);
        }
        if (l_Index < r_Index)
        {
            midX.push((mid + r_Index) / 2);
            lindx.push(mid + 1);
            rindx.push(r_Index);
        }
    }
    std::cout << testCount << " words were inputted...\n"; // To see how many were inserted
    system("pause");
}

我遇到的问题是一些输入重复而一些丢失。

【问题讨论】:

  • 你能把你见过的递归算法的伪代码贴出来吗?
  • 1) 获取数组的中间并使其成为根。 2)递归地对左半边和右半边做同样的事情。 a) 获取左半部分的中间,使其成为步骤 1 中创建的根的左子。 b) 获取右半的中间,使其成为步骤 1 中创建的根的右子。 - geeksforgeeks.org
  • 这是一个前序遍历,所以你想要的是迭代前序遍历算法 (en.wikipedia.org/wiki/Tree_traversal#Pre-order) 的变体,但是你将位置与节点堆叠在一起。
  • 谢谢!我仍然遇到麻烦,因为我的实现不起作用,(因为一些索引丢失并且一些重复)但我认为我已经接近了!

标签: c++ loops recursion stack


【解决方案1】:

这个问题主要是因为我不了解正常的方式 递归方法词,尽管并排查看它们时 看看如何处理它,我总是卡在该怎么做。

这需要练习……也许还需要审查其他人的工作。

需要两个堆栈(以包含 left_Index 和 right_Index)。

抱歉,我不明白为什么 OP 会这样想。我下面的演示只有 1 个堆栈称为 'todo',也许你会发现这个想法很有用。

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <vector>
#include <cassert>

#include "./BTree.hh"  // code not provided, used in this MCVE to 
                       // conveniently provide "showTallTreeView()" 

typedef std::vector<int>  IVec_t;  

class T607_t
{
   IVec_t m_sortedIVec;    // sorted - created with for loop
   IVec_t m_recursiveIVec; // extract from sorted by recursion
   IVec_t m_iterativeIVec; // extract from sorted by iteration

public:

   T607_t() = default;
   ~T607_t() = default;

   int exec(int , char**  )
      {
         fillShowSortedIVec();

         fillShowRecursiveIVec();

         fillShowIterativeIVec();

         showResults();

         return 0;
      }

private: // methods

向量属于 T607_t 类,因此每个向量都可用于任何成员函数。

对于这个 MCVE,我只需创建“IVec_t m_sortedIVec;”并填充一个简单的 for 循环:

   void fillShowSortedIVec()
      {
         for (int i=0; i<15; ++i)
            m_sortedIVec.push_back (i*100);  // create in sorted order

         showIVec(m_sortedIVec, "\n  m_sortedIVec   :");   
      }

接下来(在这个 MCVE 中)是递归填充和显示,以及我对 OP 的递归方法的改编以产生递归插入序列:

   // ///////////////////////////////////////////////////////////////
   void fillShowRecursiveIVec()
      {
         assert(m_sortedIVec.size() > 0);
         int max = static_cast<int>(m_sortedIVec.size()) - 1;

         // use OP's recursive insert
         array2balancedR (m_sortedIVec, 0, max);  
         // NOTE - 'sequence' is inserted to 'm_recursiveIVec'
         //        instead of into tree the op did not share

         showIVec(m_recursiveIVec, "\n  m_recursiveIVec:");
      }

   // recursive extract from: m_sortedIVec  to: m_recursiveIVec
   // my adaptation of OP's recursive method
   void array2balancedR(IVec_t& array, int lIndex, int rIndex)
      {
         //base case
         if(lIndex > rIndex)
         {
            return;
         }
         else    //recursive calls
         {
            int mid = (lIndex+rIndex)/2;
            m_recursiveIVec.push_back(array[mid]);  // does this
            // tree.insert(array[mid]);             // instead of this

            array2balancedR(array, lIndex, mid-1);  // recurse left
            array2balancedR(array, mid+1, rIndex);  // recurse right
         }
      }

注意:我将“IVec_t& 数组”作为参数留给了这个函数,因为 OP 的代码有它。在这个“类”包装器中,函数不需要“通过递归”传递数组,因为每个方法都可以访问实例数据。

下一步(在此 MCVE 中)是使用一种可能的迭代方法的填充和显示操作。我仔细设计了这种迭代方法以匹配 OP 的递归工作。

首先,我添加了一个“工具”(IndxRng_t)来简化迭代的“堆栈”捕获,以供以后处理。 (即“待办事项”)。

   // //////////////////////////////////////////////////////////////
   // iterative extract from  m_sortedIVec  to: m_iterativeIVec
   class IndxRng_t  // tool to simplify iteration
   {
   public:
      IndxRng_t() = delete; // no default
      IndxRng_t(int li, int ri)
         : lIndx (li)
         , rIndx (ri)
         {}
      ~IndxRng_t() = default;

      // get'er and set'er free.  also glutton free.  gmo free.
      bool            done() { return (lIndx > rIndx); } // range used up
      int              mid() { return ((lIndx + rIndx) / 2); } // compute
      IndxRng_t   left(int m) { return {lIndx, m-1}; }  // ctor
      IndxRng_t  right(int m) { return {m+1, rIndx}; }  // ctor
   private:
      int lIndx;
      int rIndx;
   };


   void fillShowIterativeIVec()
      {
         assert(m_sortedIVec.size() > 0);
         int max = static_cast<int>(m_sortedIVec.size()) - 1;

         array2balancedI(m_sortedIVec, 0, max); 
         // 'sequence' inserted to 'm_iterativeIVec'

         showIVec(m_iterativeIVec, "\n  m_iterativeIVec:");
      }


   void array2balancedI(IVec_t& array, int lIndex, int rIndex)
      {
         std::vector<IndxRng_t>  todo;
         todo.push_back({lIndex, rIndex}); // load the first range

         // iterative loop (No recursion)
         do
         {
            if (0 == todo.size()) break; // exit constraint
            // no more ranges to extract mid from

            // fetch something to do
            IndxRng_t  todoRng = todo.back();
            todo.pop_back(); // and remove from the todo list

            if(todoRng.done()) continue; // lIndex > rIndex 

            int mid = todoRng.mid();
            m_iterativeIVec.push_back(array[mid]);  // do this
            // tree.insert(array[mid]);             // instead of this

            todo.push_back(todoRng.right(mid) ); // iterate on right
            todo.push_back(todoRng.left(mid)  ); // iterate on left

         }while(1);
      }

并且这个 mcve 会生成一个结果显示:

   void showResults()
      {
         assert(m_recursiveIVec.size() == m_sortedIVec.size());
         assert(m_iterativeIVec.size() == m_sortedIVec.size());

         std::cout << std::endl;

         std::stringstream ss; // for btree use only

         std::cout << "\n  demo:\n     create a BTree, "
                   << std::flush;
         std::cout << "\n     Insert IVec_t " << std::endl;

         BBT::BTree_t btree(ss);
         std::cout << std::flush;

         for (size_t i=0; i<m_iterativeIVec.size(); ++i)
            btree.insertPL(m_iterativeIVec[i]);

         std::cout << "\n iterative result:\n\n" 
                   << btree.showTallTreeView();
      }


   void showIVec(IVec_t& ivec, std::string lbl)
   {
      std::cout << lbl << std::endl;
      for (auto it : ivec)
         std::cout << std::setw(5) << it << std::flush;
      std::cout << std::endl;
   }

}; // class T607_t


int main(int argc, char* argv[])
{
   T607_t  t607;
   return  t607.exec(argc, argv);
}

我的输出(在 Ubuntu 17.10,g++ 7.2.0 上),

  m_sortedIVec   :
    0  100  200  300  400  500  600  700  800  900 1000 1100 1200 1300 1400

  m_recursiveIVec:
  700  300  100    0  200  500  400  600 1100  900  800 1000 1300 1200 1400

  m_iterativeIVec:
  700  300  100    0  200  500  400  600 1100  900  800 1000 1300 1200 1400


  demo:
     create a BTree, 
     Insert IVec_t 

 iterative result:

  BTree_t::showTallTreeView():  (balance: 0  sz: 15)

                     0 
               100 
                    200 
          300 
                    400 
               500 
                    600 
     700 
                    800 
               900 
                    1000 
          1100 
                    1200 
               1300 
                    1400 

-----------------

【讨论】:

  • showTallTreeView() 在其一侧显示树......顶部在左侧。
【解决方案2】:

将排序数组转换为二叉搜索树 (BST) 的迭代 JavaScript 实现:

function sortedArrayToBstIteratively(nums) {
    // use stack to iteratively split nums into node tuples and reuse values
    const stack = []

    // add root node to tree
    const tree = { first: 0, last: nums.length - 1 }
    stack.push(tree)

    // split array in the middle and continue with the two halfs
    while (stack.length > 0) {
        const node = stack.pop()

        if (node.last >= node.first) {
            if (node.last === node.first) {
                // node reaches a single leaf value (last == first)
                node.value = nums[node.first]
            } else {
                // node has still valid indices to further split the array (last > first)
                const middle = Math.ceil((node.first + node.last) / 2)
                node.value = nums[middle]
                node.left = { first: node.first, last: middle - 1 }
                node.right = { first: middle + 1, last: node.last }
                stack.push(node.left)
                stack.push(node.right)
            }
        } else {
            // node has no more valid indices (last < first), create empty leaf
            node.value = null
        }

        delete node.first
        delete node.last
    }

    // console.log(JSON.stringify(tree))

    return tree
}

【讨论】:

    【解决方案3】:

    我认为您不需要两个堆栈。您只需要一个堆栈或一个队列。
    下面的代码可以在Leetcode上测试

    二叉树节点的定义。

    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    

    单栈法

    def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
        if not nums:
            return None
        l = len(nums)
        node = TreeNode(0)
        head = node
        s = collections.deque([(node, 0, l)])
        while s:
            node, left, right = s.pop()
            mid = (right + left) // 2
            node.val = nums[mid]
            if mid < right-1:
                node.right = TreeNode(0)
                s.append((node.right, mid+1, right))
            if left < mid:
                node.left = TreeNode(0)
                s.append((node.left, left, mid))
        return head
    

    一个队列方法

    def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
        if not nums:
            return None
        l = len(nums)
        node = TreeNode(0)
        head = node
        q = collections.deque([(node, 0, l)])
        while q:
            node, left, right = q.popleft()
            mid = (right + left) // 2
            node.val = nums[mid]
            if left < mid:
                node.left = TreeNode(0)
                q.append((node.left, left, mid))
            if mid < right-1:
                node.right = TreeNode(0)
                q.append((node.right, mid+1, right))
        return head
    

    它们是使用双端队列实现的。注意 popleft() 返回第一个元素(如栈),pop() 返回最后一个元素(如队列)。

    【讨论】:

      猜你喜欢
      • 2021-08-23
      • 2011-11-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-14
      • 1970-01-01
      • 2013-11-18
      • 2020-09-23
      相关资源
      最近更新 更多