一個翻牌算法


本周四同事分享了一個思維訓練的PPT,里面有一個關於翻牌的題目,題目大致是:拿出從A到10的10張撲克牌,背面朝上摞在一起。首先把最上面的一張挪到下面,掀開新出現的一張牌是A,取出,再挪一張牌到下面,翻一張是2,依次類推,可以有順序地翻出A到10的牌來。請問這10張牌最初是怎么排列的?看完這個題目,我當時說可以用一個算法實現。

第二天6點多醒來就一直在想這個問題,開始的時候想用遞歸實現,最后發現有點復雜,自己實現不了,然后想用數組實現,想法大致是這樣的,先將這N個數存到數組中,然后將第一張插到最后面,第二張為A,以此類推,將每張牌經過的索引都記下來,因為每張牌最后是幾是知道的,然后反推出1~N張牌是多少,但是發現記錄牌經過的索引有點麻煩,效率也不高,記錄的數組的第一個元素即為所求。

早上到了公司一邊干活,一邊實現這個算法,從題目可以很容易看出奇數位一次為1~N/2,剩下的就是求偶數位的值,馬上寫了個算法,運行是發現有的結果是正確的,大部分是錯的,於是寫了個測試方法,測試方法就很簡單了,這個方法只用模擬翻牌的過程,然后輸出的結果為1~N就是正確的,否則就是錯誤的。經測試發現我的算法思路完全是錯誤的,但是通過這個測試算法,我發現了能夠正確實現這個題目的方法。這個題目其實不是一個遞歸的過程,而是一個進棧出棧的過程,奇數位進棧,偶數位出棧。我們知道最后的結果,把每張牌當做一個對象,就是進棧出棧都是以引用的方式,翻牌完成后,按順序將它們的值一次賦值為1~N,那么我們也就知道開始的牌的順序了,就這么簡單,思路就這么簡單,實現起來也就很快,於是馬上實現了一個粗糙算法,最后用一個Window Form實現了,發給了同事看看,為了讓大家能看得清楚,記錄了翻牌的過程,當然要記錄過程也是很簡單的。

代碼真的很簡單,將每張牌當做一個對象,這樣就不用記錄牌經過的過程,引用類型嗎!創建的對象的個數為N,過程也是線性的,不會有性能問題。

主要代碼如下(代碼很粗糙,但思路簡單清晰,我們知道就是對的),源碼下載

 //將牌定義成對象
        public class Card
        { 
            public int Value=0 ;
            public override string ToString()
            {
                return Value.ToString();
            } 
        }

        //測試算法,記錄了翻牌過程
        static List<string> TestResult(Card[] arr)
        {
            if (arr == null)
            { 
               throw new Exception("參數異常");
            }
            int len = arr.Length;
            Queue<Card> queue = new Queue<Card>(len);
            foreach (Card i in arr)
            {
                queue.Enqueue(i);
            }
            List<string> list = new List<string>(len);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < len; i++)
            {
                list.Add(GetItem(sb.ToString(),queue));
                Card cur = queue.Dequeue();
                queue.Enqueue(cur);
                sb.Append(queue.Dequeue().ToString().PadRight(3,' ')+"  ");
            }
            return list;
        }

        static string GetItem(string s,Queue<Card> queue)
        { 
            StringBuilder sb = new StringBuilder(s);
            foreach (var item in queue)
            {
                sb.Append(item.ToString().PadRight(3, ' ') + "  ");
            }
            return sb.ToString();
        }

        //實現翻牌的算法
        static Card[] TestArr(int size)
        {
            Card[] arr = new Card[size];
            for (int i = 0; i < size; i++)
            {
                arr[i] = new Card();
            }
            int len = arr.Length;
            Queue<Card> queue = new Queue<Card>(len);
            foreach (Card i in arr)
            {
                queue.Enqueue(i);
            }
            for (int i = 1; i <= len; i++)
            {
                Card cur = queue.Dequeue();
                queue.Enqueue(cur);
                cur = queue.Dequeue();
                cur.Value = i;
            }
            return arr;
        }

幾個截圖,如果題目我說得不清楚,下面幾張圖應該可以讓大家看得更明白

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM