每日一題:leetcode341.扁平化嵌套列表迭代器

題目描述

在這里插入圖片描述

題目分析

這個題目自己大概花了一個小時,雖然是一遍AC,但是速度有點慢,太長時間不寫代碼導致自己對代碼不太敏感,寫起來慢騰騰的。

看到這個的想法就是,要用棧來保存列表的迭代器,這樣將孩子列表遍歷完成后才能重新回到父親列表中,如果棧為空當然就結束了。

首先是對函數調用的分析,會首先調用hasNext再調用next,這就要求我們如果把后移操作放到hasNext中,那么棧頂就要指向當前位置之前,對于迭代器來說不容易操作,因此我們決定將后移操作放到next函數中

首要要定義棧中保存的迭代器的含義:我的想法是該層列表當前指向元素,這應該是比較符合直覺的,然而就是這種符合直覺的做法導致我的代碼要復雜許多。因為這種定義其實是不統一的,棧頂的迭代器是直接可以訪問元素的,而下面的迭代器因為保存的是當前元素的位置,所以棧頂訪問結束后彈出以后是不能直接訪問元素的,必須要執行一次后移操作,這種對棧中元素處理的不統一性導致代碼變得復雜:一旦彈出必須要執行一次后移操作。可能這里還不明白為什么變復雜了,我再梳理一下需要進行的步驟:
首先,我們必須在hasNext函數中判斷,當前棧頂迭代器需不需要彈出,如果不需要,就要判斷當前棧頂迭代器指向的是不是一個整數,如果是,返回真即可,如果不是,需要將子列表的迭代器壓棧進行訪問,可是子列表有可能為空,因此不能直接訪問子列表,同樣要對新壓入棧的迭代器判斷需不需要彈出。如果不需要彈出就是正常操作:要么繼續壓棧,要么返回。但是如果需要彈出的話,還需要對父親迭代器進行后移操作,后移以后首先要判斷的同樣是需不需要彈出。

可以總結一下:

  • 每次后移操作后首先要做的是判斷是否需要彈出
  • 每次入棧操作以后要判斷是否需要彈出

如果我們直接按照上面的順序進行操作,將會寫出比較復雜的代碼(我就是這樣),可以看到彈出操作比較頻繁,我們可以將其剝離成一個函數

實現代碼

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() {//檢查列表是否為空,應該在每次執行++操作后進行檢查//如果為空返回真if (S.empty()) return true;while(S.top().first == S.top().second) {//如果棧頂部的迭代器已經失效,則將其彈出S.pop();if (S.empty()) return true;++S.top().first;}return false;}bool move_to_integer() {//將迭代器指向整數,如果不是整數,則將當前迭代器壓入棧//返回假表示后面沒有整數,只有空列表while( !( S.top().first -> isInteger()) ) {auto &tp = S.top();//如果當前迭代器指向的不是整數,則得到其指向的列表const auto &lst = tp.first -> getList();if (lst.empty()) {//如果指向的列表為空,則不用處理該列表,直接跳過++tp.first;if (is_empty()) {//如果后面沒有有效元素,直接返回假return false;}} else {//如果列表不為空,則將列表的迭代器壓棧S.emplace(lst.cbegin(), lst.cend());}}return true;}
};

寫代碼遇到一個小問題就是我將聲明語句放到了表達式中,但是總報錯,經過測試發現聲明語句不能寫在表達式中

AC之后我又去看了一下官方題解,發現思路大同小異,但是發現人家的代碼十分簡潔,主要的區別在于hasNext函數中題解通過將上面幾個函數放到同一個循環里面,通過安排代碼的順序來完成任務:

  • 先判斷是否需要彈出,如果需要彈出則彈出后直接continue判斷是否非空,然后再次執行循環,這時候就體現出設計的重要性了,題解中棧保存的是下次應該訪問的迭代器地址,這就要求父列表迭代器在壓入子列表迭代器后同時向后移動一位,因此彈出棧頂迭代器后就不需要再次向后移動了。如果不這樣做,彈出后需要先判斷棧是否為空,然后再后移,然后再判斷是否需要彈出,將導致代碼復雜
  • 彈出后判斷是否是一個整數,如果是直接返回true,如果不是則不斷壓棧,直到是一個整數。如果是一個空列表,則重新執行循環以后會彈出,因此不需要做多余的處理。

題解代碼

