版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
Python程序員面試分類真題31.
實現(xiàn)一個棧的數(shù)據(jù)結(jié)構(gòu),使其具有以下方法:壓棧、彈棧、取棧頂元素、判斷棧是否為空以及獲取棧中元素個數(shù)。正確答案:棧的實現(xiàn)有兩種方法,分別為采用數(shù)組來實現(xiàn)和采用鏈表(江南博哥)來實現(xiàn)。下面分別詳細介紹這兩種方法。
方法一:數(shù)組實現(xiàn)
在采用數(shù)組來實現(xiàn)棧的時候,??臻g是一段連續(xù)的空間。實現(xiàn)思路如下圖所示。
從上圖中可以看出,可以把數(shù)組的首元素當做棧底,同時記錄棧中元素的個數(shù)size,假設(shè)數(shù)組首地址為arr,從上圖可以看出,壓棧的操作其實是把待壓棧的元素放到數(shù)組arr[size]中,然后執(zhí)行size+操作;同理,彈棧操作其實是取數(shù)組arr[size-1]元素,然后執(zhí)行size-操作。根據(jù)這個原理可以非常容易實現(xiàn)棧,示例代碼如下:
classMyStack:
#模擬棧
def__init__(self):
self.items=[]
#撐判斷棧是否為空
defisEmpty(self):
returnlen(self.items)==0
#返回棧的大小
defsize(self):
returnlen(self.items)
#返回棧頂元素
deftop(self):
ifnotself.isEmpty():
returnelf.items[len(self.items)-1]
else:
returnNone
#彈棧
defpop(self):
iflen(self.items)>0:
returnself.items.pop()
else:
print"棧已經(jīng)為空"
returnNone
#壓棧
defpush(self,item):
self.items.append(item)
if__name__=="__main__":
s=MyStack()
s.push(4)
print"棧頂元素為:"+str(s.top())
print"棧大小為:"+str(s.size())
s.pop()
prmt"彈棧成功"
s.pop()
方法二:鏈表實現(xiàn)
在創(chuàng)建鏈表的時候經(jīng)常采用一種從頭結(jié)點插入新結(jié)點的方法,可以采用這種方法來實現(xiàn)棧,最好使用帶頭結(jié)點的鏈表,這樣可以保證對每個結(jié)點的操作都是相同的,實現(xiàn)思路如下圖所示。
在上圖中,在進行壓棧操作的時候,首先需要創(chuàng)建新的結(jié)點,把待壓棧的元素放到新結(jié)點的數(shù)據(jù)域中,然后只需要(1)和(2)兩步就實現(xiàn)了壓棧操作(把新結(jié)點加到了鏈表首部)。同理,在彈棧的時候,只需要進行(3)的操作就可以刪除鏈表的第一個元素,從而實現(xiàn)彈棧操作。實現(xiàn)代碼如下:
classLNode:
def__new__(self,x):
self.data=x
self.next=None
classMyStack:
def__init__(self):
#pHead=LNode()
self.data=None
self.nexFNone
#判斷stack是否為空,如果為空返回true,否則返回false
defempty(self):
ifself.next==None:
returnTrue
else:
returnFalse
#獲取棧中元素的個數(shù)
defsize(self):
size=0
p=self.next
whilep!=None:
p=p.next
size+=1
returnsize
#入棧:把e放到棧頂
defpush(self,e):
p=LNode
p.data=e
p.next=self.next
self.next=p
#出棧,同時返回棧頂元素
defpop(self):
tmp=self.next
iftmp!=None:
self.next=tmp.next
returntmp.data
print"棧已經(jīng)為空"
returnNone
#取得棧頂元素
deftop(self):
ifself.next!=None:
returnself.next.data
print"棧已經(jīng)為空"
returnNone
if__name__=="__main__":
stack=MyStack()
stack.push(1)
print"棧頂元素為:"+str(stack.top())
print"棧大小為:"+str(stack.size())
stack.pop()
print"彈棧成功"
stack.pop()
程序的運行結(jié)果為:
棧頂元素為:1
棧大小為:1
彈棧成功
棧已經(jīng)為空
兩種方法的對比:
采用數(shù)組實現(xiàn)棧的優(yōu)點是:一個元素值占用一個存儲空間;它的缺點為:如果初始化申請的存儲空間太大,會造成空間的浪費,如果申請的存儲空間太小,后期會經(jīng)常需要擴充存儲空間,擴充存儲空間是個費時的操作,這樣會造成性能的下降。
采用鏈表實現(xiàn)棧的優(yōu)點是:使用靈活方便,只有在需要的時候才會申請空間。它的缺點為:除了要存儲元素外,還需要額外的存儲空間存儲指針信息。
算法性能分析:
這兩種方法壓棧與彈棧的時間復雜度都為O(1)。[考點]如何實現(xiàn)棧
2.
實現(xiàn)一個隊列的數(shù)據(jù)結(jié)構(gòu),使其具有入隊列、出隊列、查看隊列首尾元素、查看隊列大小等功能。正確答案:與實現(xiàn)棧的方法類似,隊列的實現(xiàn)也有兩種方法,分別為采用數(shù)組來實現(xiàn)和采用鏈表來實現(xiàn)。下面分別詳細介紹這兩種方法。
方法一:數(shù)組實現(xiàn)
下圖給出了一種最簡單的實現(xiàn)方式,用front來記錄隊列首元素的位置,用rear來記錄隊列尾元素往后一個位置。入隊列的時候只需要將待入隊列的元素放到數(shù)組下標為rear的位置,同時執(zhí)行rear+,出隊列的時候只需要執(zhí)行front+即可。
示例代碼如下:
classMyQueue:
def__init__(self):
self.arr=[]
self.front=0#隊列頭
self.rear=0#隊列尾
#判斷隊列是否為空
defisEmpty(self):
returnself.front==self.rear
#返回隊列的大小
defsize(self):
returnself.rear-self.front
#返回隊列首元素
defgetFront(self):
ifself.isEmpty():
returnNone
returnself.arr[self.front]
#返回隊列尾元素
defgetBack(self):
ifself.isEmpty():
returnNone
returnself.arr[self.rear-1]
#刪除隊列頭元素
defdeQueue(self):
ifself.rear>self.front:
self.front+=1
else:
print"隊列已經(jīng)為空"
#把新元素加入隊列尾
defenQueue(self,item):
self.arr.append(item)
self.rear+=1
if__name__=="__main__":
queue=MyQueue()
queue.enQueue(1)
queue.enQueue(2)
print"隊列頭元素為:"+str(queue.getFront())
print"隊列尾元素為:"+str(queue.getBack())
print"隊列大小為:"+str(queue.size())
程序的運行結(jié)果為:
隊列頭元素為:1
隊列尾元素為:2
隊列大小為:2
以上這種實現(xiàn)方法最大的缺點為:出隊列后數(shù)組前半部分的空間不能被充分地利用,解決這個問題的方法為把數(shù)組看成一個環(huán)狀的空間(循環(huán)隊列)。當數(shù)組最后一個位置被占用后,可以從數(shù)組首位置開始循環(huán)利用,具體實現(xiàn)方法可以參考數(shù)據(jù)結(jié)構(gòu)的課本。
方法二:鏈表實現(xiàn)
采用鏈表實現(xiàn)隊列的方法與實現(xiàn)棧的方法類似,分別用兩個指針指向隊列的首元素與尾元素,如下圖所示。用pHead來指向隊列的首元素,用pEnd來指向隊列的尾元素。
在上圖中,剛開始隊列中只有元素1、2和3,當新元素4要進隊列的時候,只需要上圖中(1)和(2)兩步,就可以把新結(jié)點連接到鏈表的尾部,同時修改pEnd指針指向新增加的結(jié)點。出隊列的時候只需要步驟(3),改變pHead指針使其指向pHead.next,此外也需要考慮結(jié)點所占空間釋放的問題。在入隊列與出隊列的操作中也需要考慮隊列尾空的時候的特殊操作,實現(xiàn)代碼如下所示:
classLNode:
def__new__(self,x):
self.data=x
self.next=None
classMyQueue:
#分配頭結(jié)點
def__init__(self):
self.pHead=None
self.pEnd=None
#判斷隊列是否為空,如果為空返回true,否則返回false
defempty(self):
ifself.pHead=None:
returnTrue
else:
returnFalse
#獲取棧中元素的個數(shù)
defsize(self):
size=()
p=self.pHead
whilep!=None:
p=p.next
size+=1
returnsize
#入隊列:把元素e加到隊列尾
defenQueue(self,e):
p=LNode()
p.data=e
p.next=None
ifself.pHead==None:
self.pHead=self.pEnd=p
else:
self.pEnd.next=p
selfpEnd=p
#出隊列,刪除隊列首元素
defdeQueue(self):
ifself.pHead==None:
print"出隊列失敗,隊列已經(jīng)為空"
self.pHead=self.pHead.next
ifself.pHead==None:
self.pEnd=None
#取得隊列首元素
defgetFront(self):
ifself.pHead==None:
print"獲取隊列首元素失敗,隊列已經(jīng)為空"
returnNone
returnself.pHead.data
#取得隊列尾元素
defgetBack(self):
ifself.pEnd==None:
print"獲取隊列尾元素失敗,隊列已經(jīng)為空"
returnNone
returnself.pEnd.data
if__name__="__main__":
queue=MyQueue()
queue.enQueue(1)
queue.enQueue(2)
print"隊列頭元素為:"+str(queue.getFront())
print"隊列尾元素為:"+str(queue.getBack())
print"隊列大小為:"+str(queue.size())
程序的運行結(jié)果為:
隊列頭元素為:1
隊列尾元素為:2
隊列大小為:2
顯然用鏈表來實現(xiàn)隊列有更好的靈活性,與數(shù)組的實現(xiàn)方法相比,它多了用來存儲結(jié)點關(guān)系的指針空間。此外,也可以用循環(huán)鏈表來實現(xiàn)隊列,這樣只需要一個指向鏈表最后一個元素的指針即可,因為通過指向鏈表尾元素可以非常容易地找到鏈表的首結(jié)點。[考點]如何實現(xiàn)隊列
3.
翻轉(zhuǎn)(也叫顛倒)棧的所有元素,例如輸入棧{1,2,3,4,5},其中,1處在棧頂,翻轉(zhuǎn)之后的棧為{5,4,3,2,1},其中,5處在棧頂。正確答案:最容易想到的辦法是申請一個額外的隊列,先把棧中的元素依次出棧放到隊列里,然后把隊列里的元素按照出隊列順序入棧,這樣就可以實現(xiàn)棧的翻轉(zhuǎn),這種方法的缺點是需要申請額外的空間存儲隊列,因此,空間復雜度較高。下面介紹一種空間復雜度較低的遞歸的方法。
遞歸程序有兩個關(guān)鍵因素需要注意:遞歸定義和遞歸終止條件。經(jīng)過分析后,很容易得到該問題的遞歸定義和遞歸終止條件。遞歸定義:將當前棧的最底元素移到棧頂,其他元素順次下移一位,然后對不包含棧頂元素的子棧進行同樣的操作。終止條件:遞歸下去,直到棧為空。遞歸的調(diào)用過程如下圖所示:
在上圖中,對于棧{1,2,3,4,5},進行翻轉(zhuǎn)的操作為:首先把棧底元素移動到棧頂?shù)玫綏5,1,2,3,4},然后對不包含棧頂元素的子棧進行遞歸調(diào)用(對子棧元素進行翻轉(zhuǎn)),子棧{1,2,3,4}翻轉(zhuǎn)的結(jié)果為{4,3,2,l},因此,最終得到翻轉(zhuǎn)后的棧為{5,4,3,2,1}。
此外,由于棧的后進先出的特點,使得只能取棧頂?shù)脑?,因此,要把棧底的元素移動到棧頂也需要遞歸調(diào)用才能完成,主要思路為:把不包含該棧頂元素的子棧的棧底的元素移動到子棧的棧頂,然后把棧頂?shù)脑嘏c子棧棧頂?shù)脑?其實就是與棧頂相鄰的元素)進行交換。
為了更容易理解遞歸調(diào)用,可以認為在進行遞歸調(diào)用的時候,子棧已經(jīng)把棧底元素移動到了棧頂,在上圖中,為了把棧{1,2,3,4,5}的棧底元素5移動到棧頂,首先對子棧{2,3,4,5},進行遞歸調(diào)用,調(diào)用的結(jié)果為{5,2,3,4},然后對子棧頂元素5,與棧頂元素1進行交換得到棧{5,1,2,3,4),實現(xiàn)了把棧底元素移動到棧頂。
實現(xiàn)代碼如下:
#Python中沒有棧的模塊,所以先新建一個棧類
classStack:
#模擬棧
def__init__(self):
self.items=[]
#判斷棧是否為空
defempty(self):
returnlen(self.items)==0
#返回棧的大小
defsize(self):
returnlen(self.items)
#返回棧頂元素
defpeek(self):
ifnotself.empty():
returnself.items[len(self.items)-1]
else:
returnNone
#彈棧
defpop(self):
iflen(self.items)>0:
returnself.items.pop()
else:
priiit"棧已經(jīng)為空"
returnNone
#壓棧
defpush(self,item):
self.items.append(item)
"""
方法功能:把棧底元素移動到棧頂
參數(shù):s棧的引用
"""
defmoveBottomToTop(s):
ifs.empty():
return
top1=s.peek()
s.pop()#彈出棧項元素
ifnots.empty():
#遞歸處理不包含棧頂元素的子棧
moveBottomToTop(s)
top2=s.peek()
s.pop()
#交換棧頂元素與予棧棧頂元素
s.push(top1)
s.push(top2)
else:
s.push(top1)
defreverse_stack(s):
ifs.empty():
return
#把棧底元素移動到棧頂
moveBottomToTop(s)
top=s.peek()
s.pop()
#遞歸處理子棧
reverse_stack(s)
s.push(top)
if__name__=="__main__":
s=Stack()
s.push(5)
s.push(4)
s.push(3)
s.push(2)
s.push(1)
reverse_stack(s)
print"翻轉(zhuǎn)后出棧順序為:",
whilenots.empty():
prints.peek(),
s.pop()
程序的運行結(jié)果為:
翻轉(zhuǎn)后出棧順序為:54321
算法性能分析:
把棧底元素移動到棧頂操作的時間復雜度為O(N),在翻轉(zhuǎn)操作中對每個子棧都進行了把棧底元素移動到棧頂?shù)牟僮?,因此,翻轉(zhuǎn)算法的時間復雜度為O(N2)。[考點]如何翻轉(zhuǎn)棧的所有元素
4.
輸入兩個整數(shù)序列,其中一個序列表示棧的push(入)順序,判斷另一個序列有沒有可能是對應(yīng)的pop(出)順序。正確答案:假如輸入的push序列是1、2、3、4、5,那么3、2、5、4、1就有可能是一個pop序列,但5、3、4、1、2就不可能是它的一個pop序列。
主要思路是使用一個棧來模擬入棧順序,具體步驟如下:
(1)把push序列依次入棧,直到棧頂元素等于pop序列的第一個元素,然后棧頂元素出棧,pop序列移動到第二個元素;
(2)如果棧頂繼續(xù)等于pop序列現(xiàn)在的元素,則繼續(xù)出棧并pop后移;否則對push序列繼續(xù)入棧。
(3)如果push序列已經(jīng)全部入棧,但是pop序列未全部遍歷,而且棧頂元素不等于當前pop元素,那么這個序列不是一個可能的出棧序列。如果棧為空,而且pop序列也全部被遍歷過,則說明這是一個可能的pop序列。下圖給出一個合理的pop序列的判斷過程。
在上圖中,(1)~(3)三步,由于棧頂元素不等于pop序列第一個元素3,因此,1,2,3依次入棧,當3入棧后,棧頂元素等于pop序列的第一個元素3,因此,第(4)步執(zhí)行3出棧,接下來指向第二個pop序列2,且棧頂元素等于pop序列的當前元素,因此,第(5)步執(zhí)行2出棧;接著由于棧頂元素4不等于當前pop序列5,因此,接下來(6)和(7)兩步分別執(zhí)行4和5入棧;接著由于棧頂元素5等于pop序列的當前值,因此,第(8)步執(zhí)行5出棧,接下來(9)和(10)兩步棧項元素都等于當前pop序列的元素,因此,都執(zhí)行出棧操作。最后由于棧為空,同時pop序列都完成了遍歷,因此,{3,2,5,4,1}是一個合理的出棧序列。
實現(xiàn)代碼如下:
classStack:
#模擬棧
def__init__(self):
selfitems=[]
#判斷棧是否為空
defempty(self):
returnlen(self.items)==0
#返回棧的大小
defsize(self):
returnlen(self.items)
#返回棧頂元素
defpeek(self):
ifnotself.empty():
returnself.items[len(self.items)-1]
else:
returnNone
#彈棧
defpop(self):
iflen(self.items)>0:
returnself.items.pop()
else:
print"棧已經(jīng)為空"
returnNone
#壓棧
defpush(self,item):
self.items.append(item)
defisPopSerial(push,pop):
ifpush==Noneorpop==None:
returnFalse
pushLen=len(push)
popLen=len(pop)
ifpushLen!=popLen:
returnFalse
pushIndex=0
popIndex=0
stack=Stack()
whilepushIndex<pushLen:
#把push序列依次入棧,直到棧頂元素等于pop序列的第一個元素
stack.push(push[pushIndex])
pushIndex+=1
#棧頂元素出棧,pop序列移動到下一個元素
while(notstack.empty())andstack.peek()==pop[popIndex]:
stack.pop()
popIndex+=1
#棧為空,且pop序列中元素都被遍歷過
returnstack.empty()andpopIndex==popLen
if__name__=="__main__":
push="12345"
pop="32541"
ifisPopSerial(push,pop):
printpop+"是"+push+"的一個pop序列"
else:
printpop+"不是"+push+"的一個pop序列"
程序的運行結(jié)果為:
32541是12345的一個pop序列
算法性能分析:
這種方法在處理一個合理的pop序列的時候需要操作的次數(shù)最多,即把push序列進行一次壓棧和出棧操作,操作次數(shù)為2N,因此,時間復雜度為O(N),此外,這種方法使用了額外的??臻g,因此,空間復雜度為O(N)。[考點]如何根據(jù)入棧序列判斷可能的出棧序列
5.
如何用O(1)的時間復雜度求棧中最小元素正確答案:由于棧具有后進先出的特點,因此,push和pop只需要對棧頂元素進行操作。如果使用上述的實現(xiàn)方式,只能訪問到棧頂?shù)脑?,無法得到棧中最小的元素。當然,可以用另外一個變量來記錄棧底的位置,通過遍歷棧中所有的元素找出最小值,但是這種方法的時間復雜度為O(N),那么如何才能用O(1)的時間復雜度求出棧中最小的元素呢?
在算法設(shè)計中,經(jīng)常會采用空間換取時間的方式來提高時間復雜度,也就是說,采用額外的存儲空間來降低操作的時間復雜度。具體而言,在實現(xiàn)的時候使用兩個棧結(jié)構(gòu),一個棧用來存儲數(shù)據(jù),另外一個棧用來存儲棧的最小元素。實現(xiàn)思路如下:如果當前入棧的元素比原來棧中的最小值還小,則把這個值壓入保存最小元素的棧中;在出棧的時候,如果當前出棧的元素恰好為當前棧中的最小值,保存最小值的棧頂元素也出棧,使得當前最小值變?yōu)楫斍白钚≈等霔V暗哪莻€最小值。為了簡單起見,可以在棧中保存int類型。
實現(xiàn)代碼如下:
#模擬棧
classStack:
def__init__(self):
self.items=[]
#判斷棧是否為空
defempty(self):
returnlen(self.items)==0
#返回棧的大小
defsize(self):
returnlen(setf.items)
#返回棧頂元素
defpeek(self):
ifnotself.empty():
returnself.items[len(self.items)-1]
else:
returnNone
#彈棧
defpop(self):
iflen(self.
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權(quán)或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 生產(chǎn)線培訓新員工
- 2024兒童用藥安全
- 陜西省西安市新城區(qū)多校2023-2024學年三年級上學期月考英語試卷
- 電動車消防安全預防電動車火災培訓課件
- 天津市河東區(qū)2024-2025學年七年級上學期期中數(shù)學試卷(含答案)
- 山東省濱州市博興縣 2024-2025學年八年級上學期11月期中道德與法治試題(含答案)
- 2024-2025學年山東省日照市日照一中高二(上)第一次質(zhì)檢數(shù)學試卷(含答案)
- 江蘇省蘇州市2024-2025學年第一學期初三化學期中模擬測試卷(七)(含解析)
- 福建省南平市延平區(qū)多校2024-2025學年四年級上學期期中語文試題
- 信息技術(shù)(第2版)(拓展模塊) 教案 項目五 Web和FTP服務(wù)器的配置與管理
- 羅馬數(shù)字對照表
- 《口腔頜面部神經(jīng)》PPT課件.ppt
- 17電梯地坎標高確認表XLBG版本
- 2.2--金風1.5兆瓦風力發(fā)電機組測量傳感器與模塊
- 零星用工單(派工單)
- 關(guān)于初中英語學習的調(diào)查問卷
- 慢性阻塞性肺疾病臨床路徑
- 人身保險產(chǎn)品條款部分條目示范寫法規(guī)定
- CT的基本結(jié)構(gòu)和成像原理
- 《農(nóng)村集體經(jīng)濟組織會計》考試試卷
- 《晴天》歌詞精編版
評論
0/150
提交評論