最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

java二叉樹的幾種遍歷遞歸與非遞歸實(shí)現(xiàn)代碼

 更新時(shí)間:2020年12月04日 18:45:44   作者:zlp1992  
這篇文章主要介紹了java二叉樹的幾種遍歷遞歸與非遞歸實(shí)現(xiàn)代碼,需要的朋友可以參考下

前序(先序)遍歷
中序遍歷
后續(xù)遍歷
層序遍歷

如圖二叉樹:

二叉樹結(jié)點(diǎn)結(jié)構(gòu)

public class TreeNode {
  int val;
  TreeNode left;
  TreeNode right;
  TreeNode(int x){
    val=x;
  }
  @Override
  public String toString(){
    return "val: "+val;
  }
}

訪問函數(shù)

  public void visit(TreeNode node){
    System.out.print(node.val+" ");
  }

前序遍歷

對(duì)于圖中二叉樹而言其前序遍歷結(jié)果為:6 2 0 1 4 5 8 9
二叉樹的前序遍歷即先遍歷根結(jié)點(diǎn)再遍歷左結(jié)點(diǎn)最后遍歷右結(jié)點(diǎn),使用遞歸如下:

  /**
   * 遞歸先序遍歷
   * */
  public void preOrderRecursion(TreeNode node){
    if(node==null) //如果結(jié)點(diǎn)為空則返回
      return;
    visit(node);//訪問根節(jié)點(diǎn)
    preOrderRecursion(node.left);//訪問左孩子
    preOrderRecursion(node.right);//訪問右孩子
  }

非遞歸:

利用棧來(lái)實(shí)現(xiàn)二叉樹的先序非遞歸遍歷

  /**
   * 非遞歸先序遍歷二叉樹
   * */
  public List<Integer> preorderTraversal(TreeNode root) {
    List<Integer> resultList=new ArrayList<>();
    Stack<TreeNode> treeStack=new Stack<>();
    if(root==null) //如果為空樹則返回
      return resultList;
    treeStack.push(root);
    while(!treeStack.isEmpty()){
      TreeNode tempNode=treeStack.pop(); 
      if(tempNode!=null){
        resultList.add(tempNode.val);//訪問根節(jié)點(diǎn)
        treeStack.push(tempNode.right); //入棧右孩子
        treeStack.push(tempNode.left);//入棧左孩子
      }
    }
    return resultList;
  }

更新:評(píng)論里有人說(shuō)不理解非遞歸的先序遍歷,其實(shí)你舉個(gè)例子,然后畫個(gè)圖就可以理解了,以上圖中的二叉樹為例,先將6入棧,此時(shí)List為空,Stack只有一個(gè)元素6,進(jìn)入while循環(huán),彈出棧頂加入List,將6的右孩子和左孩子入棧,此時(shí)Stack從棧底到棧頂元素為8,2,List元素為6,由于棧不為空,進(jìn)入while循環(huán),彈出棧頂2,將2加入List,同時(shí)將2的右孩子和左孩子分別入棧,此時(shí)Stack從棧底到棧頂?shù)脑貫?,4,0, List的元素為6,2,由于棧不為空再次進(jìn)入while循環(huán)…依次下去,彈出0加入List,入棧1,null,此時(shí)Stack從棧底到棧頂為8,4,1,null,List為6,2,0,彈出null為空繼續(xù)彈出1,如此下去就可以了…

中序遍歷

對(duì)于二叉樹的中序遍歷,即先訪問左結(jié)點(diǎn)再訪問根節(jié)點(diǎn)最后訪問右結(jié)點(diǎn)

遞歸方法如下:

  /**
   * 遞歸中序遍歷
   * */
  public void preOrderRecursion(TreeNode node){
    if(node==null) //如果結(jié)點(diǎn)為空則返回
      return;
    preOrderRecursion(node.left);//訪問左孩子
    visit(node);//訪問根節(jié)點(diǎn)
    preOrderRecursion(node.right);//訪問右孩子
  }

非遞歸:

