《程式設計之美》1.3一摞烙餅的排序
阿新 • • 發佈:2019-02-10
執行結果如下圖所示:#include<iostream> #include<assert.h> using namespace std; /****************/ // //烙餅排序的實現 // /****************/ class CPrefixSorting { private: int* m_CakeArray; //烙餅資訊陣列 int m_nCakeCnt; //烙餅個數 int m_nMaxSwap; //最多交換次數。根據前面的推斷,這裡最多為(m_nCakeCnt-2)*2+1 int* m_SwapArray; //交換結果陣列 int* m_ReverseCakeArray; //當前翻轉烙餅資訊陣列 int* m_ReverseCakeArraySwap; //當前翻轉烙餅交換結果陣列 int m_nSearch; //當前搜尋次數資訊 public: CPrefixSorting(){ //建構函式 m_nCakeCnt = 0; m_nMaxSwap = 0; } ~CPrefixSorting() { //解構函式 if (m_CakeArray != NULL) { delete m_CakeArray; } if (m_SwapArray != NULL) { delete m_SwapArray; } if (m_ReverseCakeArray != NULL) { delete m_ReverseCakeArray; } if (m_ReverseCakeArraySwap != NULL) { delete m_ReverseCakeArraySwap; } } // //計算烙餅翻轉資訊 //@param //pCakeArray 儲存烙餅索引陣列 //nCakeCnt 烙餅個數 // void Run(int* pCakeArray, int nCakeCnt) { Init(pCakeArray, nCakeCnt); m_nSearch = 0; Search(0); } // //輸出烙餅翻轉次數 // void Output() { for (int i = 0; i < m_nMaxSwap; i++) { cout << m_SwapArray[i] << " "; } cout << endl << " |Search Times| : " << m_nSearch << endl; cout << "Total Swap Times = " << m_nMaxSwap << endl; } private: // //初始化陣列資訊 //@param //pCakeArray 儲存烙餅索引陣列 //nCakeCnt // void Init(int* pCakeArray, int nCakeCnt) { assert(pCakeArray != NULL); assert(nCakeCnt > 0); m_nCakeCnt = nCakeCnt; //初始化烙餅陣列 m_CakeArray = new int[m_nCakeCnt]; assert(m_CakeArray != NULL); for (int i = 0; i < m_nCakeCnt; i++) { m_CakeArray[i] = pCakeArray[i]; } //設定最多交換次數資訊 m_nMaxSwap = UpBound(m_nCakeCnt); //初始化交換結果陣列 m_SwapArray = new int[m_nMaxSwap + 1]; assert(m_SwapArray!=NULL); //初始化中間交換結果資訊 m_ReverseCakeArray = new int[m_nCakeCnt]; for (int i = 0; i < m_nCakeCnt; i++) { m_ReverseCakeArray[i] = m_CakeArray[i]; } m_ReverseCakeArraySwap = new int[m_nMaxSwap]; } // //尋找當前翻轉的上界 // int UpBound(int nCakeCnt) { return (nCakeCnt - 2) * 2 + 1;//原先return (nCakeCnt-1)*2也可以, //程式碼這麼寫也沒問題,只不過不是最優解而已 } // //尋找當前翻轉的下界 // int LowerBound(int* pCakeArray, int nCakeCnt) { int t, ret = 0; //根據當前陣列排序資訊情況判斷至少需要交換多少次 for (int i = 1; i < nCakeCnt; i++) { //判斷位置相鄰的兩個烙餅,是否為尺寸排序上相鄰的 //此處應該考慮順序問題,若烙餅的次序從上往下數是從大到小的,即t==-1 //翻轉次數應該是1而非0,即要整個翻轉一次。 t = pCakeArray[i] - pCakeArray[i - 1]; if ((t == 1) || (t == -1)) { } else { ret++; } } //判斷下界時,如果最大的烙餅不在最後一個位置,則要多翻轉一次(包含了t==-1的情況) //能有效減少無效搜尋次數,雖然還是會包含無效搜尋。。 if (pCakeArray[nCakeCnt - 1] != nCakeCnt - 1) ret++; return ret; } //排序的主函式 void Search(int step) { int i, nEstimate; m_nSearch++; //估算這次搜尋所需要的最小交換次數nEstimate nEstimate = LowerBound(m_ReverseCakeArray, m_nCakeCnt); //根節點(最原始陣列)呼叫search時step是0,第一層子節點step是1, //第二層子節點step是2,以此類推可知step是從0開始計數的。因為nElimate可能為0, //所以當step等於m_nMaxSwap時候,會造成下面的m_reverseCakeArraySwap[step]=i; //的陣列越界。所以判斷條件應改為>= if (step + nEstimate >= m_nMaxSwap) return; //如果已經排序好,即翻轉完成,輸出結果 if (IsSorted(m_ReverseCakeArray, m_nCakeCnt)) { if (step < m_nMaxSwap) { m_nMaxSwap = step; for (i = 0; i < m_nMaxSwap; i++) m_SwapArray[i] = m_ReverseCakeArraySwap[i]; } return; } //遞迴進行翻轉 for (i = 1; i < m_nCakeCnt; i++) { Revert(0, i); m_ReverseCakeArraySwap[step] = i; Search(step + 1); Revert(0, i); } } // //true:已經排好序 //false:未排序 // bool IsSorted(int* pCakeArray, int nCakeCnt) { //若陣列內容從小到大有序,返回true for (int i = 1; i < nCakeCnt; i++) { if (pCakeArray[i - 1] > pCakeArray[i]) { return false; } } return true; } // //翻轉烙餅資訊 // void Revert(int nBegin, int nEnd) { //把陣列中給定兩個係數之間的內容反序之~ assert(nEnd > nBegin); int i, j, t; //翻轉烙餅資訊 for (i = nBegin, j = nEnd; i < j; i++, j--) { t = m_ReverseCakeArray[i]; m_ReverseCakeArray[i] = m_ReverseCakeArray[j]; m_ReverseCakeArray[j] = t; } } }; //主函式,供測試 int main() { int Cake[10] = {3,2,1,6,5,4,9,8,7,0}; CPrefixSorting TestA; TestA.Run(Cake, 10); TestA.Output(); return 0; }