iOS 面試題(八):實現(xiàn)一個嵌套數(shù)組的迭代器

問題:

給你一個嵌套的 NSArray 數(shù)據(jù),實現(xiàn)一個迭代器類瑰抵,該類提供一個 next() 方法,可以依次的取出這個 NSArray 中的數(shù)據(jù)。

比如 NSArray 如果是 [1,[4,3],6,[5,[1,0]]]局荚, 則最終應該輸出:1, 4, 3, 6, 5, 1, 0 。

另外愈污,實現(xiàn)一個 allObjects 方法耀态,可以一次性取出所有元素。

給你一個嵌套的 NSArray 數(shù)據(jù)暂雹,實現(xiàn)一個迭代器類首装,該類提供一個 next() 方法,可以依次的取出這個 NSArray 中的數(shù)據(jù)杭跪。

解答:

本題的代碼稍長仙逻,完整的代碼我放在git上了,以下是講解涧尿。

先說第二問吧系奉,第二問比較簡單:實現(xiàn)一個 allObjects 方法,可以一次性取出所有元素姑廉。

對于此問缺亮,我們可以實現(xiàn)一個遞歸函數(shù),在函數(shù)中判斷數(shù)組中的元素是否又是數(shù)組桥言,如果是的話瞬内,就遞歸調(diào)用自己迷雪,如果不是數(shù)組,則加入到一個 NSMutableArray 中即可虫蝶。下面是示例代碼:

- (NSArray*)allObjects {NSMutableArray*result= [NSMutableArrayarray];

[self fillArray:_originArray into:result];returnresult;

}

- (void)fillArray:(NSArray*)arrayinto:(NSMutableArray*)result{for(NSUIntegeri =0; i

[self fillArray:array[i] into:result];

}else{

[resultaddObject:array[i]];

}

}

}

如果你還在糾結(jié)掌握遞歸有什么意義的話章咧,歡迎翻翻我半年前寫的另一篇文章:遞歸的故事(上)遞歸的故事(下)能真。

接下來讓我們來看第一問赁严,在同學的回復中,我看到很多人用第二問的辦法粉铐,把數(shù)組整個另外保存一份疼约,然后再記錄一個下標,每次返回其中一個蝙泼。這個方法當然是可行的程剥,但是大部分的迭代器通常都不會這么實現(xiàn)。因為這么實現(xiàn)的話汤踏,數(shù)組需要整個復制一遍织鲸,空間復雜度是 O(N)。

所以溪胶,我個人認為本題第一問更好的解法是:

記錄下遍歷的位置搂擦,然后每次遍歷時更新位置。由于本題中元素是一個嵌套數(shù)組哗脖,所以我們?yōu)榱擞涗浵挛恢闷偬撸托枰獌蓚€變量:一個是當前正在遍歷的子數(shù)組,另一個是這個數(shù)組遍歷到的位置才避。

我在實現(xiàn)的時候橱夭,定義了一個名為 NSArrayIteratorCursor 的類來記錄這些內(nèi)容,NSArrayIteratorCursor 的定義和實現(xiàn)如下:

@interface ? NSArrayIteratorCursor:NSObject

@property(nonatomic)NSArray*array;

@property(nonatomic)NSUIntegerindex;

@end

@implementation ?NSArrayIteratorCursor

- (id)initWithArray:(NSArray*)array?

{self= [superinit];

if(self) {

_array = array;

_index =0;

}

return ?self;

}

@end

由于數(shù)組在遍歷的時候可能產(chǎn)生遞歸桑逝,就像我們實現(xiàn) allObjects 方法那樣徘钥。所以我們需要處理遞歸時的 NSArrayIteratorCursor 的保存,我在實現(xiàn)的時候肢娘,拿數(shù)組當作棧呈础,來實現(xiàn)保存遍歷時的狀態(tài)。

最終橱健,我實現(xiàn)了一個迭代器類而钞,名字叫 NSArrayIterator,用于最終提供 next 方法的實現(xiàn)拘荡。這個類有兩個私有變量臼节,一個是剛剛說的那個棧,另一個是原數(shù)組的引用。

@interface ?NSArrayIterator:NSObject

- (id)initWithArray:(NSArray*)array;

- (id)next;

- (NSArray*)allObjects;

@end

@implementation NSArrayIterator

{NSMutableArray*_stack;

NSArray*_originArray;

}

在初使化的時候网缝,我們初始化遍歷位置的代碼如下:

- (id)initWithArray:(NSArray*)array?{

self= [super init];

if(self) {? ? ? ?

?_originArray = array;? ? ? ?

?_stack = [NSMutableArray ?array];? ? ?

? [self ?setupStack];??

? }

return ?self;

}

- (void)setupStack?{

NSArray ?IteratorCursor*c = [[NSArray IteratorCursoralloc] initWithArray:_originArray];?

? [_stack addObject:c];

}

接下來就是最關(guān)鍵的代碼了巨税,即實現(xiàn) next 方法,在 next 方法的實現(xiàn)邏輯中粉臊,我們需要:

判斷棧是否為空草添,如果為空則返回 nil。

從棧中取出元素扼仲,看是否遍歷到了結(jié)尾远寸,如果是的話,則出棧屠凶。

判斷第 2 步是否使棧為空驰后,如果為空,則返回 nil矗愧。

終于拿到元素了灶芝,這一步判斷拿到的元素是否是數(shù)組。