在上圖中的二叉樹,其中序遍歷為:0 1 2 4 5 6 8 9
可以看到,二叉樹的中序遍歷如下:
先將根節(jié)點(diǎn)入棧,
一直往其左孩子走下去,將左孩子入棧,直到該結(jié)點(diǎn)沒有左孩子,則訪問這個(gè)結(jié)點(diǎn),如果這個(gè)結(jié)點(diǎn)有右孩子,則將其右孩子入棧,重復(fù)找左孩子的動(dòng)作,這里有個(gè)要判斷結(jié)點(diǎn)是不是已經(jīng)被訪問的問題。
非遞歸中序遍歷(效率有點(diǎn)低),使用map(用set貌似更合理)來(lái)判斷結(jié)點(diǎn)是否已經(jīng)被訪問

  /**
   * 非遞歸中序遍歷
   * */
  public List<Integer> inorderTraversalNonCur(TreeNode root) {
    List<Integer> visitedList=new ArrayList<>();
    Map<TreeNode,Integer> visitedNodeMap=new HashMap<>();//保存已訪問的節(jié)點(diǎn)
    Stack<TreeNode> toBeVisitedNodes=new Stack<>();//待訪問的節(jié)點(diǎn)
    if(root==null)
      return visitedList;
    toBeVisitedNodes.push(root);
    while(!toBeVisitedNodes.isEmpty()){
      TreeNode tempNode=toBeVisitedNodes.peek(); //注意這里是peek而不是pop
      while(tempNode.left!=null){ //如果該節(jié)點(diǎn)的左節(jié)點(diǎn)還未被訪問,則需先訪問其左節(jié)點(diǎn)
        if(visitedNodeMap.get(tempNode.left)!=null) //該節(jié)點(diǎn)已經(jīng)被訪問(不存在某個(gè)節(jié)點(diǎn)已被訪問但其左節(jié)點(diǎn)還未被訪問的情況)
          break;
        toBeVisitedNodes.push(tempNode.left);
        tempNode=tempNode.left;
      }
      tempNode=toBeVisitedNodes.pop();//訪問節(jié)點(diǎn)
      visitedList.add(tempNode.val);
      visitedNodeMap.put(tempNode, 1);//將節(jié)點(diǎn)加入已訪問map
      if(tempNode.right!=null) //將右結(jié)點(diǎn)入棧
        toBeVisitedNodes.push(tempNode.right);
    }
    return visitedList;
  }

Discuss中有人給出更簡(jiǎn)潔的方法

public List<Integer> inorderTraversal(TreeNode root) {
  List<Integer> list = new ArrayList<Integer>();

  Stack<TreeNode> stack = new Stack<TreeNode>();
  TreeNode cur = root;

  while(cur!=null || !stack.empty()){
    while(cur!=null){
      stack.add(cur);
      cur = cur.left;
    }
    cur = stack.pop();
    list.add(cur.val);
    cur = cur.right;
  }

  return list;
}

后序遍歷

遞歸代碼就不貼了

如果之前的非遞歸中序遍歷使用map的方法理解后,后序遍歷的話我們也可以使用一個(gè)map來(lái)保存那些已經(jīng)被訪問的結(jié)點(diǎn),后序遍歷即先訪問左孩子再訪問右孩子最后訪問根結(jié)點(diǎn)。
非遞歸代碼:

  /**
   * 非遞歸后序遍歷
   * */
  public List<Integer> postOrderNonCur(TreeNode root){
    List<Integer> resultList=new ArrayList<>();
    if(root==null)
      return resultList;
    Map<TreeNode,Integer> visitedMap=new HashMap<>();
    Stack<TreeNode> toBeVisitedStack=new Stack<>();
    toBeVisitedStack.push(root);
    while(!toBeVisitedStack.isEmpty()){
      TreeNode tempNode=toBeVisitedStack.peek(); //注意這里是peek而不是pop
      if(tempNode.left==null && tempNode.right==null){ //如果沒有左右孩子則訪問
        resultList.add(tempNode.val);
        visitedMap.put(tempNode, 1);
        toBeVisitedStack.pop();
        continue;
      }else if(!((tempNode.left!=null&&visitedMap.get(tempNode.left)==null )|| (tempNode.right!=null && visitedMap.get(tempNode.right)==null))){
        //如果節(jié)點(diǎn)的左右孩子均已被訪問      
        resultList.add(tempNode.val);
        toBeVisitedStack.pop();
        visitedMap.put(tempNode, 1);
        continue;
      }
      if(tempNode.left!=null){
        while(tempNode.left!=null && visitedMap.get(tempNode.left)==null){//左孩子沒有被訪問
          toBeVisitedStack.push(tempNode.left);
          tempNode=tempNode.left;
        }
      }
      if(tempNode.right!=null){
        if(visitedMap.get(tempNode.right)==null){//右孩子沒有被訪問
          toBeVisitedStack.push(tempNode.right);
        }        
      }
    }
    return resultList;
  }

Discuss中有人給出了一個(gè)”巧“的方法,即先采用類似先序遍歷,先遍歷根結(jié)點(diǎn)再右孩子最后左孩子(先序是先根結(jié)點(diǎn)再左孩子最后右孩子),最后把遍歷的序列逆轉(zhuǎn)即得到了后序遍歷

public List<Integer> postorderTraversal(TreeNode root) {
  Deque<TreeNode> stack = new LinkedList<>();
  stack.push(root);
  List<Integer> ret = new ArrayList<>();
  while (!stack.isEmpty()) {
    TreeNode node = stack.pop();
    if (node != null) {
      ret.add(node.val);
      stack.push(node.left);
      stack.push(node.right);
    }
  }
  Collections.reverse(ret);
  return ret;
} 

層序遍歷

層序遍歷也即寬度優(yōu)先搜索,一層一層搜索,非遞歸代碼如下:

  public List<List<Integer>> levelOrder(TreeNode root) {
    List<List<Integer>> resultList=new ArrayList<>();
    int levelNum=0;//記錄某層具有多少個(gè)節(jié)點(diǎn)
    Queue<TreeNode> treeQueue=new LinkedList<>();
    treeQueue.add(root);
    while(!treeQueue.isEmpty()){
      levelNum=treeQueue.size();
      List<Integer> levelList=new ArrayList<>();
      while(levelNum>0){
        TreeNode tempNode=treeQueue.poll();
        if(tempNode!=null){
          levelList.add(tempNode.val);
          treeQueue.add(tempNode.left); 
          treeQueue.add(tempNode.right);
        }
        levelNum--;
      }
      if(levelList.size()>0) 
        resultList.add(levelList);
    }
    return resultList;    
  }