class NestedIterator {
private:// pair 中存儲的是列表的當前遍歷位置,以及一個尾后迭代器用于判斷是否遍歷到了列表末尾stack<pair<vector<NestedInteger>::iterator, vector<NestedInteger>::iterator>> stk;public:NestedIterator(vector<NestedInteger> &nestedList) {stk.emplace(nestedList.begin(), nestedList.end());}int next() {// 由于保證調用 next 之前會調用 hasNext,直接返回棧頂列表的當前元素,然后迭代器指向下一個元素return stk.top().first++->getInteger();}bool hasNext() {while (!stk.empty()) {auto &p = stk.top();if (p.first == p.second) { // 遍歷到當前列表末尾,出棧stk.pop();continue;}if (p.first->isInteger()) {return true;}// 若當前元素為列表,則將其入棧,且迭代器指向下一個元素auto &lst = p.first++->getList();stk.emplace(lst.begin(), lst.end());}return false;}
};

只能感嘆題解代碼的巧妙,自己距離這樣的水平還很遠,要多寫多思考。這種模擬題感覺對代碼能力還是挺有幫助的,因為工程中代碼更多是這樣的,注重的是代碼的設計效率、是否優雅,而不是算法是否巧妙

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383561.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383561.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383561.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

每日一題:leetcode82. 刪除排序鏈表中的重復元素 II

題目描述 題目分析 這才是正常的中等題難度嘛&#xff0c;昨天的中等題題解我半天看不懂。。。 首先&#xff0c;需要增加一個啞節點&#xff08;操作鏈表的常規操作&#xff09;&#xff0c;因為有可能刪除首節點&#xff0c;我們不想要為首節點添加單獨的邏輯。其次&#xf…

每日一題:leetcode456.132模式

題目描述 題目分析 我覺得這道題應該是我做過最難的中等題之一了&#xff0c;這是昨天的每日一題&#xff0c;但是昨天用nlogn的做法做出來以后在看題解&#xff0c;發現有些看不懂&#xff08;覺得題解有點故弄玄虛&#xff09;。然后今天中午又花了一點時間才搞懂&#xff0…

leetcode283.移動零

題目描述 題目分析 在寫簡單題放松&#xff0c;看到這道題第一個想法是用STL庫函數&#xff0c;雖然知道大概要用雙指針之類的&#xff0c;但是庫函數爽哇。 class Solution { public:void moveZeroes(vector<int>& nums) {stable_sort(nums.begin(), nums.end(), …

每日一題:leetcode61.旋轉鏈表

題目描述 題目分析 很容易發現&#xff0c;如果k是n的整數倍&#xff0c;相當于沒有移動。這樣直接對k%n使得k在一個可以接受的范圍。 因為是順序移動&#xff0c;各元素之間的相對位置保持不變&#xff0c;所以就想著將鏈表先變成一個環。然后再移動頭指針&#xff0c;最后再…

每日一題:leetcode173.二叉搜索樹迭代器

題目描述 題目分析 更加地覺得編程重要的不在于如何寫代碼&#xff0c;用什么具體的技巧&#xff0c;編碼本身只是一種將思維呈現的方式&#xff0c;但是如果思維是不清晰的&#xff0c;那么就算懂得再多的編碼的奇技淫巧也是沒有什么幫助的。相反&#xff0c;如果有一個清晰的…

Ubuntu20.04 Clion/Pycharm/IDEA 輸入中文+光標跟隨解決方案

ibus輸入法&#xff08;棄用&#xff09; 之前一直用的搜狗輸入法&#xff0c;但是搜狗輸入法無法在Jetbrains全家桶下使用&#xff0c;但是又需要輸入中文&#xff0c;沒有辦法我只能下載了谷歌輸入法&#xff0c;十分難用&#xff0c;但是也沒有其他辦法&#xff0c;經常到網…

leetcode11.盛最多水的容器

題目描述 題目分析 看到題目后第一個想法當然是O(n2)O(n^2)O(n2)的&#xff0c;但是數據范圍是3e4&#xff0c;應該會超時&#xff0c;而且這種數據范圍也不是讓暴力求解的 。 相當于求解∑i<jmax((j?i)?min(a[i],a[j]))\sum_{i<j}{max((j-i)*min(a[i],a[j]))}∑i<…

每日一題:leetcode190.顛倒二進制位

題目描述 題目分析 題目本身很簡單&#xff0c;沒覺得有什么技巧可以再進行優化了&#xff0c;覺得位運算是無法打亂相對順序的&#xff0c;而這里需要進行鏡像顛倒的操作。因此就踏實地寫了一個循環。 在使用位運算得到每一位的時候&#xff0c;我吸取了經驗&#xff0c;用一…

結構屈曲分析

