遞歸轉迭代實操記錄

針對那些經典的像素遊戲設計的自動切圖工具裏用到種子填充算法的實現。算法

一開始是用遞歸實現的,後來遇到一些頭像之類的比較大一點的圖素,運行的時候常常佔滿C#默認的1M線程棧內存而崩潰。嘗試使用各類多線方式改造並無成功,因而乾脆改爲迭代形式,建立一個Stack本身徹底精確控制其中的數據操做。多線程

這裏截取一段改爲迭代後的代碼,這是窗體的事件處理用cs代碼。由於只是個小工具,沒有徹底把界面和邏輯分離。app

//堆棧最大深度 private const int MaxStackFrames = 640 * 480; private Stack<FillStackFrame> mStack = new Stack<FillStackFrame>(MaxStackFrames); private void fillRegion(int x, int y) { //防止多線程操做堆棧引發的問題 lock (this) { //從UI初始化本次調用不會變的參數 int distance = Convert.ToInt32(txtDistance.Text); //復位堆棧而且壓入初始數據 mStack.Clear(); var frame = new FillStackFrame(); frame.x = x; frame.y = y; mStack.Push(frame); //堆棧爲空即全部鏈接區域被填充完畢,執行結束 while (mStack.Count > 0) { //彈出堆棧數據 var popped = mStack.Pop(); x = popped.x; y = popped.y; //遞歸終止條件1 遇到填充區域越界 if (x < 0 || y < 0 || x >= srcImg.Width || y >= srcImg.Height) continue; //遞歸終止條件2 遇到背景色,或者已搜索像素 if (!isTransparent(srcImg, x, y) && tmpImg.GetPixel(x, y).A != 255) { tmpImg.SetPixel(x, y, fillColor); if (maxX < x) maxX = x; if (minX > x) minX = x; if (maxY < y) maxY = y; if (minY > y) minY = y; for (int i = 1; i <= distance; ++i) { if (mStack.Count >= MaxStackFrames) throw new StackOverflowException("填充蒙版時堆棧溢出!"); //八個方向延伸搜索,這裏的全部堆棧Push操做換回FillRegion方法的調用,就是本來的遞歸寫法了 mStack.Push(new FillStackFrame() { x = x - i, y = y}); mStack.Push(new FillStackFrame() { x = x + i, y = y}); mStack.Push(new FillStackFrame() { x = x, y = y - i}); mStack.Push(new FillStackFrame() { x = x, y = y + i}); mStack.Push(new FillStackFrame() { x = x - i, y = y - i}); mStack.Push(new FillStackFrame() { x = x + i, y = y + i}); mStack.Push(new FillStackFrame() { x = x - i, y = y + i}); mStack.Push(new FillStackFrame() { x = x + i, y = y - i}); } } } } }

總結一下遞歸轉迭代的操做步驟:函數

  1. 初始化函數調用前用到的類的成員變量和常量,放在類的成員變量或者常量直接初始化就能夠。
  2. 初始化在本次調用不變的數據好比這裏的延伸距離distance,放在進入迭代循環以前。
  3. 把遞歸函數的參數合成一個類型Frame,建立一個Stack<Frame>來代替運行時提供的棧內存。這個Stack根據具體狀況能夠是類的成員變量也能夠是函數的局部變量。
  4. 壓入初始的傳入參數幀
  5. 進入迭代循環,迭代循環基本就是本來函數的遞歸執行體改造過來。
  6. 迭代循環中把全部遞歸調用自身的函數換成新參數構建成幀而且壓入Stack。
  7. 原有的return改爲對迭代循環的continue。
  8. 若是有跳出全部原來遞歸的須要,在迭代循環中加入break。
  9. 其餘操做順序維持不變。
相關文章
相關標籤/搜索