前中後序
.和層序遍歷梳理一遍。隊列,遞歸,和棧
。這裏筆者都有進行過詳細介紹,能夠關注筆者數據結構與算法專欄。持續分享,共同窗習。有左右節點
。而每一層一層的遍歷都和左右節點有着很大的關係。也就是咱們選用的數據結構不能一股腦的往一個方向鑽,而
左右應該均衡考慮
。這樣咱們就選用
隊列來實現。
第二層
每一個執行的時候添加到隊列
,那麼添加的全部節點都在第二層後面
。pop遍歷第n層
的節點,每一個節點會push左右兩個節點進去
。可是隊列先進先出。它會放到隊尾(下一層
)。直到第n層的最後一個pop出來
,第n+1層的還在隊列中整齊排着。這就達到一個層序
的效果。實現的代碼也很容易理解:java
public void cengxu(node t) {//層序遍歷
Queue<node> q1 = new ArrayDeque<node>();
if (t == null)
return;
if (t != null) {
q1.add(t);
}
while (!q1.isEmpty()) {
node t1 = q1.poll();
if (t1.left != null)
q1.add(t1.left);
if (t1.right != null)
q1.add(t1.right);
System.out.print(t1.value + " ");
}
System.out.println();
}
複製代碼
其實這種就是一個相似dfs的思想。用遞歸實現。前面有很詳細的介紹遞歸算法。咱們採用的三序遍歷是採用同一個遞歸。而且你們也都直到遞歸是一個有來有回的過程。三序遍歷只是利用了遞歸中的來回過程當中不一樣片斷截取輸出,而達到前(中、後序遍歷的結果)。node
前序的規則就是根結點 ---> 左子樹 ---> 右子樹
.咱們在調用遞歸前進行節點操做。對於前序,就是先訪問(輸出)該節點。而遞歸左,遞歸右側,會優先遞歸左側。直到沒有左節點
。纔會中止。訪問次序大體爲: 算法
public void qianxu(node t)// 前序遞歸 前序遍歷:根結點 ---> 左子樹 ---> 右子樹 {
if (t != null) {
System.out.print(t.value + " ");// 當前節點
qianxu(t.left);
qianxu(t.right);
}
}
複製代碼
有了前序的經驗,咱們就很好利用遞歸實現中序遍歷。中序遍歷的規則是:左子樹---> 根結點 ---> 右子樹
。因此咱們訪問節點的順序須要變。數據結構
來回
的過程,對於剛好有兩個子節點(子節點無節點)的節點來講。只須要訪問一次左節點,訪問根,訪問右節點
。便可。一顆子樹
,也要知足中序遍歷要求
。因此就要先訪問左節點的左節點(若是存在)。那麼若是你這樣想,規則雖然懂了。可是也太複雜了。那麼咱們藉助遞歸。由於它的子問題和根節點的問題一致,只是範圍減少
了。因此咱們使用遞歸思想來解決。代碼爲:函數
public void zhongxu(node t)// 中序遍歷 中序遍歷:左子樹---> 根結點 ---> 右子樹 {
if (t != null) {
zhongxu(t.left);
System.out.print(t.value + " ");// 訪問完左節點訪問當前節點
zhongxu(t.right);
}
}
複製代碼
同理,有了前面的分析,後續就是左子樹 ---> 右子樹 ---> 根結點
學習
public void houxu(node t)// 後序遍歷 後序遍歷:左子樹 ---> 右子樹 ---> 根結點 {
if (t != null) {
houxu(t.left);
houxu(t.right);
System.out.print(t.value + " "); // 訪問玩左右訪問當前節點
}
}
複製代碼
遞歸有時候在效率
方面不是使人滿意的。 利用棧,咱們直到棧的順序爲現金先出。那麼順序如何添加?遞歸是左遞歸,右遞歸。可是利用棧要相反,由於若是左進棧、右進棧
會出現如下後果:
再右節點進棧,左節點進棧
。而後循環一直到最後會一直優先取到左節點。達到和遞歸順序相仿效果。
public void qianxu3(node t)// 非遞歸前序 棧 先左後右 t通常爲root {
Stack<node> q1 = new Stack<node>();
if (t == null)
return;
if (t != null) {
q1.push(t);
}
while (!q1.empty()) {
node t1 = q1.pop();
if (t1.right != null) {
q1.push(t1.right);
}
if (t1.left != null) {
q1.push(t1.left);
}
System.out.print(t1.value + " ");
}
}
複製代碼
方法二和非遞歸中序遍歷的方法相似,只不過須要修改輸出時間,在進棧時候輸入訪問節點便可。具體參考中序遍歷分析。測試
public void qianxu2(node t) {
Stack<node> q1 = new Stack();
while(!q1.isEmpty()||t!=null)
{
if (t!=null) {
System.out.print(t.value+" ");
q1.push(t);
t=t.left;
}
else {
t=q1.pop();
t=t.right;
}
}
}
複製代碼
非遞歸中序和前序有所區別。 咱們直到中序排列的順序是:左節點,根節點,右節點
。那麼咱們在通過根節點的前面節點 不能釋放, 由於後面還須要用到它。因此要用棧先儲存
。 它的規則大體爲:spa
依次存入左節點全部點
,直到最左側在棧頂。拋出棧頂並訪問
。(例如第一個拋出2)。若是有右節點。那麼將右節點加入棧中
,而後右節點一致左下遍歷直到尾部。(這裏5和7沒有左節點,因此不加)可是若是拋出15
。右節點加入23
.再找23的左側節點加入棧頂。就這樣循環下去直到棧爲空
。可行性分析:中序是左—中—右
的順序。訪問完左側。當拋出當前點的時候說明左側已經訪問完(或者本身就是左側),那麼須要首先訪問當前點的右側。那麼這個右節點把它當成根節點重複相同操做
(由於右節點要知足先左再右的順序)。這樣其實就是模擬了一個遞歸的過程,須要本身思考。 .net
public void zhongxu2(node t) {
Stack<node> q1 = new Stack();
while(!q1.isEmpty()||t!=null)
{
if (t!=null) {
q1.push(t);
t=t.left;
}
else {
t=q1.pop();
System.out.print(t.value+" ");
t=t.right;
}
}
}
複製代碼
實現代碼2:(我的首次寫的)3d
public void zhongxu3(node t)// 先儲藏全部左側點,拋出一個點,訪問該點右節點,對右節點在儲存全部子左節點 {
Stack<node> q1 = new Stack();
if (t == null)
return;
if (t != null) {
q1.push(t);
}
node t1 = q1.peek();// 不能拋出,要先存最左側
while (t1.left != null) {
t1 = t1.left;
q1.push(t1);
}
while (!q1.isEmpty()) {
node t2 = q1.pop();
System.out.print(t2.value + " ");
if (t2.right != null) {
t2 = t2.right;
q1.push(t2);
while (t2.left != null) {
t2 = t2.left;
q1.push(t2);
}
}
}
}
複製代碼
非遞歸後序遍歷有兩種方法 一種方法是利用和前面中序、前序第二種方法相似的方法進入壓棧出棧,可是要藉助額外的標記次數
,一個節點訪問第二次才能輸出。(這個訪問第一次是入棧
,第二次是子樹解決完畢本身即將出棧
(先不出棧))。
在前面的前序和中序先到最左側壓入棧的時候,兩種順序依次是
中入棧
——>左入棧
——>左出棧——>中出棧——>右入棧
——>右孩子入出——>右出棧 在入棧時候操做便可前序左出棧
——>中出棧
——>右入棧 ——>右孩子入出——>右出棧
按照出棧順序便可完成中序而在後序遍歷中:它有這樣的規則:
即將出棧
。第二次訪問,public void houxu2(node t) {
Stack<node> q1 = new Stack();
Map<Integer,Integer >map=new HashMap<>();
while(!q1.isEmpty()||t!=null)
{
if (t!=null) {
q1.push(t);
map.put(t.value, 1); //t.value標記這個值節點出現的次數
t=t.left;
}
else {
t=q1.peek();
if(map.get(t.value)==2) {//第二次訪問,拋出
q1.pop();
System.out.print(t.value+" ");
t=null;//須要往上走
}
else {
map.put(t.value, 2);
t=t.right;
}
}
}
}
複製代碼
另外一種方法是藉助雙棧進行處理。咱們曾在前序方法一
藉助一個棧右壓,左壓。持續讓達到一個前序遍歷的效果。可是這個方法很難實現後續。
先壓左,再壓右
,那麼咱們得到的順序將是和前序徹底相反的順序(順序爲:中間,右側,左側
。倒過來恰好是左側、右側、中間的後續)對稱看起來的前序。即用另外一個棧將序列進行反轉順序
!
public void houxu3(node t)// q1和q2 q1要先右後左,先遍歷右側,q1先裝右側就把右側放到前面,左側放在上面(棧頂) {
Stack<node> q1 = new Stack();
Stack<node> q2 = new Stack();
if (t == null)
return;
if (t != null) {
q1.push(t);
}
while (!q1.isEmpty()) {
node t1 = q1.pop();
q2.push(t1);
if (t1.left != null) {
q1.push(t1.left);
}
if (t1.right != null) {
q1.push(t1.right);
}
}
while (!q2.isEmpty()) {
node t1 = q2.pop();
System.out.print(t1.value + " ");
}
}
複製代碼
測試結果:
bigsai
)。筆者認真更新數據結構與算法
。有興趣能夠關注一波學一塊兒學習。回覆數據結構或者爬蟲有精心準備學習資料贈送。