[TDD磕算法] 排排隊(duì)棚菊,吃果果(一)失敗嘗試

本文是[TDD磕算法] 我為什么嘗試用TDD解算法題系列的一篇。

題目

原諒我標(biāo)題黨了扳肛,其實(shí)是這次解的題目很難一言兩語概括出來傻挂。這里我嘗試解釋解釋。

假定一隊(duì)人排成一列
每個(gè)人向前看挖息,如果看到前面有1個(gè)比自己高金拒、或者和自己同樣高的人。就舉個(gè)牌子報(bào)數(shù)1。同理如果有3個(gè)绪抛,就報(bào)3资铡。
如果把每個(gè)人的身高和報(bào)數(shù)列出來,就形成一個(gè)二元組的隊(duì)列幢码,比如:
[(172, 0), (160, 1), (182, 0), (170, 2)]
就表示第一個(gè)人172cm笤休,前面沒有高于或等于他高度的人;第二個(gè)160cm症副,前面有一個(gè)店雅;……

這道題目是,給定打亂順序的一個(gè)序列贞铣,把他們排回應(yīng)有的順序闹啦。
比如:
輸入:[ (7,2), (4,3), (8,0), (7,1), (6,0)]
輸出:[ (6,0), (8,0), (7,1), (4,3), (7,2)]

這道題可以在刷題網(wǎng)站Leetcode上看到,編號406
https://leetcode.com/problems/queue-reconstruction-by-height

初步分析

初看起來題目非常適合TDD解決辕坝。排序的邏輯似乎有點(diǎn)復(fù)雜窍奋,但是應(yīng)該比較容易一步步從簡單到復(fù)雜演進(jìn)。比如先從最簡單的情況開始酱畅,隊(duì)列里只有一個(gè)人琳袄,可以直接返回。兩個(gè)人的話有兩種情況……
另外這道題目不禁讓我想起了Combined Number這道Kata纺酸。同樣也是實(shí)現(xiàn)排序窖逗,排序的邏輯有些繞。最終可以很漂亮的把問題的幾個(gè)方面分離開解決掉吁峻。

關(guān)于Combined Number滑负,可以在這里看到更詳細(xì)的練習(xí)和討論。
http://cyber-dojo.org/kata/edit/FCDDF1?avatar=zebra
https://groups.google.com/forum/#!topic/agileshanghai/d6cy6tCXHA8

失敗嘗試

大致想想之后就開始信心滿滿的TDD起來了用含,果不其然失敗了……
全過程見 http://cyber-dojo.org/review/show/8922CE128B?avatar=ray&was_tag=1&now_tag=1
大體歷程如下

第一個(gè)測試

只有一個(gè)人的話矮慕,必然前面沒有更高的人。

reconstruct_should_be([[3,0]], [[3,0]]);

reconstruct_should_be的第一個(gè)參數(shù)是輸入啄骇,第二個(gè)是預(yù)期的輸出痴鳄。

實(shí)現(xiàn)代碼,輸入什么就返回什么缸夹。

function reconstrutQueue(people) {
  return people;
}
第二個(gè)測試

兩個(gè)人痪寻,有一個(gè)前面有更高的人,另一個(gè)沒有虽惭。

reconstruct_should_be([[2,1],[3,0]], [[3,0],[2,1]]);

實(shí)現(xiàn)代碼橡类,按前面人數(shù)排序。

function reconstructQueue(people) {
  return people.sort( (p1, p2) => p1[1] -p2[1]);
}

稍作重構(gòu)芽唇,抽取常量

return people.sort( (p1, p2) => p1[PRE] -p2[PRE]);
第三個(gè)測試

兩個(gè)人顾画,前面都沒有更高的人取劫。也就是說是低的在前。

reconstruct_should_be([[2,0],[3,0]], [[2,0],[3,0]]);

直接通過了研侣。

第四個(gè)測試

