每日一题:leetcode341.扁平化嵌套列表迭代器
題目描述
題目分析
這個(gè)題目自己大概花了一個(gè)小時(shí),雖然是一遍AC,但是速度有點(diǎn)慢,太長(zhǎng)時(shí)間不寫代碼導(dǎo)致自己對(duì)代碼不太敏感,寫起來(lái)慢騰騰的。
看到這個(gè)的想法就是,要用棧來(lái)保存列表的迭代器,這樣將孩子列表遍歷完成后才能重新回到父親列表中,如果棧為空當(dāng)然就結(jié)束了。
首先是對(duì)函數(shù)調(diào)用的分析,會(huì)首先調(diào)用hasNext再調(diào)用next,這就要求我們?nèi)绻押笠撇僮鞣诺絟asNext中,那么棧頂就要指向當(dāng)前位置之前,對(duì)于迭代器來(lái)說(shuō)不容易操作,因此我們決定將后移操作放到next函數(shù)中
首要要定義棧中保存的迭代器的含義:我的想法是該層列表當(dāng)前指向元素,這應(yīng)該是比較符合直覺(jué)的,然而就是這種符合直覺(jué)的做法導(dǎo)致我的代碼要復(fù)雜許多。因?yàn)檫@種定義其實(shí)是不統(tǒng)一的,棧頂?shù)牡魇侵苯涌梢栽L問(wèn)元素的,而下面的迭代器因?yàn)楸4娴氖钱?dāng)前元素的位置,所以棧頂訪問(wèn)結(jié)束后彈出以后是不能直接訪問(wèn)元素的,必須要執(zhí)行一次后移操作,這種對(duì)棧中元素處理的不統(tǒng)一性導(dǎo)致代碼變得復(fù)雜:一旦彈出必須要執(zhí)行一次后移操作。可能這里還不明白為什么變復(fù)雜了,我再梳理一下需要進(jìn)行的步驟:
首先,我們必須在hasNext函數(shù)中判斷,當(dāng)前棧頂?shù)餍璨恍枰獜棾?#xff0c;如果不需要,就要判斷當(dāng)前棧頂?shù)髦赶虻氖遣皇且粋€(gè)整數(shù),如果是,返回真即可,如果不是,需要將子列表的迭代器壓棧進(jìn)行訪問(wèn),可是子列表有可能為空,因此不能直接訪問(wèn)子列表,同樣要對(duì)新壓入棧的迭代器判斷需不需要彈出。如果不需要彈出就是正常操作:要么繼續(xù)壓棧,要么返回。但是如果需要彈出的話,還需要對(duì)父親迭代器進(jìn)行后移操作,后移以后首先要判斷的同樣是需不需要彈出。
可以總結(jié)一下:
- 每次后移操作后首先要做的是判斷是否需要彈出
- 每次入棧操作以后要判斷是否需要彈出
如果我們直接按照上面的順序進(jìn)行操作,將會(huì)寫出比較復(fù)雜的代碼(我就是這樣),可以看到彈出操作比較頻繁,我們可以將其剝離成一個(gè)函數(shù)
實(shí)現(xiàn)代碼
class NestedIterator { public:NestedIterator(vector<NestedInteger> &nestedList) {S.emplace(nestedList.cbegin(), nestedList.cend());}int next() {return (S.top().first++)->getInteger();}bool hasNext() {return !is_empty() && move_to_integer();} private:using vec_iter = vector<NestedInteger>::const_iterator;stack<pair<vec_iter, vec_iter>> S;bool is_empty() {//檢查列表是否為空,應(yīng)該在每次執(zhí)行++操作后進(jìn)行檢查//如果為空返回真if (S.empty()) return true;while(S.top().first == S.top().second) {//如果棧頂部的迭代器已經(jīng)失效,則將其彈出S.pop();if (S.empty()) return true;++S.top().first;}return false;}bool move_to_integer() {//將迭代器指向整數(shù),如果不是整數(shù),則將當(dāng)前迭代器壓入棧//返回假表示后面沒(méi)有整數(shù),只有空列表while( !( S.top().first -> isInteger()) ) {auto &tp = S.top();//如果當(dāng)前迭代器指向的不是整數(shù),則得到其指向的列表const auto &lst = tp.first -> getList();if (lst.empty()) {//如果指向的列表為空,則不用處理該列表,直接跳過(guò)++tp.first;if (is_empty()) {//如果后面沒(méi)有有效元素,直接返回假return false;}} else {//如果列表不為空,則將列表的迭代器壓棧S.emplace(lst.cbegin(), lst.cend());}}return true;} };寫代碼遇到一個(gè)小問(wèn)題就是我將聲明語(yǔ)句放到了表達(dá)式中,但是總報(bào)錯(cuò),經(jīng)過(guò)測(cè)試發(fā)現(xiàn)聲明語(yǔ)句不能寫在表達(dá)式中。
AC之后我又去看了一下官方題解,發(fā)現(xiàn)思路大同小異,但是發(fā)現(xiàn)人家的代碼十分簡(jiǎn)潔,主要的區(qū)別在于hasNext函數(shù)中題解通過(guò)將上面幾個(gè)函數(shù)放到同一個(gè)循環(huán)里面,通過(guò)安排代碼的順序來(lái)完成任務(wù):
- 先判斷是否需要彈出,如果需要彈出則彈出后直接continue判斷是否非空,然后再次執(zhí)行循環(huán),這時(shí)候就體現(xiàn)出設(shè)計(jì)的重要性了,題解中棧保存的是下次應(yīng)該訪問(wèn)的迭代器地址,這就要求父列表迭代器在壓入子列表迭代器后同時(shí)向后移動(dòng)一位,因此彈出棧頂?shù)骱缶筒恍枰俅蜗蚝笠苿?dòng)了。如果不這樣做,彈出后需要先判斷棧是否為空,然后再后移,然后再判斷是否需要彈出,將導(dǎo)致代碼復(fù)雜
- 彈出后判斷是否是一個(gè)整數(shù),如果是直接返回true,如果不是則不斷壓棧,直到是一個(gè)整數(shù)。如果是一個(gè)空列表,則重新執(zhí)行循環(huán)以后會(huì)彈出,因此不需要做多余的處理。
題解代碼
class NestedIterator { private:// pair 中存儲(chǔ)的是列表的當(dāng)前遍歷位置,以及一個(gè)尾后迭代器用于判斷是否遍歷到了列表末尾stack<pair<vector<NestedInteger>::iterator, vector<NestedInteger>::iterator>> stk;public:NestedIterator(vector<NestedInteger> &nestedList) {stk.emplace(nestedList.begin(), nestedList.end());}int next() {// 由于保證調(diào)用 next 之前會(huì)調(diào)用 hasNext,直接返回棧頂列表的當(dāng)前元素,然后迭代器指向下一個(gè)元素return stk.top().first++->getInteger();}bool hasNext() {while (!stk.empty()) {auto &p = stk.top();if (p.first == p.second) { // 遍歷到當(dāng)前列表末尾,出棧stk.pop();continue;}if (p.first->isInteger()) {return true;}// 若當(dāng)前元素為列表,則將其入棧,且迭代器指向下一個(gè)元素auto &lst = p.first++->getList();stk.emplace(lst.begin(), lst.end());}return false;} };只能感嘆題解代碼的巧妙,自己距離這樣的水平還很遠(yuǎn),要多寫多思考。這種模擬題感覺(jué)對(duì)代碼能力還是挺有幫助的,因?yàn)楣こ讨写a更多是這樣的,注重的是代碼的設(shè)計(jì)效率、是否優(yōu)雅,而不是算法是否巧妙
總結(jié)
以上是生活随笔為你收集整理的每日一题:leetcode341.扁平化嵌套列表迭代器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: lol以前是不是很流行开局在下路草丛蹲人
- 下一篇: 每日一题:leetcode82. 删除排