1. 程式人生 > >面試題集-堆疊和佇列

面試題集-堆疊和佇列

如何準備:

Whether you are asked to implement a simple stack / queue, or you are asked to implementa modified version of one, you will have a big leg up on other candidates if you can flawlessly work with stacks and queues Practice makes perfect! Here is some skeleton code for a Stackand Queue class

當面試官需要你實現一個簡單的堆疊或者列隊的時候,你能順利的寫完,那你就比一般的應聘者先人一步了。熟能生巧,下面的這些基本的堆疊和佇列的框架程式碼一定要掌握。

Implementing a Stack

堆疊

1 class Stack {2 Node top;3 Node pop() {4 if (top != null) {5 Object item = top.data;6 top = top.next;7 return item;8 }9 return null;10 }11 void push(Object item) {12 Node t = new Node(item);13 t.next = top;14 top = t;15 }16 }

Implementing a Queue

佇列

1 class Queue {2 Node first, last;3 void enqueue(Object item) {4 if (!first) {5 back = new Node(item);6 first = back;7 } else {8 back.next = new Node(item);9 back = back.next;10 }11 }12 Node dequeue(Node n) {13 if (front != null) {14 Object item = front.data;15 front = front.next;16 return item;17 }18 return null;19 }20 }
3 1 Describe how you could use a single array to implement three stacks

3.1 怎樣用一個數組實現三個堆疊
3.1解答:
解法一:
將陣列劃分成3等份,每一份獨立的用來實現堆疊。
*第一個堆疊:從 0     至 n/3
*第二個堆疊:從 n/3  至 2n/3
*第三個堆疊:從2n/3 至 n
這種解法是基於對每個堆疊的使用沒有額外的使用說明,所以我們直接為每個堆疊劃分固定的大小。

解法二:
解法二中的,主要陣列中還有空餘的空間,堆疊就還能增長。
每次為堆疊分配一個空間的時候,在這個新空間中記錄上一個空間地址。這樣堆疊中的每個元素都有一個指標指向之前的元素。
這樣的實現方法有一個問題就是如果一個堆疊彈出一個空間(釋放空間),這個空間並不會作為空閒空間現在陣列後面。這樣話我們就不能使用新產生的空閒空間。
為了解決這個問題,我們用一個列表來記錄空閒的空間。當有新空閒空間出現,我們就把它加入到這個表中。如果需要新分配一個空間,就從這個表中刪除一個元素。
這樣的實現方法使得3個堆疊能夠動態的使用陣列的空間,但是這是以增大空間複雜度換來的。


3 2 How would you design a stack which, in addition to push and pop, also has a function min which returns the minimum element? Push, pop and min should all operate in O(1) time

3.2 適合實現一個堆疊,除了有函式push、pop函式之外還有min函式,min函式範圍堆疊中的最小元素。要求push,pop和min三個函式的時間複雜度均為O(1)。
3.2解答:
在每個堆疊中的節點中記錄目前堆疊中的最小值。那麼呼叫min( )函式時只需要看看棧頂元素中記錄的最小值即可。

但是這樣解法存在的問題是,如果堆疊的需要記錄的元素非常多,那麼這樣的方法將會消耗大量的空間。因為我們在每個堆疊的元素中都記錄來了最小值。這個能不能改進呢?
我們可以在建立一個輔助的堆疊只用來記錄最小的元素。

這樣的方法就是不是有更高的效率呢,在堆疊s2中只記錄最小值,避免了大量的冗餘資料的記錄。

3 3 Imagine a (literal) stack of plates If the stack gets too high, it might topple There- fore, in real life, we would likely start a new stack when the previous stack exceeds some threshold Implement a data structure SetOfStacks that mimics this SetOf- Stacks should be composed of several stacks, and should create a new stack once the previous one exceeds capacity SetOfStacks push() and SetOfStacks pop() should behave identically to a single stack (that is, pop() should return the same values as it would if there were just a single stack)  FOLLOW UP  Implement a function popAt(int index) which performs a pop operation on a specific sub-stack

3.3 想象下啊:一堆盤子,如果堆得太高的話,就容易倒下來。所以在現實中如果盤子堆到一定高度,我們就會重新起一個堆。現在實現一個新的資料結構來模擬這樣現象。SetOfStack當中包含很多的堆疊,當一個堆疊達到上限的時候,啟用下一個堆疊。SetOfStack.push 和 SetOfStack.pop應該和普通堆疊的操作一樣。
進階:
實現一個函式popAt(int index),指定在哪個堆疊上彈出元素。
3.3解答:
根據題意,我們的資料結構大體上應該是這麼一個框架:

