国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

C++數據結構學習:二叉樹(2)

2019-11-17 05:04:14
字體:
來源:轉載
供稿:網友
  線索化二叉樹

  這是數據結構課程里第一個碰到的難點,不知道你是不是這樣看,反正我當初是費了不少腦細胞——當然,惱人的矩陣壓縮和相關的加法乘法運算不在考慮之列。
我費了不少腦細胞是因為思考:他們干什么呢?很欣喜的看到在這本黃皮書上,這章被打了*號,雖然我不確定作者是不是跟我一個想法——線索化二叉樹在現在的PC上是毫無用處的!——不知我做了這個結論是不是會被人罵死,^_^。

  為了證實這個結論,我們來看看線索化二叉樹提出的緣由:第一,我們想用比較少的時間,尋找二叉樹某一個遍歷線性序列的前驅或者后繼。當然,這樣的操作很頻繁的時候,做這方面的改善才是有意義的。第二,二叉樹的葉子節點還有兩個指針域沒有用,可以節省內存。說真的,提出線索化二叉樹這樣的構思真的很精巧,完全做到了“廢物利用”——這個人真應該投身環保事業。但在計算機這個死板的東西身上,人們的精巧構思往往都是不能實現的——為了速度,計算機的各個部件都是整潔劃一的,而構思的精巧往往都是建立在組成的復雜上的。

  我們來看看線索化二叉樹究竟能不能達到上面的兩個目標。

  求遍歷后的線性序列的前驅和后繼。前序線索化能依次找到后繼,但是前驅需要求雙親;中序線索化前驅和后繼都不需要求雙親,但是都不很直接;后序線索化能依次找到前驅,但是后繼需要求雙親。可以看出,線索化成中序是最佳的選擇,基本上算是達到了要求。

  節省內存。添加了兩個標志位,問題是這兩個位怎么儲存?即使是在支持位存儲的CPU上,也是不能拿位存儲器來存的,第一是因為結構體成員的地址是在一起的,第二是位存儲器的數目是有限的。因此,最少需要1個字節來儲存這兩個標志位。而為了速度和移植,一般來說,內存是要對齊的,實際上根本就沒節省內存!然而,當這個空間用來儲存雙親指針時,帶來的方便絕對不是線索化所能比擬的,前面已經給出了無棧的非遞歸遍歷。并且,在線索化二叉樹上插入刪除操作附加的代價太大。

  綜上,線索化最好是中序線索化(前序后序線索化后還得用棧,何必要線索化呢),附加的標志域空間至少1個字節,在32位的CPU會要求對齊到4字節,還不如存儲一個雙親指針,同樣能達到中序線索化的目的,并且能帶來其他的好處。所以,線索化二叉樹在現在的PC上是毫無用處的!
