亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C > 正文

C語言二叉樹的三種遍歷方式的實現及原理

2020-01-26 13:26:18
字體:
來源:轉載
供稿:網友

二叉樹遍歷分為三種:前序、中序、后序,其中序遍歷最為重要。為啥叫這個名字?是根據根節點的順序命名的。

比如上圖正常的一個滿節點,A:根節點、B:左節點、C:右節點,前序順序是ABC(根節點排最先,然后同級先左后右);中序順序是BAC(先左后根最后右);后序順序是BCA(先左后右最后根)。

 

比如上圖二叉樹遍歷結果

    前序遍歷:ABCDEFGHK

    中序遍歷:BDCAEHGKF

    后序遍歷:DCBHKGFEA

分析中序遍歷如下圖,中序比較重要(java很多樹排序是基于中序,后面講解分析)


下面介紹一下,二叉樹的三種遍歷方式,其中每一種遍歷方式都有三種實現方式。

節點定義:

struct TreeNode{  int val;  TreeNode *left,*right;  TreeNode(int val){    this->val = val;    this ->left = this->right = NULL;  }};

先序遍歷

以上面這張圖為例:我們講講樹的三種遍歷方式:

先序遍歷:先訪問根節點,然后訪問左孩子,最后訪問右孩子。

所以,上面遍歷的結果是:GEDACHS。

下面,我們來看看具體代碼實現

1.遞歸實現

void preOrder(TreeNode *root){  if (root==NULL)    return;  cout<<root->val<<endl;  preOrder(root->left);  preOrder(root->right);}

2.使用輔助?!?/strong> 

    實現思路:1.將根節點入棧
       2.每次從棧頂彈出一個節點,訪問該節點
       3.把當前節點的右孩子入棧
       4.把當前節點的左孩子入棧

  具體實現:

