日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

力扣--扁平化嵌套列表迭代器

發(fā)布時(shí)間:2024/4/11 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 力扣--扁平化嵌套列表迭代器 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

扁平化嵌套列表迭代器

文章目錄

  • 扁平化嵌套列表迭代器
    • 一、題目描述
    • 二、分析
      • 方法一:
      • 代碼一:
      • 方法二:
      • 代碼二:
      • C++代碼:

一、題目描述

/*** // This is the interface that allows for creating nested lists.* // You should not implement it, or speculate about its implementation* class NestedInteger {* public:* // Return true if this NestedInteger holds a single integer, rather than a nested list.* bool isInteger() const;** // Return the single integer that this NestedInteger holds, if it holds a single integer* // The result is undefined if this NestedInteger holds a nested list* int getInteger() const;** // Return the nested list that this NestedInteger holds, if it holds a nested list* // The result is undefined if this NestedInteger holds a single integer* const vector<NestedInteger> &getList() const;* };*/class NestedIterator { public:NestedIterator(vector<NestedInteger> &nestedList) {}int next() {}bool hasNext() {} };/*** Your NestedIterator object will be instantiated and called as such:* NestedIterator i(nestedList);* while (i.hasNext()) cout << i.next();*/

二、分析

  • 首先,現(xiàn)在有一種數(shù)據(jù)結(jié)構(gòu)NestedInteger,這個(gè)結(jié)構(gòu)中存的數(shù)據(jù)可能是一個(gè)Integer整數(shù),也可能是一個(gè)NestedInteger列表
  • 注意,這個(gè)列表里面裝著的是NestedInteger,也就是說這個(gè)列表中的每一個(gè)元素可能是個(gè)整數(shù),可能又是個(gè)列表,這樣無限遞歸嵌套下去……
  • NestedInteger有如下 API:
public class NestedInteger {// 如果其中存的是一個(gè)整數(shù),則返回 true,否則返回 falsepublic boolean isInteger();// 如果其中存的是一個(gè)整數(shù),則返回這個(gè)整數(shù),否則返回 nullpublic Integer getInteger();// 如果其中存的是一個(gè)列表,則返回這個(gè)列表,否則返回 nullpublic List<NestedInteger> getList(); }
  • 我們的算法會(huì)被輸入一個(gè)NestedInteger列表,我們需要做的就是寫一個(gè)迭代器類,將這個(gè)帶有嵌套結(jié)構(gòu)NestedInteger的列表「拍平」:
public class NestedIterator implements Iterator<Integer> {// 構(gòu)造器輸入一個(gè) NestedInteger 列表public NestedIterator(List<NestedInteger> nestedList) {}// 返回下一個(gè)整數(shù)public Integer next() {}// 是否還有下一個(gè)整數(shù)?public boolean hasNext() {} }

方法一:

  • 學(xué)過設(shè)計(jì)模式的朋友應(yīng)該知道,迭代器也是設(shè)計(jì)模式的一種,目的就是為調(diào)用者屏蔽底層數(shù)據(jù)結(jié)構(gòu)的細(xì)節(jié),簡單地通過hasNext和next方法有序地進(jìn)行遍歷。
  • NestedInteger結(jié)構(gòu)實(shí)際上也是一種支持無限嵌套的結(jié)構(gòu),而且可以同時(shí)表示整數(shù)和列表兩種不同類型,我想 Notion 的核心數(shù)據(jù)結(jié)構(gòu) block 估計(jì)也是這樣的一種設(shè)計(jì)思路。
  • 那么話說回來,對(duì)于這個(gè)算法問題,我們?cè)趺唇鉀Q呢?NestedInteger結(jié)構(gòu)可以無限嵌套,怎么把這個(gè)結(jié)構(gòu)「打平」,為迭代器的調(diào)用者屏蔽底層細(xì)節(jié),扁平化地輸出所有整數(shù)元素呢?
  • 顯然,NestedInteger這個(gè)神奇的數(shù)據(jù)結(jié)構(gòu)是問題的關(guān)鍵,不過題目專門提醒我們:
You should not implement it, or speculate about its implementation.

我不應(yīng)該去嘗試實(shí)現(xiàn)NestedInteger這個(gè)結(jié)構(gòu),也不應(yīng)該去猜測它的實(shí)現(xiàn)?為什么?憑什么?是不是題目在誤導(dǎo)我?是不是我進(jìn)行推測之后,這道題就不攻自破了?