三個(gè)人谱邪,從低到高排。

reconstruct_should_be([[2,0],[3,0],[1,0]], [[1,0],[2,0],[3,0]]);

實(shí)現(xiàn)代碼庶诡,如果前面人數(shù)一樣惦银,按高度排序。

  return people.sort( (p1, p2) => {
    if (p1[PRE] === p2[PRE]) return p1[0] - p2[0]
    return p1[PRE] -p2[PRE]
  });
第五個(gè)測試

三個(gè)人末誓,中等高度的排在最高的后面扯俱。

reconstruct_should_be([[2,1],[3,0],[1,0]], [[1,0],[3,0],[2,1]]);

又一次直接通過了。

第六個(gè)測試

三個(gè)人基显,最矮的排在中間蘸吓。

reconstruct_should_be([[2,0],[3,0],[1,1]], [[2,0],[1,1],[3,0]]);

實(shí)現(xiàn)代碼善炫,排序方式改為先按高度排列撩幽,相同高度的按前面人數(shù)排列。這樣三個(gè)人就會(huì)排成[1,1],[2,0],[3,0]箩艺。

  let result = people.sort( (p1, p2) => {
    if (p1[VAL] === p2[VAL]) { 
      return p1[PRE] - p2[PRE];
    }
    return p1[VAL] - p2[VAL];
  });

在排序之后增加一個(gè)調(diào)整順序的過程窜醉。硬編碼快速通過,把前面有1個(gè)人的項(xiàng)目向后挪一位艺谆。也就是[1,1]挪到[2,0]后面榨惰。

  for (let i=0; i<result.length; i++) {
    if (result[i][PRE] === 1) {
      let temp = result[i];
      result[i] = result[i+1];
      result[i+1] = temp;
      i++;
    }
  }
第七個(gè)測試

三個(gè)人,最矮的排在最后静汤。

reconstruct_should_be([[2,0],[3,0],[1,2]], [[2,0],[3,0],[1,2]]);

實(shí)現(xiàn)代碼琅催,增加第二個(gè)硬編碼分支,處理有2個(gè)人在前面的情況虫给。

  for (let i=0; i<result.length; i++) {
    if (result[i][PRE] === 1) {
    ...
    }
    if (result[i][PRE] === 2) {
      let temp = result.splice(i,1)[0];
      result.splice(i+2,0,temp);
      i+=2;
    }
  }

重構(gòu)藤抡,把兩個(gè)分支改寫成相同的邏輯。

    if (result[i][PRE] === 1) {
      let temp = result.splice(i,1)[0];
      result.splice(i+1,0,temp);
      i+=1;
    }
    if (result[i][PRE] === 2) {
      let temp = result.splice(i,1)[0];
      result.splice(i+2,0,temp);
      i+=2;
    }

重構(gòu)抹估,統(tǒng)一為一個(gè)分支缠黍。

    if (sortedByVal[i][PRE] > 0) {
      let nLater = sortedByVal[i][PRE];
      let temp = sortedByVal.splice(i,1)[0];
      sortedByVal.splice(i+nLater,0,temp);
      i+=nLater;
    }
第八個(gè)測試

最高的人在前面,后面的兩個(gè)按高度排药蜻。

reconstruct_should_be([[2,1],[3,0],[1,1]], [[3,0],[1,1],[2,1]]);

實(shí)現(xiàn)代碼瓷式,呃…… 就這么掉坑里了。
首先是沒有很容易的快速通過的思路语泽。
反復(fù)幾次后發(fā)現(xiàn)在排好的數(shù)組里挪動(dòng)的方式贸典,把一個(gè)人向后挪了之后,換來的人可能也需要后移踱卵。
所以決定不在原來的數(shù)組里挪動(dòng)位置廊驼,而是全部插入一個(gè)新數(shù)組。插入時(shí)比較新數(shù)組里的位置和這個(gè)人前面人的個(gè)數(shù),直到所有的人都插入新數(shù)組為止蔬充。

