程式設計之美 第一章 1 3 一摞烙餅的排序

2021-06-25 16:11:50 字數 3779 閱讀 6906

/*

一摞烙餅的排序:

把一摞並按照大小次序擺好,小的在上面,大的在下面。由於我乙隻手托著盤子,只好用另乙隻手,一次抓住最

上面的幾塊餅。把它們上下顛倒一下,反覆幾次,烙餅就排好序了。

請寫乙個程式,對於n塊大小不一的烙餅,輸出最優化的翻餅過程。

分析:每次我們只能選擇最上方的一摞餅,一起翻轉。不能一張一張直接抽取出來,然後進行插入,也不能交換任意兩塊

餅。因此,基本的排序方法不好用。

每次操作都是針對最上面的餅,如果最底層的餅已經排序,只需要處理上面的n-1個餅。問題變為n-2,n-3,...,

直到最上面的兩個餅排好序。

解法1:

為了把最大的烙餅放在最下面,我們走兩步:

1將最上面和最大的烙餅(包括這兩張餅)一起反轉

2將整坨烙餅全部翻轉

之後我們對上面n-1,n-2個餅重複上述過程

經過兩次反轉可以把最大的烙餅翻轉到最下面,最多需要把上面的n-1,

個烙餅一次翻轉兩次。至多需要2(n-1)次翻轉就可以把所有烙餅排好序。

因為第二小的烙餅排好的時候,最小的烙餅已經在最上面了(參見冒泡)。

改進:假如這坨烙餅中幾個不同部分相對有序,我們先把小些的烙餅反轉,比每次翻轉最大的烙餅要快。

考慮每次翻轉的時候,把原本應該相鄰的烙餅換到一起,等所有烙餅都換到一起後,就完成排序了。

每次反轉最大的烙餅實際上是:把最大的和次最大的交換到一起。

分析演算法的主要流程:

設定了烙餅組,當前組,交換組,最終交換組,個數,最大次數,搜尋次數

1原始資料賦給烙餅組,交換組,個數->最大數

2搜尋:估算最小交換次數(檢驗相鄰位置烙餅大小是否相鄰),檢驗是否超過最大次數,檢驗是否已經排好序->檢驗是否小於最大次數->交換組賦給

最終交換組

遞迴翻**從小向大翻,先逆置,在設定交換組的第幾步為第幾張餅。遞迴搜尋,再逆置(?為什麼)

輸入:10

3 2 1 6 5 4 9 8 7 0

輸出:4 8 6 8 4 9

1721266*/