  • 你看,我可不是什么好孩子,你不讓推測,我就偏偏要去推測!我反手就把NestedInteger這個(gè)結(jié)構(gòu)給實(shí)現(xiàn)出來:
public class NestedInteger {private Integer val;private List<NestedInteger> list;public NestedInteger(Integer val) {this.val = val;this.list = null;}public NestedInteger(List<NestedInteger> list) {this.list = list;this.val = null;}// 如果其中存的是一個(gè)整數(shù),則返回 true,否則返回 falsepublic boolean isInteger() {return val != null;}// 如果其中存的是一個(gè)整數(shù),則返回這個(gè)整數(shù),否則返回 nullpublic Integer getInteger() {return this.val;}// 如果其中存的是一個(gè)列表,則返回這個(gè)列表,否則返回 nullpublic List<NestedInteger> getList() {return this.list;} }
  • 嗯,其實(shí)這個(gè)實(shí)現(xiàn)也不難嘛,寫出來之后,發(fā)現(xiàn)這玩意兒竟然……
class NestedInteger {Integer val;List<NestedInteger> list; }/* 基本的 N 叉樹節(jié)點(diǎn) */ class TreeNode {int val;TreeNode[] children; }
  • 這玩意兒不就是棵 N 叉樹嗎?葉子節(jié)點(diǎn)是Integer類型,其val字段非空;其他節(jié)點(diǎn)都是List<NestedInteger>類型,其val字段為空,但是list字段非空,裝著孩子節(jié)點(diǎn)。
  • 比如說輸入是[[1,1],2,[1,1]],其實(shí)就是如下樹狀結(jié)構(gòu):
  • 好的,剛才題目說什么來著?把一個(gè)NestedInteger扁平化對(duì)吧?這不就等價(jià)于遍歷一棵 N叉樹的所有「葉子節(jié)點(diǎn)」嗎?我把所有葉子節(jié)點(diǎn)都拿出來,不就可以作為迭代器進(jìn)行遍歷了嗎?
  • N 叉樹的遍歷怎么整?
void traverse(TreeNode root) {for (TreeNode child : root.children)traverse(child);
  • 這個(gè)框架可以遍歷所有節(jié)點(diǎn),而我們只對(duì)整數(shù)型的NestedInteger感興趣,也就是我們只想要「葉子節(jié)點(diǎn)」,所以traverse函數(shù)只要在到達(dá)葉子節(jié)點(diǎn)的時(shí)候把val加入結(jié)果列表即可

代碼一:

class NestedIterator implements Iterator<Integer> {private Iterator<Integer> it;public NestedIterator(List<NestedInteger> nestedList) {// 存放將 nestedList 打平的結(jié)果List<Integer> result = new LinkedList<>();for (NestedInteger node : nestedList) {// 以每個(gè)節(jié)點(diǎn)為根遍歷traverse(node, result);}// 得到 result 列表的迭代器this.it = result.iterator();}public Integer next() {return it.next();}public boolean hasNext() {return it.hasNext();} // 遍歷以 root 為根的多叉樹,將葉子節(jié)點(diǎn)的值加入 result 列表private void traverse(NestedInteger root, List<Integer> result) {if (root.isInteger()) {// 到達(dá)葉子節(jié)點(diǎn)result.add(root.getInteger());return;}// 遍歷框架for (NestedInteger child : root.getList()) {traverse(child, result);}} }

這樣,我們就把原問題巧妙轉(zhuǎn)化成了一個(gè) N 叉樹的遍歷問題,并且得到了解法。

方法二:

  • 以上解法雖然可以通過,但是在面試中,也許是有瑕疵的。
  • 我們的解法中,一次性算出了所有葉子節(jié)點(diǎn)的值,全部裝到result列表,也就是內(nèi)存中,next和hasNext方法只是在對(duì)result列表做迭代。如果輸入的規(guī)模非常大,構(gòu)造函數(shù)中的計(jì)算就會(huì)很慢,而且很占用內(nèi)存。
  • 一般的迭代器求值應(yīng)該是「惰性的」,也就是說,如果你要一個(gè)結(jié)果,我就算一個(gè)(或是一小部分)結(jié)果出來,而不是一次把所有結(jié)果都算出來。
  • 如果想做到這一點(diǎn),使用遞歸函數(shù)進(jìn)行 DFS 遍歷肯定是不行的,而且我們其實(shí)只關(guān)心「葉子節(jié)點(diǎn)」,所以傳統(tǒng)的 BFS 算法也不行。實(shí)際的思路很簡單:
  • 調(diào)用hasNext時(shí),如果nestedList的第一個(gè)元素是列表類型,則不斷展開這個(gè)元素,直到第一個(gè)元素是整數(shù)類型。
  • 由于調(diào)用next方法之前一定會(huì)調(diào)用hasNext方法,這就可以保證每次調(diào)用next方法的時(shí)候第一個(gè)元素是整數(shù)型,直接返回并刪除第一個(gè)元素即可。

代碼二:

public class NestedIterator implements Iterator<Integer> {private LinkedList<NestedInteger> list;public NestedIterator(List<NestedInteger> nestedList) {// 不直接用 nestedList 的引用,是因?yàn)椴荒艽_定它的底層實(shí)現(xiàn)// 必須保證是 LinkedList,否則下面的 addFirst 會(huì)很低效list = new LinkedList<>(nestedList);}public Integer next() {// hasNext 方法保證了第一個(gè)元素一定是整數(shù)類型return list.remove(0).getInteger();}public boolean hasNext() {// 循環(huán)拆分列表元素,直到列表第一個(gè)元素是整數(shù)類型while (!list.isEmpty() && !list.get(0).isInteger()) {// 當(dāng)列表開頭第一個(gè)元素是列表類型時(shí),進(jìn)入循環(huán)List<NestedInteger> first = list.remove(0).getList();// 將第一個(gè)列表打平并按順序添加到開頭for (int i = first.size() - 1; i >= 0; i--) {list.addFirst(first.get(i));}}return !list.isEmpty();} }

C++代碼:

  • 時(shí)間均衡的棧
  • 構(gòu)造時(shí)僅僅扒一層皮就 逆向 堆入棧中,在用戶調(diào)用 hasNext 時(shí)才做深入扒皮搜索。
  • 這種做法比較時(shí)間均衡,如果用戶搞了一個(gè)很長的列表,然后就取前邊幾個(gè)元素就不用了,那這種實(shí)現(xiàn)要高效的多。
class NestedIterator { private:stack<NestedInteger> st; public:NestedIterator(vector<NestedInteger> &nestedList) {for (auto iter = nestedList.rbegin(); iter != nestedList.rend(); iter++) {st.push(*iter);}}int next() {auto t = st.top();st.pop();return t.getInteger();}bool hasNext() {while (!st.empty()) {auto cur = st.top();if (cur.isInteger()) return true;st.pop();auto curList = cur.getList();for (auto iter = curList.rbegin(); iter != curList.rend(); iter++) {st.push(*iter);}}return false;} };
  • 頭重腳輕的棧
  • 構(gòu)造時(shí)扒皮抽骨到單個(gè)數(shù)字再 push 到棧里。這樣預(yù)處理很慢,但是調(diào)用時(shí)很快。有點(diǎn)頭重腳輕。
class NestedIterator { private:stack<NestedInteger> st; public:NestedIterator(vector<NestedInteger> &nestedList) {for (auto iter = nestedList.rbegin(); iter != nestedList.rend(); iter++) {parseData(*iter);}}void parseData(NestedInteger item) {if (item.isInteger()) {st.push(item);} else {auto list = item.getList();for (auto iter = list.rbegin(); iter != list.rend(); iter++) {parseData(*iter);}}}int next() {auto t = st.top();st.pop();return t.getInteger();}bool hasNext() {if (st.empty()) return false;return true;} };
  • 頭重腳輕的 vector
  • 不用棧,用 vector 來做也可以。只是存儲(chǔ)的時(shí)候不再是從尾到頭而是從頭到尾啦。
class NestedIterator { private:vector<int> v;int ind = 0;public:NestedIterator(vector<NestedInteger> &nestedList) {for (auto iter = nestedList.begin(); iter != nestedList.end(); iter++) {parseData(*iter);}}void parseData(NestedInteger item) {if (item.isInteger()) {v.push_back(item.getInteger());} else {auto list = item.getList();for (auto iter = list.begin(); iter != list.end(); iter++) {parseData(*iter);}}}int next() {return v[ind++];}bool hasNext() {if (ind >= v.size()) return false;return true;} };

總結(jié)

以上是生活随笔為你收集整理的力扣--扁平化嵌套列表迭代器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。