function reorder(sortedByVal) {
  let result = [];
  let length = sortedByVal.length;
  for (let i=0; i<length; i++) {
    for (let j=0; j<sortedByVal.length; j++) {
      if (sortedByVal[j][PRE] <= i) {
        result.push(sortedByVal[j]);
        sortedByVal.splice(j,1);
        break;
      }
    }
  }
  return result;
}

但是發(fā)現(xiàn)這時(shí)候[ [ 1, 0 ], [ 3, 0 ], [ 2, 1 ] ]這個(gè)測試又失敗了蝶俱。
經(jīng)過一段思索無法繼續(xù)后,我覺得就算通過苦思冥想把這個(gè)題解出來饥漫,也已經(jīng)和TDD無關(guān)了榨呆。就此停止了這次練習(xí)。

感想

從來沒有嘗試過仔細(xì)描述一段失敗的過程庸队。不知道你讀到這里是和感受积蜻?

  • “簡直是浪費(fèi)時(shí)間嘛”?
  • “早就知道這樣做不靠譜”彻消?
  • “看起來還有救啊竿拆,怎么就失敗了”?
  • “太菜了宾尚,讓我來”丙笋?

這里說說掉坑的心得。怎么知道自己掉坑了煌贴。

  • 最終爬不出來的點(diǎn)很容易判定御板。突然發(fā)現(xiàn)自己思考了10分鐘,還是沒有寫代碼牛郑。
  • 顧此失彼怠肋,寫了一段代碼快速通過當(dāng)前測試,結(jié)果前面的測試掛了淹朋。
  • 感覺前面重構(gòu)出的結(jié)構(gòu)在阻礙新的用例快速通過笙各。
  • 一個(gè)早期征兆是想不清楚下一個(gè)測試是什么?比如這個(gè)過程中兩次直接通過的測試础芍,都代表了對當(dāng)前程序的行為沒有理解清楚杈抢。
  • 測試想不清楚的背后是沒有想出逐步分解問題的思路,進(jìn)而加入測試的順序可能不是適宜代碼演進(jìn)者甲。導(dǎo)致重構(gòu)時(shí)對實(shí)現(xiàn)進(jìn)行重大調(diào)整春感。比如第六步和最后一步。

下一篇文章會(huì)總結(jié)我在反思之后重新解決的過程虏缸,敬請期待鲫懒。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市刽辙,隨后出現(xiàn)的幾起案子窥岩,更是在濱河造成了極大的恐慌,老刑警劉巖宰缤,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件颂翼,死亡現(xiàn)場離奇詭異晃洒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)朦乏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門球及,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人呻疹,你說我怎么就攤上這事吃引。” “怎么了刽锤?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵镊尺,是天一觀的道長。 經(jīng)常有香客問我并思,道長庐氮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任宋彼,我火速辦了婚禮弄砍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宙暇。我一直安慰自己输枯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布占贫。 她就那樣靜靜地躺著,像睡著了一般先口。 火紅的嫁衣襯著肌膚如雪型奥。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天碉京,我揣著相機(jī)與錄音厢汹,去河邊找鬼。 笑死谐宙,一個(gè)胖子當(dāng)著我的面吹牛烫葬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凡蜻,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼搭综,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了划栓?” 一聲冷哼從身側(cè)響起兑巾,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎忠荞,沒想到半個(gè)月后蒋歌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帅掘,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年堂油,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了修档。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡府框,死狀恐怖萍悴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寓免,我是刑警寧澤癣诱,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站袜香,受9級特大地震影響撕予,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蜈首,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一实抡、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧欢策,春花似錦吆寨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至俺孙,卻和暖如春辣卒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背睛榄。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工荣茫, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人场靴。 一個(gè)月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓啡莉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親旨剥。 傳聞我的和親對象是個(gè)殘疾皇子咧欣,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容