/*關鍵:

1 考慮每次翻轉的時候,把原本應該相鄰的烙餅換到一起,等所有烙餅都換到一起後,就完成排序了。

每次反轉最大的烙餅實際上是:把最大的和次最大的交換到一起。

2 搜尋:估算最小交換次數(檢驗相鄰位置烙餅大小是否相鄰),檢驗是否超過最大次數,檢驗是否已經排好序->檢驗是否小於最大次數->交換組賦給

最終交換組

遞迴翻**從小向大翻,先逆置,在設定交換組的第幾步為第幾張餅。遞迴搜尋,再逆置(?為什麼)

3 int iminsearchtimes = lowerbound(_pcurcakearr,_icakesize);//對當前陣列,判定最少的交換次數。

//在到達當前烙餅狀態之前,我們已經翻轉了step次,iminsearchtimes是我們至少還需要翻轉多少次才能成功

//iminsearchtimes越大,_imaxswaptimes越小,越容易剪枝。總結:上界越小,下界越大,剪枝越多

if(istep + iminsearchtimes > _imaxswaptimes)//如果已經搜尋的次數加上最少搜尋次數仍大於最大搜尋次數

//說明不符合要求,直接退出

4  if(issorted(_pcurcakearr,_icakesize))//遞迴出口,判斷當前烙餅組是否已經排好序了,把成員變數作為形參傳遞,難道是為了防止多執行緒的髒讀?

;templatecakesort::cakesort()

//cakesort::cakesort()

templatecakesort::~cakesort()

//cakesort::~cakesort()

//if(_pcurcakearr)

// //if(_preversearr)

// //if(_pswaparr)

//}templatevoid cakesort::run(int* pcakearr,int icakesize)

//void cakesort::run(int* pcakearr,int icakesize)

templatevoid cakesort::init(int* pcakearr,int icakesize)

//void cakesort::init(int* pcakearr,int icakesize)

_imaxswaptimes = upperbound(_icakesize);

//_pswaparr = new int[_imaxswaptimes + 1];//初始化交換結果陣列,交換結果次數等於最大次數加1?

_pswaparr.reset(new int[_imaxswaptimes + 1]);

//_preversearr = new int[_imaxswaptimes];

_preversearr.reset(new int[_imaxswaptimes]);

//assert(_pswaparr && _preversearr);

}templateint cakesort::upperbound(int icakesize)//最多交換次數為2*n

//int cakesort::upperbound(int icakesize)

templatevoid cakesort::search(int istep)

//void cakesort::search(int istep)

if(issorted(_pcurcakearr,_icakesize))//遞迴出口,判斷當前烙餅組是否已經排好序了,把成員變數作為形參傳遞,難道是為了防止多執行緒的髒讀?

}return;//注意,這裡要加return,否則就多搜尋了無用功了。都已經超出最大次數了,顯然是剪枝了

} for(int i = 1 ; i < _icakesize ; i++)//遞迴主體,從小到下依次反轉 }

//int cakesort::lowerbound(int* parrcake,int icakesize)

templateint cakesort::lowerbound(t parrcake,int icakesize)

//int cakesort::lowerbound(ptrarr parrcake,int icakesize)

else

}return iret;

}//bool cakesort::issorted(int* parrcake,int icakesize)

templatebool cakesort::issorted(t parrcake,int icakesize)

//bool cakesort::issorted(ptrarr parrcake,int icakesize)

} return true;

}templatevoid cakesort::output()//輸出具體反轉次數

//void cakesort::output()

printf("\nsearch times:%d\n",_isearchtimes);

printf("swap times:%d\n",_imaxswaptimes);

}templatevoid cakesort::reverse(int ibeg,int iend)

//void cakesort::reverse(int ibeg,int iend)

}void process()

cakesortcakesort;

cakesort.run(iarr,n);

cakesort.output(); }}

int main(int argc,char* argv)

程式設計之美1 3 一摞烙餅的排序

程式設計之美 讀書筆記 1.3 一摞烙餅的排序 問題 星期五的晚上,一幫同事在希格瑪大廈附近的 硬碟酒吧 多喝了幾杯。程式設計師多喝了幾杯之後談什麼呢?自然是演算法問題。有個同事說 我以前在餐館打工,顧客經常點非常多的烙餅。店裡的餅大小不一,我習慣在到達顧客飯桌前,把一摞餅按照大小次序擺好 小的在上...

程式設計之美 一摞烙餅排序

問題 星期五的晚上,一幫同事在希格瑪大廈附近的 硬碟酒吧 多喝了幾杯。程式設計師多喝了幾杯之後談什麼呢?自然是演算法問題。有個同事說 我以前在餐館打工,顧客經常點非常多的烙餅。店裡的餅大小不一,我習慣在到達顧客飯桌前,把一摞餅按照大小次序擺好 小的在上面,大的在下面。由於我乙隻手托著盤子,只好用另乙...

程式設計之美1 3 一摞烙餅的排序(Python)

問題 星期五的晚上,一幫同事在希格瑪大廈附近的 硬碟酒吧 多喝了幾杯。程式設計師多喝了幾杯之後談什麼呢?自然是演算法問題。有個同事說 我以前在餐館打工,顧客經常點非常多的烙餅。店裡的餅大小不一,我習慣在到達顧客飯桌前,把一摞餅按照大小次序擺好 小的在上面,大的在下面。由於我乙隻手托著盤子,只好用另乙...