void preOrder2(TreeNode *root){  if (root == NULL)    return;  stack<TreeNode*> stk; //開辟一個??臻g  stk.push(root);  while(!stk.empty()){    TreeNode* pNode = stk.pop();    cout<<pNode->val;    if (pNode->right!=NULL)      stk.push(pNode->right);    if (pNode->left!=NULL)      stk.push(pNode->left);  }}

3.Morris遍歷

Morris遍歷,常數的空間即可在O(n)時間內完成二叉樹的遍歷。

O(1)空間進行遍歷困難之處在于在遍歷的子結點的時候如何重新返回其父節點?

在Morris遍歷算法中,通過修改葉子結點的左右空指針來指向其前驅或者后繼結點來實現的。

其本質:線索二叉樹(Threaded Binary Tree),通過利用葉子節點空的right指針,指向中序遍歷的后繼節點,從而避免了對 stack 的依賴。

具體實現:

void preOrder(TreeNode* root){  if (root == NULL)    return;  TreeNode* pNode = root;  while(pNode != NULL){    if (pNode->left == NULL)    {      cout<<pNode->val<<endl;      pNode = pNode->right;    }    else{      TreeNode* pPre = pNode->left;      while(pPre->right != NULL && pPre->right != pNode){        pPre = pPre->right;      }      if (pPre->right == NULL)      {        /* code */        pPre->right = pNode;        cout<<pNode->val<<endl;        pNode = pNode->left;      }      else{        pPre->right = NULL;        pNode = pNode->right;      }    }  }}

中序遍歷

中序遍歷:先訪問左孩點,然后訪問根節點,最后訪問右孩子。

所以,上面遍歷的結果是:DEAGHCS。

下面,我們來看看具體代碼實現

1.遞歸實現

void InOrder(TreeNode *root){  if (root==NULL)    return;  InOrder(root->left);  cout<<root->val<<endl;  InOrder(root->right);}

2.使用輔助棧

實現思路:

初始化一個二叉樹結點pNode指向根結點;

若pNode非空,那么就把pNode入棧,并把pNode變為其左孩子;(直到最左邊的結點)

若pNode為空,彈出棧頂的結點,并訪問該結點,將pNode指向其右孩子(訪問最左邊的結點,并遍歷其右子樹)

具體實現:

void InOrder(TreeNode *root){  if (root==NULL)  {    return;  }  stack<TreeNode*> stk;  TreeNode *pNode = root;  while(pNode!=NULL || !stk.empty()){    if (pNode != NULL)    {      stk.push(pNode);      pNode = pNode->left;    }    else{      pNode = stk.pop();      stk.pop();      cout<<pNode->val<<endl;      pNode = pNode->right;    }  }}

3.Morris遍歷

實現思路:

1.如果當前節點pNode的左孩子為空,那么輸出該節點,并把該節點的右孩子作為當前節點

2.如果當前節點pNode的左孩子非空,那么找出該節點在中序遍歷的前驅結點prev

當第一次訪問該前驅結點prev時,其右孩子必定為空,那么就將其右孩子設置為當前結點,以便根據這個指針返回到當前結點pNode中,并將當前結點pNode設置為其左孩子;  

當該前驅結點pPre的右孩子為當前結點,那么就輸出當前結點,并把前驅結點的右孩子設置為空(恢復樹的結構),將當前結點更新為當前結點的右孩子;

3.重復以上兩步,直到當前結點為空。

具體實現:

void InOrder(TreeNode *root){  if (root == NULL)    return;  TreeNode* pNode = root;  while(pNode != NULL){    if (pNode->left == NULL)    {      cout<<pNode->val<<endl;      pNode = pNode->right;    }    else{      TreeNode* pPre = pNode->left;      while(pPre->right != NULL && pPre->right != pNode){        pPre = pPre->right;      }      if (pPre->right == NULL)      {        /* code */        pPre->right = pNode;        pNode = pNode->left;      }      else{        pPre->right = NULL;        cout<<pNode->val<<endl;        pNode = pNode->right;      }    }  }}

后序遍歷

后序遍歷:先訪問左孩子,然后訪問右孩子,最后訪問根節點。

所以,上面遍歷的結果是:DAEHSCG。

下面,我們來看看具體代碼實現:

1.遞歸實現

void PostOrder(TreeNode *root){  if (root==NULL)    return;  PostOrder(root->left);  PostOrder(root->right);  cout<<root->val<<endl;}

2.使用輔助棧

void postOrder(TreeNode *root) {   if(root == NULL)    return;  stack<TreeNode *> stk;  stk.push(root);  TreeNode *prev = NULL;  while(!stk.empty()) {    TreeNode *pNode = stk.top();    if(!prev || prev->left == pNode || prev->right == pNode) { // traverse down      if(pNode->left)        stk.push(pNode->left);      else if(pNode->right)        stk.push(pNode->right);     /* else {        cout << pNode->val << endl;        stk.pop();      }    */    }    else if(pNode->left == prev) { // traverse up from left      if(pNode->right)        stk.push(pNode->right);    }  /* else if(pNode->right == prev) { // traverse up from right        cout << pNode->val << endl;        stk.pop();    }  */    else {      cout << pNode->val << endl;      stk.pop();    }    prev = pNode;  }}

雙輔助棧實現思路:  

  • 設置兩個棧stk, stk2;
  • 將根結點壓入第一個棧stk;
  • 彈出stk棧頂的結點,并把該結點壓入第二個棧stk2;
  • 將當前結點的左孩子和右孩子先后分別入棧stk;
  • 當所有元素都壓入stk2后,依次彈出stk2的棧頂結點,并訪問之。
  • 第一個棧的入棧順序是:根結點,左孩子和右孩子;于是,壓入第二個棧的順序是:根結點,右孩子和左孩子。

因此,彈出的順序就是:左孩子,右孩子和根結點。

void PostOrder2(TreeNode *root){ //兩個棧實現  if (root == NULL)    return;  stack<TreeNode*> stk,stk2;  stk.push(root);  while(!stk.empty()){    TreeNode* pNode = stk.top();    stk.pop();    stk2.push(pNode);// 將根節點壓棧    if (pNode->left != NULL) // 如果左孩子不為空,則壓棧    {      stk.push(pNode->left);    }    if (pNode->right != NULL) // 如果左孩子不為空,則壓棧    {      stk.push(pNode->right);    }  }  while(!stk2.empty()){    cout<<stk2.top()->val<<endl;    stk2.pop();  }}

3.Morris遍歷實現

實現思路:

1.先建立一個臨時結點dummy,并令其左孩子為根結點root,將當前結點設置為dummy;

2.如果當前結點的左孩子為空,則將其右孩子作為當前結點;

3.如果當前結點的左孩子不為空,則找到其在中序遍歷中的前驅結點,

  • -如果前驅結點的右孩子為空,將它的右孩子設置為當前結點,將當前結點更新為當前結點的左孩子;
  • -如果前驅結點的右孩子為當前結點,倒序輸出從當前結點的左孩子到該前驅結點這條路徑上所有的結點。將前驅結點的右孩子設置為空,將當前結點更新為當前結點的右孩子。

4.重復以上過程,直到當前結點為空。

具體實現:

void reverse(TreeNode* p1,TreeNode *p2){  if (p1 == p2)    return;  TreeNode* x = p1;  TreeNode* y = p1->right;  while(true){    TreeNode* tmp = y->right;    y->right = x;    x = y;    y = tmp;    if (x == p2)      break;  }}
void printReverse(TreeNode* p1,TreeNode *p2){  reverse(p1,p2);  TreeNode* pNode = p2;  while(true){    cout<<pNode->val<<endl;    if (pNode == p1)      break;    pNode = pNode->right;  }  reverse(p2,p1);}
void PostOrder3(TreeNode* root){  if(root == NULL)    return;  TreeNode *dummy = new TreeNode(-1);  dummy->left = root;  TreeNode *pNode = dummy;  while(pNode != NULL) {    if(pNode->left == NULL)      pNode = pNode->right;    else {      TreeNode *pPrev = pNode->left;      while(pPrev->right != NULL && pPrev->right != pNode)        pPrev = pPrev->right;      if(pPrev->right == NULL) {        pPrev->right = pNode;        pNode = pNode->left;      }      else {        printReverse(pNode->left, pPrev);        pPrev->right = NULL;        pNode = pNode->right;      }    }  }}

總結

上述三種遍歷方式時間復雜度和空間復雜度分析:

1.遞歸遍歷和非遞歸遍歷 時間復雜度0(n) 空間復雜度O(n)

2.Morris遍歷 時間復雜度0(n) 空間復雜度O(1)

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美在线影院在线视频| 日韩在线播放一区| 欧美日韩国产中文精品字幕自在自线| 萌白酱国产一区二区| 91精品国产综合久久香蕉| 欧美在线视频播放| 欧美日韩国产中文精品字幕自在自线| 日韩一区二区三区xxxx| 国产精品久久婷婷六月丁香| 奇米成人av国产一区二区三区| 欧美成人精品不卡视频在线观看| 亚洲欧美第一页| 97在线观看视频国产| 精品亚洲一区二区三区在线播放| 成人在线小视频| 久久69精品久久久久久久电影好| 国产91精品久久久久| 97人人做人人爱| 久久综合伊人77777蜜臀| 亚洲一区制服诱惑| 97精品在线视频| 91精品视频播放| 国产亚洲欧美aaaa| 日本成人在线视频网址| 亚洲专区国产精品| 视频在线一区二区| 国产精品久久久久久久久久| 国产香蕉97碰碰久久人人| 国产精品福利片| 日韩免费观看在线观看| 亚洲欧美在线免费观看| 欧美激情在线播放| 亚洲最新在线视频| 久久成人av网站| 国产视频久久久久| 奇米4444一区二区三区| 日本一区二区三区在线播放| 97在线视频国产| 成人写真视频福利网| 亚洲欧美中文在线视频| 国产第一区电影| 欧美成人免费va影院高清| 美女福利视频一区| 亚洲系列中文字幕| 国产精品欧美激情在线播放| 亚洲无限乱码一二三四麻| 国产啪精品视频网站| 日韩av在线播放资源| 欧美制服第一页| 日韩精品免费看| 成人网中文字幕| 一区二区在线免费视频| 国内精品久久久久久中文字幕| 精品夜色国产国偷在线| 亚洲欧美日韩精品| 日韩精品一二三四区| 91久久久久久久久久久| 欧美性xxxx极品hd欧美风情| 亚洲第一中文字幕在线观看| 亚洲福利在线播放| 在线观看欧美成人| 国产精品午夜视频| 色综合天天狠天天透天天伊人| 欧美午夜性色大片在线观看| 亚洲精品国产suv| 亚洲国产成人久久| 欧美日韩亚洲视频一区| 亚洲精品成人久久电影| 黑人欧美xxxx| 欧美国产日本在线| 国产精品扒开腿做爽爽爽视频| 97久久超碰福利国产精品…| 人人澡人人澡人人看欧美| 国产精品久久久久久久久久久不卡| 亚洲天堂免费观看| 91免费欧美精品| 精品视频久久久久久| 欧美激情亚洲另类| 正在播放欧美一区| 在线观看视频99| 亚洲精品自拍偷拍| 国内精久久久久久久久久人| 欧美激情精品久久久久久免费印度| 奇米4444一区二区三区| 伊人男人综合视频网| 精品magnet| 91精品久久久久久久久久久久久| 亚洲乱亚洲乱妇无码| 日韩av电影手机在线| 欧美日韩国产综合新一区| 国产91色在线播放| 亚洲第一免费播放区| 免费91在线视频| 亚洲精品国产拍免费91在线| 欧美福利小视频| 日韩av网站在线| 国产精品午夜视频| 久久中国妇女中文字幕| 91精品啪在线观看麻豆免费| 欧美风情在线观看| 中文字幕精品久久| 欧美激情免费看| 91香蕉电影院| 亚洲高清一二三区| 久久久久久久国产精品| 欧美激情网友自拍| 成人免费看片视频| 国产精品十八以下禁看| 国产视频久久久久久久| 国产日韩中文字幕| 国产欧美一区二区三区四区| 亚洲精品国精品久久99热一| 日本免费一区二区三区视频观看| 亚洲福利在线观看| 日韩大片在线观看视频| 久久免费视频在线| 91精品国产综合久久香蕉最新版| 亚洲老头同性xxxxx| 欧美日韩美女在线观看| 欧美激情视频在线免费观看 欧美视频免费一| 欧美激情一区二区三区在线视频观看| 在线亚洲午夜片av大片| 亚洲第一天堂av| 国产精品一区二区女厕厕| 91精品久久久久久久久久另类| 亚洲a∨日韩av高清在线观看| 国产裸体写真av一区二区| 欧美成年人在线观看| 色偷偷888欧美精品久久久| 日韩亚洲一区二区| 中文在线不卡视频| 久久久91精品国产| 久久精品欧美视频| 欧美中文在线字幕| 亚洲片av在线| 久久99热精品| 国产视频精品xxxx| 欧美国产极速在线| 国产精品69久久| 国产精品成人aaaaa网站| 亚洲欧洲av一区二区| 国产一区二区三区视频免费| 欧美日韩国产中文字幕| 91精品久久久久久久久久入口| 久久久久久久香蕉网| 亚洲欧美日本精品| 中文字幕欧美亚洲| 日本久久久久久| 国产成人免费av| 欧美成人激情视频| 日韩中文字幕在线播放| 国产精品一区二区三区毛片淫片| 欧美国产日韩一区二区三区| 97人人做人人爱| 国产精品视频一区二区高潮| 国产福利精品av综合导导航| 久久黄色av网站| 亚洲视频专区在线| 亚洲综合国产精品| 国产欧美一区二区白浆黑人| 亚洲精品99久久久久中文字幕| 色yeye香蕉凹凸一区二区av| 国产精品亚洲一区二区三区| 97成人精品视频在线观看|