到此這篇關(guān)于java二叉樹的幾種遍歷遞歸與非遞歸實(shí)現(xiàn)代碼的文章就介紹到這了,更多相關(guān)java二叉樹內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建類操作示例

    Java實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建類操作示例

    這篇文章主要介紹了Java實(shí)現(xiàn)動(dòng)態(tài)創(chuàng)建類操作,結(jié)合完整示例形式分析了Java動(dòng)態(tài)創(chuàng)建類的具體步驟與相關(guān)操作技巧,需要的朋友可以參考下
    2020-02-02
  • Springboot集成ProtoBuf的實(shí)例

    Springboot集成ProtoBuf的實(shí)例

    這篇文章主要介紹了Springboot集成ProtoBuf的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • SpringBoot預(yù)防XSS攻擊的實(shí)現(xiàn)

    SpringBoot預(yù)防XSS攻擊的實(shí)現(xiàn)

    XSS攻擊是一種在web應(yīng)用中的計(jì)算機(jī)安全漏洞,它允許惡意web用戶將代碼植入到提供給其它用戶使用的頁(yè)面,本文主要介紹了SpringBoot預(yù)防XSS攻擊的實(shí)現(xiàn),感興趣的可以了解一下
    2023-08-08
  • springboot默認(rèn)掃描的路徑方式

    springboot默認(rèn)掃描的路徑方式

    這篇文章主要介紹了springboot默認(rèn)掃描的路徑方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Java深入講解異常處理try?catch的使用

    Java深入講解異常處理try?catch的使用

    這篇文章主要介紹了Java異常處理機(jī)制try?catch流程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2022-06-06
  • java制作復(fù)制文件工具代碼分享

    java制作復(fù)制文件工具代碼分享

    如果目標(biāo)位置沒有同名文件,則直接拷貝過去;如果目標(biāo)位置已有同名文件,則比對(duì)文件的最后修改日期,來(lái)進(jìn)行覆蓋或者忽略。程序會(huì)在可以在復(fù)制過程中自動(dòng)創(chuàng)建目錄,并生成log文件,創(chuàng)建了哪些目錄、文件,覆蓋了哪些文件、跳過了哪些文件,文件的時(shí)間、位置等信息都一目了然
    2014-01-01
  • java編譯器的基礎(chǔ)知識(shí)點(diǎn)

    java編譯器的基礎(chǔ)知識(shí)點(diǎn)

    在本篇文章里小編給大家整理的是一篇關(guān)于java編譯器的基礎(chǔ)知識(shí)點(diǎn)內(nèi)容,有興趣的朋友們可以閱讀下。
    2020-02-02
  • Java AQS中ReentrantReadWriteLock讀寫鎖的使用

    Java AQS中ReentrantReadWriteLock讀寫鎖的使用

    ReentrantReadWriteLock稱為讀寫鎖,它提供一個(gè)讀鎖,支持多個(gè)線程共享同一把鎖。這篇文章主要講解一下ReentrantReadWriteLock的使用和應(yīng)用場(chǎng)景,感興趣的可以了解一下
    2023-02-02
  • java異步編程的7種實(shí)現(xiàn)方式小結(jié)

    java異步編程的7種實(shí)現(xiàn)方式小結(jié)

    異步處理的實(shí)現(xiàn)方式有很多種,常見多線程,消息中間件,發(fā)布訂閱的廣播模式,本文就詳細(xì)的介紹java異步編程的7種實(shí)現(xiàn)方式,感興趣的可以了解一下
    2023-03-03
  • Netty粘包問題的常見解決方案

    Netty粘包問題的常見解決方案

    粘包和拆包問題也叫做粘包和半包問題,它是指在數(shù)據(jù)傳輸時(shí),接收方未能正常讀取到一條完整數(shù)據(jù)的情況(只讀取了部分?jǐn)?shù)據(jù),或多讀取到了另一條數(shù)據(jù)的情況)就叫做粘包或拆包問題,本文介紹了Netty如何解決粘包問題,需要的朋友可以參考下
    2024-06-06

最新評(píng)論

伊宁市| 鹤峰县| 河西区| 阿城市| 睢宁县| 中西区| 大邑县| 正蓝旗| 昌都县| 普宁市| 望都县| 邵阳市| 湖州市| 当涂县| 西丰县| 肥东县| 祁连县| 岚皋县| 宜川县| 庆城县| 尖扎县| 江西省| 松江区| 东光县| 肥东县| 济阳县| 梁河县| 济源市| 且末县| 万安县| 南江县| 尼勒克县| 静安区| 高青县| 达孜县| 孝昌县| 城固县| 绍兴县| 根河市| 浮山县| 搜索|