結構屈曲分析主要用于判定結構受載后是否有失穩風險&#xff0c;作為工程應用&#xff0c;一般分為線性屈曲分析和非線性屈曲分析。 線性屈曲分析需要具備較多的前提條件&#xff0c;如載荷無偏心、材料無缺陷等&#xff0c;在實際工程應用中結構制作過程和加載方式很難達到線性…

每日一題:leetcode74.搜索二維矩陣

題目描述 題目分析 感覺這是一個放錯標簽的簡單題。題目非常簡單&#xff0c;思路應該很明確是二分&#xff0c;我很快寫了一個&#xff08;雖然不小心把!打成調試了一會&#xff09;。 class Solution { public:bool searchMatrix(vector<vector<int>>& mat…

每日一題:leetcode90.子集貳

題目描述 題目分析 感覺這道題讓自己對枚舉排列有了一個更好的認識&#xff0c;感覺自己的這種思路不錯。 假設沒有重復元素&#xff08;退化成78.子集&#xff09;&#xff0c;我們應該怎么做&#xff1f;初始的時候冪集中只有一個空集&#xff0c;然后對每個元素&#xff0…

每日一題:leetcode1006.笨階乘

題目描述 題目分析 因為順序一定且沒有括號&#xff0c;所以邏輯很簡單。我們要順序處理的矛盾在于&#xff0c;減號后面會再出現乘法和除法&#xff0c;我們不妨將對乘法和除法用一個臨時值進行計算&#xff0c;計算結束后再合并到值里面&#xff0c;一般來講乘法和除法的處理…

每日一題:leetcode80.刪除有序數組中的重復元素貳

題目描述 題目分析 又是一道貼錯標簽的簡單題&#xff0c;很明顯的雙指針&#xff0c;我的做法是用兩個變量保存是否需要記錄&#xff0c;官方題解的做法是直接判斷&#xff0c;人家的高明一些 class Solution { public:int removeDuplicates(vector<int>& nums) {…

每日一題:leetcode81.搜索旋轉排序數組Ⅱ

題目描述 題目分析 不含重復元素的題解&#xff08;leetcode33&#xff09; 這道題也是我們算法課的一道編程題&#xff0c;寫完以后發現當時的思路和現在沒有什么變化&#xff0c;果然是自己啊。我的想法是先判斷區間整體是升序的還是旋轉的&#xff0c;如果是升序的就按照正…

C++ JSON庫:JSON for Morden C++

緒論 最近因為項目的需要&#xff0c;需要對JSON進行一定的數據處理&#xff0c;因為想要用C進行編碼&#xff0c;便對C的JSON庫進行的調研&#xff0c;發現這個庫比較好用&#xff1a;JSON for Morder C。 使用指南 想要使用這個json庫&#xff0c;只需要在源文件中包含jso…

Linux信號實現精確到微秒的sleep函數:通過sigsuspend函數解決時序競態問題

原理就是先使用定時器定時&#xff0c;然后再使用pause函數或者sigsuspend函數主動阻塞掛起&#xff0c;最終恢復現場。 如果使用pause函數的話&#xff0c;優點是使用簡單&#xff0c;缺點是有可能產生時序競態&#xff0c;導致進程一直阻塞下去&#xff1a;在定時和掛起之間…

Linux創建多個子進程并通過捕獲SIGCHLD信號進行非阻塞回收

我們通過fork函數創建多個子進程&#xff0c;并通過exec函數族在子進程中進行其他的工作&#xff0c;但是為了避免僵尸進程&#xff0c;我們要對子進程進行回收。常用的回收方式是wait或者waitpid進行阻塞回收&#xff0c;因為如果非阻塞回收很難把握時機&#xff0c;而阻塞回收…

Linux創建守護進程

守護進程&#xff08;Daemon&#xff09;是運行在后臺的一種特殊進程。它獨立于控制終端并且周期性地執行某種任務或等待處理某些發生的事件。它不需要用戶輸入就能運行而且提供某種服務&#xff0c;不是對整個系統就是對某個用戶程序提供服務。Linux系統的大多數服務器就是通過…

Linux創建多個子線程并回收

創建子線程的邏輯相比子進程要更容易理解一些&#xff0c;因為線程沒有像進程那樣復制很多東西另起爐灶&#xff0c;子線程從傳入的開始函數開始運行&#xff0c;但是難點在于傳入參數和回收時獲取退出狀態&#xff0c;因為這兩個原本都是void *類型的&#xff0c;而我們在使用…

Qt發布程序

Windows&#xff1a; https://www.cnblogs.com/linuxAndMcu/p/10974927.html Ubuntu&#xff1a; https://blog.csdn.net/u014779536/article/details/107854060