兩種基本的資料結構
佇列的基本操作包括入隊enqueue和出隊dequeue,佇列有隊頭head和隊尾tail指標。元素總是從隊頭出,從隊尾入。採用陣列實現佇列時候,為了合理利用空間,可以採用迴圈實現佇列空間的有效利用。
關於棧和佇列的基本操作如下圖所示:
採用陣列簡單實現一下棧和佇列,實現佇列時候,長度為n的陣列最多可以含有n-1個元素,迴圈利用,這樣方便判斷佇列是空還是滿。程式如下所示:
View Code View Code測試結果如下所示:
問題:
(1)說明如何用兩個棧實現一個佇列,並分析有關佇列操作的執行時間。
解答:棧中的元素是先進後出,而佇列中的元素是先進先出。現有棧s1和s2,s1中存放佇列中的結果,s2輔助轉換s1為佇列。入佇列操操作:當一個元素入佇列時,先判斷s1是否為空,如果為空則新元素直接入s1,如果非空則將s1中所有元素出棧並存放到s2中,然後在將元素入s1中,最後將s2中所有元素出棧併入s1中。此時s1中存放的即是佇列入隊的順序。出隊操作:如果s1為空,則說明佇列為空,非空則s1出棧即可。入隊過程需要在s1和s2來回交換,執行時間為O(n),出隊操作直接是s1出棧執行時間為O(1)。舉例說明轉換過程,如下圖示:
我採用C++語言實現整程式如下:
View Code(2)說明如何用兩個佇列實現一個棧,並分析有關棧操作的執行時間。
解答:類似上面的題目,佇列是先進先出,而棧是先進後出。現有佇列q1和q2,q1中存放的是棧的結果,q2輔助q1轉換為棧。入棧操作:當一個元素如棧時,先判斷q1是否為空,如果為空則該元素之間入佇列q1,如果非空則將q1中的所有元素出隊併入到q2中,然後將該元素入q1中,最後將q2中所有元素出隊併入q1中。此時q1中存放的就是棧的如棧順序。出棧操作:如果q1為空,則棧為空,否則直接q1出隊操作。入棧操作需要在佇列q1和q2直接來來回交換,執行時間為O(n),出棧操作是佇列q1出隊操作,執行時間為O(1)。我用C++語言實現完整程式如下:
2、連結串列
連結串列與陣列的區別是連結串列中的元素順序是有各物件中的指標決定的,相鄰元素之間在實體記憶體上不一定相鄰。採用連結串列可以靈活地表示動態集合。連結串列有單鏈表和雙鏈表及迴圈連結串列。書中著重介紹了雙鏈表的概念及操作,雙鏈表L的每一個元素是一個物件,每個物件包含一個關鍵字和兩個指標:next和prev。連結串列的操作包括插入一個節點、刪除一個節點和查詢一個節點,重點來說一下雙向連結串列的插入和刪除節點操作,圖例如下:
連結串列是最基本的資料結構,凡是學計算機的必須的掌握的,在面試的時候經常被問到,關於連結串列的實現,百度一下就知道了。在此可以討論一下與連結串列相關的練習題。
(1)在單鏈表上插入一個元素,要求時間複雜度為O(1)。
解答:一般情況在連結串列中插入一元素是在末尾插入的,這樣需要從頭遍歷一次連結串列,找到末尾,時間為O(n)。要在O(1)時間插入一個新節點,可以考慮每次在頭節點後面插入,即每次插入的節點成為連結串列的第一個節點。
(2)在單鏈表上刪除一個給定的節點p,要求時間複雜度為O(1)。
解答:一般情況刪除一個節點時候,我們需要找到該節點p的前驅節點q,需要對連結串列進行遍歷,執行時間為O(n-1)。我們可以考慮先將q的後繼節點s的值替換q節點值,然後刪除s即可。如下圖刪除節點q的操作過程:
(3)單鏈表逆置,不允許額外分配儲存空間,不允許遞迴,可以使用臨時變數,執行時間為O(n)。
解答:這個題目在面試筆試中經常碰到,基本思想上將指標逆置。如下圖所示:
(4)遍歷單鏈表一次,找出連結串列中間節點。
解答:定義兩個指標p和q,初始都指向連結串列頭節點。然後開始向後遍歷,p每次移動2步,q移動一步,當p到達末尾的時候,p正好到達了中間位置。
(5)用一個單鏈表L實現一個棧,要求push和pop的操作時間為O(1)。
解答:根據棧中元素先進後出的特點,可以在連結串列的頭部進行插入和刪除操作。
(6)用一個單鏈表L實現一個佇列,要求enqueue和dequeue的操作時間為O(1)。
解答:佇列中的元素是先進先出,在單鏈表結構中增加一個尾指標,資料從尾部插入,從頭部刪除。
3、有根樹的表示
採用連結串列資料結構來表示樹,書中先降二叉樹的連結串列表示法,然後拓展到分支數無限制的有根數。先來看看二叉樹的連結串列表示方法,用域p、left和right來儲存指向二叉樹T中的父親、左孩子和右孩子的指標。如下圖所示:
對於分支數目無限制的有根樹,採用左孩子、右兄弟的表示方法。這樣表示的樹的每個節點都包含有一個父親指標p,另外兩個指標:
(1)left_child指向節點的最左孩子。
(2)right_sibling指向節點緊右邊的兄弟。