如果是數(shù)組唉韭,則重新生成一個遍歷的 NSArrayIteratorCursor 對象夜涕,放到棧中。

重新從棧中拿出第一個元素纽哥,循環(huán)回到第 4 步的判斷钠乏。

如果到了這一步栖秕,說明拿到了一個非數(shù)組的元素春塌,這樣就可以把元素返回,同時更新索引到下一個位置簇捍。

以下是相關(guān)的代碼只壳,對于沒有算法基礎(chǔ)的同學,可能讀起來還是比較累暑塑,其實我寫起來也不快吼句,所以希望你能多理解一下,其實核心思想就是手工操作棧的入棧和出棧:

- (id)next {

//? 1. 判斷棧是否為空事格,如果為空則返回 nil惕艳。

if([_stackcount] ==0)?{

return nil;

}

// 2. 從棧中取出元素,看是否遍歷到了結(jié)尾驹愚,如果是的話远搪,則出棧。

NSArray IteratorCursor*c;

c= [_stack lastObject];

while(c.index ==c.array.count&& _stack.count>0) {

[_stack removeLastObject];

c= [_stack lastObject];

}

// 3. 判斷第 2 步是否使棧為空逢捺,如果為空谁鳍,則返回 nil。

if(_stack.count==0) {

returnnil;

}

// 4. 終于拿到元素了,這一步判斷拿到的元素是否是數(shù)組倘潜。

id item =c.array[c.index];

while([item isKindOfClass:[NSArrayclass]]){

c.index++;

// 5. 如果是數(shù)組绷柒,則重新生成一個遍歷的 NSArrayIteratorCursor 對象,放到棧中涮因。

NSArrayIteratorCursor *nc = [[NSArrayIteratorCursor alloc] initWithArray:item];

[_stack addObject:nc];

// 6. 重新從棧中拿出第一個元素废睦,循環(huán)回到第 4 步的判斷。c= nc;

item =c.array[c.index];

}

// 7. 如果到了這一步蕊退,說明拿到了一個非數(shù)組的元素郊楣,這樣就可以把元素返回,同時更新索引到下一個位置瓤荔。

c.index++;returnitem;

}

在讀者回復中净蚤,聽榆大叔 和 yiplee 同學用了類似的做法,他們的代碼在:

聽榆大叔输硝、yiplee

最終今瀑,我想說這個只是我個人想出來的解法,很可能不是最優(yōu)的点把,甚至可能也有很多問題橘荠,比如,這個代碼有很多可以進一步 challenge 的地方:

這個代碼是線程安全的嗎郎逃?如果我們要實現(xiàn)一個線程安全的迭代器哥童,應該怎么做?

如果在使用迭代器的時候褒翰,數(shù)組被修改了贮懈,會怎么樣?

如何檢測在遍歷元素的時候优训,數(shù)組被修改了朵你?

如何避免在遍歷元素的時候,數(shù)組被修改揣非?

如果大家有想出更好的解法抡医,歡迎留言告訴我。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末早敬,一起剝皮案震驚了整個濱河市忌傻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌搞监,老刑警劉巖水孩,帶你破解...
    沈念sama閱讀 221,576評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異腺逛,居然都是意外死亡矗积,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評論 3 399
  • 文/潘曉璐 我一進店門银伟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抛杨,你說我怎么就攤上這事〖隼啵” “怎么了怖现?”我有些...
    開封第一講書人閱讀 168,017評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長玉罐。 經(jīng)常有香客問我屈嗤,道長,這世上最難降的妖魔是什么吊输? 我笑而不...
    開封第一講書人閱讀 59,626評論 1 296
  • 正文 為了忘掉前任饶号,我火速辦了婚禮,結(jié)果婚禮上季蚂,老公的妹妹穿的比我還像新娘茫船。我一直安慰自己,他們只是感情好扭屁,可當我...
    茶點故事閱讀 68,625評論 6 397
  • 文/花漫 我一把揭開白布算谈。 她就那樣靜靜地躺著,像睡著了一般料滥。 火紅的嫁衣襯著肌膚如雪然眼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,255評論 1 308
  • 那天葵腹,我揣著相機與錄音高每,去河邊找鬼。 笑死礁蔗,一個胖子當著我的面吹牛觉义,可吹牛的內(nèi)容都是我干的雁社。 我是一名探鬼主播浴井,決...
    沈念sama閱讀 40,825評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼霉撵!你這毒婦竟也來了磺浙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評論 0 276
  • 序言:老撾萬榮一對情侶失蹤徒坡,失蹤者是張志新(化名)和其女友劉穎撕氧,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體喇完,經(jīng)...
    沈念sama閱讀 46,271評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡伦泥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,363評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片不脯。...
    茶點故事閱讀 40,498評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡府怯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出防楷,到底是詐尸還是另有隱情牺丙,我是刑警寧澤,帶...
    沈念sama閱讀 36,183評論 5 350
  • 正文 年R本政府宣布复局,位于F島的核電站冲簿,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏亿昏。R本人自食惡果不足惜峦剔,卻給世界環(huán)境...
    茶點故事閱讀 41,867評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望角钩。 院中可真熱鬧羊异,春花似錦、人聲如沸彤断。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽宰衙。三九已至平道,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間供炼,已是汗流浹背一屋。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留袋哼,地道東北人冀墨。 一個月前我還...
    沈念sama閱讀 48,906評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像涛贯,于是被迫代替她去往敵國和親诽嘉。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,507評論 2 359

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