由於要和普通的堆疊的push()有相同的效果,也就是說每次push()都必須將元素放到最近使用的一個堆疊中。但是在這個堆疊已經滿了情況下,那就必須建一個新的堆疊然後再入棧。那麼push的實現如下:

那pop()如何實現呢?和push()差不多,也一定要在最近的一個堆疊上操作。但是如果最後一個堆疊是空的話,就應該將其移除。

那進階的問題這麼處理呢?
這個問題確實有點難度。實現起來也比較麻煩,因為整個系統看起來應該像一個“翻轉”系統。如果我從堆疊1中彈出一個元素,那麼我們需要將堆疊2底部的元素壓到堆疊1的頂端。堆疊3的元素要到堆疊2....
注:你可能會不同意我的說法。認為實現這個函式不需要“翻轉”整個堆疊。系統中的每個堆疊並不需要都是滿棧的,這樣的話也可以省下很多的時間複雜度,特別是在堆疊非常大的時候。但是如果假設除了最後一個堆疊之外,所有的堆疊必須滿棧的話,這樣的方法就不行了。具體採用什麼樣的結構,你可以在面試時和麵試官好好溝通然後決定。

3 4 In the classic problem of the Towers of Hanoi, you have 3 rods and N disks of different sizes which can slide onto any tower The puzzle starts with disks sorted in ascending order of size from top to bottom (e g , each disk sits on top of an even larger one) You have the following constraints:  (A) Only one disk can be moved at a time  (B) A disk is slid off the top of one rod onto the next rod  (C) A disk can only be placed on top of a larger disk  Write a program to move the disks from the first rod to the last using Stacks

3.4 經典的漢諾塔問題,有3根柱子,柱子上串有N個尺寸不同的碟子。漢諾塔問題的起始狀態為,所有的碟子都從小到大的穿在柱子上(下面的碟子最大)。在滿足下面三個限制:(A) 每次只能移動一個碟子;(B) 只有每根柱子頂端的碟子才能移動;(C)任何碟子只能放在比它大的碟子上。寫一段程式(要求使用堆疊),將第一個根柱子上所有的碟子移動到移到最後一根柱子上。

3.4解答:
首先我們考慮解題的演算法:將N個碟子從第一根柱子移到最後一根柱子。我們先從最簡單的情況開始。如果只有一個碟子,那麼直接將它移到最後的柱子。那兩個碟子呢?
(1)先將第一個碟子從第一根柱子移到第二根
(2)將第二個碟子從第一根柱子上移動到第三根
(3)再將第二根柱子上的碟子移動到第三根上,完成!
那三個碟子呢?
(1)採用兩個碟子移動的方法,講上兩個碟子移動到第二根柱子上
(2)將第三個碟子移動到第三根柱子
(3)在採用運來的方法將第二根柱子上的兩個碟子移動到第三根柱子。
很明顯採用遞迴演算法就能解決本題:

3.5 Implement a MyQueue class which implements a queue using two stacks

3.5 用兩個堆疊來實現一個佇列
3.5解答:
堆疊和佇列的最大區別在於:一個是先進後出,一個是後進先出。但是題目的要求使得peek和pop的動作恰好相反。那麼我們就可以利用第二個堆疊完成入棧元素順序的反轉。(先所有的元素進堆疊S1,這樣最先進棧的元素在棧底,最後的在棧頂;然後將S1中的元素依次彈出,並壓入堆疊S2中。這樣S2中先進的元素就在棧頂後進的就在棧底了。)
但是如果對佇列和出佇列的動作反覆進行的話,我就要在S1和S2兩個堆疊中反覆的倒來倒去。其實有偷懶的方法。
當有元素要進入佇列的時候,直接壓如堆疊S1中,元素需要從佇列中出列的話,檢查S2中是否有元素,如果沒有再採用之前的方法將S1中的元素“倒”入到S2中,如果S2中非空在直接彈出元素即為佇列中出列的元素。這樣的方法就免於在兩個堆疊之間倒來倒去了:

3 6 Write a program to sort a stack in ascending order You should not make any assumptions about how the stack is implemented The following are the only functions that should be used to write this program: push | pop | peek | isEmpty

3.6 寫一個程式將堆疊升序排序。該堆疊就是一個普通的堆疊,不能有其他假設。函式實現是隻能呼叫函式push(),pop(),peek()和isEmpty這幾個函式。
3.6解答:
再建一個堆疊,從原堆疊中彈出一個元素到新的堆疊中。然後再比較原堆疊棧頂元素和新堆疊棧頂大小。如果符合排序規則,再次入新堆疊。如果不符合,在彈出新堆疊中的元素逐個比較直到滿足排序關係。這樣類似插入排序的方法,之間複雜度為O(n^2)。