更多文章 更多內容請看C/C++技術專題  數據結構  數據結構教程專題,或   由于對其他體系不太了解,以下觀點姑妄聽之。在內存空間非常充裕的現在,一個節點省2~3個字節實在是沒什么意思(實際上由于對齊還省不出來);而在內存非常寶貴的地方(比如單片機),會盡量避免使用樹結構——利用其他的方法。所以,現在看來,線索化二叉樹真的是毫無用處了。


  二叉搜索樹
  這恐怕是二叉樹最重要的一個應用了。它的構想實際是個很自然的事情——查找值比當前節點小轉左,大轉右,等則查到,到頭了就是沒找著。越自然的東西越好理解,也就越不需要我廢話。在給出BST的實現之前,我們要在二叉樹的類中添加一個打印樹狀結構的成員函數,這樣,就能清楚的看出插入和刪除過程。

  public:
  
  void
  BTNode*p=root;BTNodezero;boolv=true;

  inti=1,level=0,h=height();

  while(i<2<
  {

  if(i==1<
  {


  cout<
  if(v)cout
  elsecout<<'';
  
  }

  else
  
  {
  
  cout<  
  if(v)cout  
  elsecout<<"";
  
  }
  
  if(p->left){a.push(p->left);flag.push(true);}
  
  else{a.push(&zero);flag.push(false);}
  
  if(p->right){a.push(p->right);flag.push(true);}
  
  else{a.push(&zero);flag.push(false);}
  
  p=a.front();a.pop();v=flag.front();flag.pop();i++;
  
  }
  
  cout<  
  }
  更多文章 更多內容請看C/C++技術專題  數據結構  數據結構教程專題,或  
  打印樹狀結構的核心是按層次遍歷二叉樹,但是,二叉樹有許多節點缺左或右子樹,連帶的越到下面空隙越大。
為了按照樹的結構打印,必須把二叉樹補成完全二叉樹,這樣下面的節點就知道放在什么位置了——a.push(&zero);但是這樣的節點不能讓它打印出來,所以對應每個節點,有一個是否打印的標志,按理說pair結構很合適,為了簡單我用了并列的兩個隊列,一個放節點指針——a,一個放打印標志——flag。這樣一來,循環結束的標志就不能是隊列空——永遠都不可能空,碰到NULL就補一個節點——而是變成了到了滿二叉樹的最后一個節點2^(height+1)-1。——黃皮書對于樹高的定義是,空樹為的高度為-1。
  
  對于輸出格式,注重的是到了第1、2、4、8號節點要換行,并且在同一行中,第一個節點的域寬是后序節點的一半。上面的函數在樹的層次少于等于5(height<=4)的時候能正常顯示,再多的話就必須輸出到文件中去ofstreamoutfile("out.txt");——假如層次再多的話,打印出來也沒什么意義了。
  
  二叉搜索樹的實現
  實際上就是在二叉樹的基礎上增加了插入、刪除、查找。
  
  #include"BaseTree.h"
  
  template
  
  classBSTree:publicBTree
  
  {
  
  public:
  
  BTNode*&find(constT&data)
  
  {
  
  BTNode**p=&root;current=NULL;
  
  while(*p)
  
  {
  
  if((*p)->data==data)break;
  
  if((*p)->dataright);}
  
  else{current=*p;p=&((*p)->left);}
  
  }
  
  return*p;
  
  }
  
  boolinsert(constT&data)
  
  {
   更多文章 更多內容請看C/C++技術專題  數據結構  數據結構教程專題,或   BTNode*&p=find(data);if(p)returnfalse;
  
  p=newBTNode(data,NULL,NULL,current);returntrue;
  
  }
  
  boolremove(constT&data)
  
  {
  
  returnremove(find(data));
  
  }
  
  private:
  

  boolremove(BTNode*&p)
  
  {
  
  if(!p)returnfalse;BTNode*t=p;
  
  if(!p->left!p->right)
  
  {
  
  if(!p->left)p=p->right;elsep=p->left;
  
  if(p)p->parent=current;
  
  deletet;returntrue;
  
  }
  
  t=p->right;while(t->left)t=t->left;p->data=t->data;current=t->parent;
  
  returnremove(current->left==t?current->left:current->right);
  
  }
  
  };
  
  以上代碼有點費解,有必要說明一下——非線性鏈式結構操作的實現都是很讓人費神。insert和remove都是以find為基礎的,因此必須讓find能最大限度的被這兩個操作利用。
   更多文章 更多內容請看C/C++技術專題  數據結構  數據結構教程專題,或
  對于insert,需要修改查找失敗時的指針內容,顯然這是個內部指針(在雙親節點的內部,而不是象root和current那樣在節點外面指向節點),這就要求find返回一個內部指針的引用。但是C++的引用綁定到一個對象之后,就不能再改變了,因此在find內部的實現是一個二重指針。insert操作還需要修改插入的新節點的parent指針域,因此在find中要產生一個能被insert訪問的指向find返回值所在節點的指針,這里用的是current。實際上find返回的指針引用不是current->left就是current->right。這樣一來,insert的實現就非常簡單了。
  
  對于remove,需要修改查找成功時的指針內容,同樣是個內部指針。在find的基礎上,很輕易就能得到這個內部指針的引用(BTNode*&p=find(data)。
  
  在p->left和p->right中至少有一個為NULL的情況下,假如p->left==NULL,那么就重連右子樹p=p->right,反之,重連左子樹p=p->left。注重,左右子樹全空的情況也包含在這兩個操作中了——在p->left==NULL的時候重連右子樹,而這時p->right也是NULL——因此不必列出來。假如重連后p不為空,需要修改p->parent=current。
  
  若p->left和p->right都不為空,可以轉化為有一個為空。例如一個中序有序序列[1,2,3,4,5],假設3既有左子樹又有右子樹,那么它的前驅2一定缺右子樹,后繼4一定缺少左子樹。【注1】這樣一來刪除節點3就等效成從[1,2,3(4),4,5]刪除節點4。這樣就可以利用上面的在p->left和p->right中至少有一個為NULL的情況下的方法了。還是由于C++的引用不能改變綁定對象,這里是用利用遞歸來解決的,還好最多只遞歸一次。假如用二重指針又是滿天星星了,這就是明明是尾遞歸卻沒有消去的原因。
  
  【注1】這是因為,假如3既有左子樹又有右子樹,那么2一定在3的左子樹上,4一定在3的右子樹上;假如2有右子樹,那么在2和3之間還應該有一個節點;假如4有左子樹,那么3和4之間也應該還有一個節點。
  
  【閑話】上面關于remove操作p->left和p->right都不為空的處理方法的講解,源于嚴蔚敏老師的課件,看完后我豁然開朗,真不知道為什么她自己那本《數據結構(C語言版)》這里寫的那么難懂,我是死活沒看明白。
更多文章 更多內容請看C/C++技術專題  數據結構  數據結構教程專題,或

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 通化市| 赫章县| 嘉黎县| 龙川县| 绵竹市| 固阳县| 泌阳县| 景洪市| 定兴县| 武冈市| 台中县| 临沭县| 包头市| 余江县| 毕节市| 翁牛特旗| 漯河市| 泾川县| 佳木斯市| 收藏| 错那县| 临沂市| 怀来县| 东平县| 牙克石市| 荆州市| 苍南县| 长寿区| 宁远县| 蒲江县| 通渭县| 察雅县| 茂名市| 陇川县| 芦山县| 寻乌县| 张北县| 土默特左旗| 南涧| 阿瓦提县| 大关县|