青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

【1】

這些天參與了CSDN論壇的討論,改變了我以前的一些看法。回頭看我以前的東西,我雖對這本書很不滿,但我還是按照它的安排在一點點的寫;這樣就導致了,我過多的在意書中的偏漏,我寫的更多是說“這本書怎樣”,而偏離了我寫這些的初衷——給正在學習數據結構的人一些幫助。正像我在前面所說的,雖然現有的教科書都不是很合理,但如果僅僅是抱怨這點,那無異于潑婦罵街。雖然本人的水平連初級都夠不上,但至少先從我做一點嘗試,以后這門課的教授方法必將一點點趨于合理。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

因此,后面不在按照書上的次序,將本著“實際應用(算法)決定數據結構”的思想來講解,常見教科書上有的,基本不再重點敘述(除了重點,例如AVL樹的平衡旋轉),——因此,在看本文的同時,一定要有一本教科書。這只是一個嘗試,希望大家多提寶貴意見。

因為現實世界中存在這“樹”這種結構——族譜、等級制度、目錄分類等等,而為了研究這類問題,必須能夠將樹儲存,而如何儲存將取決于所需要的操作。這里有個問題,是否允許存在空樹。有些書認為樹都是非空的,因為樹表示的是一種現實結構,而0不是自然數;我用過的教科書都是說可以有空樹,當然是為了和二叉樹統一。這個沒有什么原則上的差別,反正就是一種習慣。

二叉樹

二叉樹可以說是人們假想的一個模型,因此,允許有空的二叉樹是無爭議的。二叉樹是有序的,左邊有一個孩子和右邊有一個的二叉樹是不同的兩棵樹。做這個規定,是因為人們賦予了左孩子和右孩子不同的意義,在二叉樹的各種應用中,你將會清楚的看到。下面只講解鏈式結構??锤鞣N講數據結構的書,你會發現一個有趣的現象:在二叉樹這里,基本操作有計算樹高、各種遍歷,就是沒有插入、刪除——那樹是怎么建立起來的?其實這很好理解,對于非線性的樹結構,插入刪除操作不在一定的法則規定下,是毫無意義的。因此,只有在具體的應用中,才會有插入刪除操作。

節點結構

數據域、左指針、右指針肯定是必須的。除非很少用到節點的雙親,或者是資源緊張,建議附加一個雙親指針,這將會給很多算法帶來方便,尤其是在這個“空間換時間”的時代。

template <class T>

struct BTNode

{

    BTNode(T data = T(), BTNode<T>* left = NULL, BTNode<T>* right = NULL, BTNode<T>* parent = NULL)

        : data(data), left(left), right(right), parent(parent) {}

    BTNode<T> *left, *right, *parent;

    T data;

};

基本的二叉樹類

template <class T>

class BTree

{

public:

    BTree(BTNode<T> *root = NULL) : root(root) {}

    ~BTree() { MakeEmpty(); }

    void MakeEmpty() { destroy(root); root = NULL; }

protected:

    BTNode<T> *root;

private:

    void destroy(BTNode<T>* p)

    {

        if (p)

        {

            destroy(p->left);

            destroy(p->right);

            delete p;

        }

    }

}

二叉樹的遍歷

基本上有4種遍歷方法,先、中、后根,逐層。當初我對這個很迷惑,搞這么多干什么?到了后面才明白,這是不同的應用需要的。例如,判斷兩個二叉樹是否相等,只要子樹根節點不同,那么就不等,顯然這時要用先序遍歷;而刪除二叉樹,必須先刪除左右子樹,然后才能刪除根節點,這時就要用后序遍歷。

實際上,搞這么多遍歷方法,根本原因是在內存中儲存的樹是非線性結構。對于用數組儲存的二叉樹,這些名目繁多的方法都是沒有必要的。利用C++的封裝和重載特性,這些遍歷方法能很清晰的表達。

1.         前序遍歷

public:

void PreOrder(void (*visit)(T &data) = print) { PreOrder(root, visit); }

private:

void PreOrder(BTNode<T>* p, void (*visit)(T &data))

{

if (p){ visit(p->data); PreOrder(p->left, visit); PreOrder(p->right, visit); }

}

2.         中序遍歷

public:

       void InOrder(void (*visit)(T &data) = print) { InOrder(root, visit); }

private:

void InOrder(BTNode<T>* p, void (*visit)(T &data))

{

       if (p){ InOrder(p->left, visit);       visit(p->data);       InOrder(p->right, visit); }

       }

3.         后序遍歷

public:

       void PostOrder(void (*visit)(T &data) = print) { PostOrder(root, visit); }

private:

void PostOrder(BTNode<T>* p, void (*visit)(T &data))

{

       if (p){ PostOrder(p->left, visit); PostOrder(p->right, visit); visit(p->data); }

       }

4.         層次遍歷

void LevelOrder(void (*visit)(T &data) = print)

{

       queue< BTNode<T>* > a; BTNode<T>* p = root;//記得#include<queue>

       while (p)

       {

              visit(p->data);

              if (p->left) a.push(p->left); if (p->right) a.push(p->right);

              if (a.empty()) break; p = a.front(); a.pop();

       }

       }

附注:缺省的visit函數print如下

private:

       static void print(T &data) { cout << data << ' '; }

5.         不用棧的非遞歸中序遍歷

當有parent指針時,可以不用棧實現非遞歸的中序遍歷,書上提到了有這種方法,但沒給出例程。

public:

BTNode<T>* next()

{

       if(!current) return NULL;

       if (current->right) { current = current->right; while (current->left) current = current->left; }

       else

       {

              BTNode<T>* y = current->parent;

              while (y && current == y->right) {current = y; y = y->parent; }

              current = y;

       }

       return current;

}

private:

BTNode<T>* current;

上面的函數能使current指針向前移動一個位置,如果要遍歷整棵二叉樹,需要使current指向中序序列的第一個節點,例如下面的成員函數:

public:

void first() { current = root; while (current->left) current = current->left; }

【2】 

線索化二叉樹

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

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

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

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

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

綜上,線索化最好是中序線索化(前序后序線索化后還得用棧,何必要線索化呢),附加的標志域空間至少1個字節,在32位的CPU會要求對齊到4字節,還不如存儲一個雙親指針,同樣能達到中序線索化的目的,并且能帶來其他的好處。所以,線索化二叉樹在現在的PC上是毫無用處的!

由于對其他體系不太了解,以下觀點姑妄聽之。在內存空間非常充裕的現在,一個節點省23個字節實在是沒什么意思(實際上由于對齊還省不出來);而在內存非常寶貴的地方(比如單片機),會盡量避免使用樹結構——利用其他的方法。所以,現在看來,線索化二叉樹真的是毫無用處了。

二叉搜索樹

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

public:

void print()

{

       queue< BTNode<T>* > a; queue<bool> flag; ofstream outfile("out.txt");

       BTNode<T>* p = root; BTNode<T> zero; bool v = true;

       int i = 1, level = 0, h = height();

       while (i < 2<<h)

       {

              if (i == 1<<level)

              {

                     cout << endl << setw(2 <<(h - level)); level++;

                     if (v) cout << p->data;

                     else cout << ' ';

              }

              else

              {

                     cout << setw(4 <<(h - level + 1));

                     if (v) cout << p->data;

                     else cout << "  ";

              }

              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 << endl;

}

打印樹狀結構的核心是按層次遍歷二叉樹,但是,二叉樹有許多節點缺左或右子樹,連帶的越到下面空隙越大。為了按照樹的結構打印,必須把二叉樹補成完全二叉樹,這樣下面的節點就知道放在什么位置了——a.push(&zero);但是這樣的節點不能讓它打印出來,所以對應每個節點,有一個是否打印的標志,按理說pair結構很合適,為了簡單我用了并列的兩個隊列,一個放節點指針——a,一個放打印標志——flag。這樣一來,循環結束的標志就不能是隊列空——永遠都不可能空,碰到NULL就補一個節點——而是變成了到了滿二叉樹的最后一個節點2^(height+1)-1?!S皮書對于樹高的定義是,空樹為的高度為-1。

對于輸出格式,注意的是到了第124、8號節點要換行,并且在同一行中,第一個節點的域寬是后序節點的一半。上面的函數在樹的層次少于等于5height<=4)的時候能正常顯示,再多的話就必須輸出到文件中去ofstream outfile("out.txt");——如果層次再多的話,打印出來也沒什么意義了。

二叉搜索樹的實現

實際上就是在二叉樹的基礎上增加了插入、刪除、查找。

#include "BaseTree.h"

template <class T>

class BSTree : public BTree<T>

{

public:

       BTNode<T>* &find(const T &data)

       {

              BTNode<T>** p = &root; current = NULL;

              while(*p)

              {

                     if ((*p)->data == data) break;

                     if ((*p)->data < data) { current = *p; p = &((*p)->right); }

                     else { current = *p; p = &((*p)->left); }

              }

              return *p;

       }

       bool insert(const T &data)

       {

              BTNode<T>* &p = find(data); if (p) return false;

              p = new BTNode<T>(data, NULL, NULL, current); return true;

       }

       bool remove(const T &data)

       {

              return remove(find(data));

       }

private:

bool remove(BTNode<T>* &p)

{

       if (!p) return false; BTNode<T>* t = p;

       if (!p->left || !p->right)

       {

              if (!p->left) p = p->right; else p = p->left;

              if (p) p->parent = current;

              delete t; return true;

       }

       t=p->right;while(t->left) t=t->left;p->data=t->data;current=t->parent;

       return remove(current->left==t?current->left:current->right);

       }

};

以上代碼有點費解,有必要說明一下——非線性鏈式結構操作的實現都是很讓人費神。insertremove都是以find為基礎的,因此必須讓find能最大限度的被這兩個操作利用。

l         對于insert,需要修改查找失敗時的指針內容,顯然這是個內部指針(在雙親節點的內部,而不是象rootcurrent那樣在節點外面指向節點),這就要求find返回一個內部指針的引用。但是C++的引用綁定到一個對象之后,就不能再改變了,因此在find內部的實現是一個二重指針。insert操作還需要修改插入的新節點的parent指針域,因此在find中要產生一個能被insert訪問的指向find返回值所在節點的指針,這里用的是current。實際上find返回的指針引用不是current->left就是current->right。這樣一來,insert的實現就非常簡單了。

l         對于remove,需要修改查找成功時的指針內容,同樣是個內部指針。在find的基礎上,很容易就能得到這個內部指針的引用(BTNode<T>* &p = find(data)

?         p->leftp->right中至少有一個為NULL

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

Posted on 2005-12-15 12:57 艾凡赫 閱讀(518) 評論(0)  編輯 收藏 引用 所屬分類: C++
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            99re6热在线精品视频播放速度 | 嫩草影视亚洲| 亚洲伊人网站| 一区二区三区高清| 亚洲午夜av在线| 欧美一级久久久久久久大片| 欧美在线观看一二区| 久久三级视频| 欧美日本国产一区| 国产精品午夜久久| 国内精品视频666| 亚洲国产日韩欧美综合久久| 一区二区av在线| 校园激情久久| 欧美阿v一级看视频| 亚洲美女精品成人在线视频| 亚洲专区一二三| 久热国产精品视频| 欧美日韩你懂的| 国内一区二区三区| 亚洲精品国产精品国自产在线| 在线中文字幕不卡| 久久亚洲精品欧美| 99国内精品久久| 午夜欧美理论片| 免费视频一区二区三区在线观看| 亚洲国产欧美日韩精品| 亚洲免费大片| 久久大香伊蕉在人线观看热2| 牛牛精品成人免费视频| 国产精品久久久久9999| 亚洲福利视频一区| 欧美一级一区| 亚洲欧洲精品一区二区三区不卡 | 久久婷婷蜜乳一本欲蜜臀| 欧美日韩国产高清| 黄色资源网久久资源365| 一本色道久久综合亚洲二区三区 | 99视频精品免费观看| 久久www免费人成看片高清 | 亚洲国产一区二区视频| 久久99伊人| 9色porny自拍视频一区二区| 免费永久网站黄欧美| 国内偷自视频区视频综合| 亚洲自拍都市欧美小说| 91久久国产自产拍夜夜嗨| 久久aⅴ国产欧美74aaa| 国产精品无码永久免费888| 日韩视频在线观看免费| 奶水喷射视频一区| 久久久久国产精品一区二区| 国产日韩精品一区二区| 午夜精品视频在线观看| 一本色道久久88综合日韩精品| 欧美大学生性色视频| 亚洲国产精品久久人人爱蜜臀| 久久综合久色欧美综合狠狠| 欧美一区二区精品| 国产欧美日韩伦理| 欧美伊人久久久久久午夜久久久久 | 欧美1区2区视频| 久久精品中文| 在线观看视频日韩| 欧美成年人视频网站| 久久综合一区二区三区| 在线观看亚洲精品| 欧美xxxx在线观看| 欧美国产精品专区| 亚洲视频一二三| 亚洲无线一线二线三线区别av| 国产精品亚洲一区二区三区在线| 亚洲欧美影音先锋| 久久国产精品电影| 亚洲自拍啪啪| 激情丁香综合| 亚洲黄色尤物视频| 欧美亚男人的天堂| 久久成人一区| 欧美 日韩 国产在线| 宅男在线国产精品| 亚洲在线观看免费| 国内外成人免费视频| 欧美激情四色| 欧美日韩亚洲一区二区三区在线观看 | 一区二区三区回区在观看免费视频| 欧美色大人视频| 久久精品夜色噜噜亚洲a∨ | 一区二区三区欧美激情| 亚洲伊人网站| 亚洲国产精品一区二区久| 亚洲毛片网站| 精品999日本| 欧美国产视频一区二区| 国产精品扒开腿爽爽爽视频| 麻豆av一区二区三区久久| 欧美日韩免费在线视频| 久久亚洲春色中文字幕久久久| 欧美久久久久久久久| 久久精品一区二区三区中文字幕| 欧美不卡激情三级在线观看| 欧美一区二区三区四区在线| 欧美大片免费| 久久婷婷丁香| 国产精品久久一级| 亚洲国产精品久久91精品| 国产日韩一级二级三级| 亚洲免费电影在线| 亚洲人成毛片在线播放| 欧美永久精品| 午夜视频在线观看一区二区| 欧美精品一区二区在线播放| 久久中文欧美| 国产日韩精品一区| 亚洲性夜色噜噜噜7777| 一区二区三区回区在观看免费视频| 久久精品国产一区二区三区| 欧美一区二区三区成人| 欧美日韩中文| 亚洲精品在线二区| 亚洲精品国产精品国自产观看浪潮| 欧美一区在线直播| 西西裸体人体做爰大胆久久久| 欧美a级大片| 欧美大胆a视频| 狠狠色综合网| 欧美一区免费视频| 欧美一区二区三区视频免费播放| 欧美日韩免费在线| 日韩视频永久免费观看| 99re热精品| 欧美精品自拍| 卡一卡二国产精品| 国产亚洲一区在线| 亚洲精品国产精品国自产观看| 久久精精品视频| 亚洲欧美日本伦理| 亚洲精品乱码久久久久久日本蜜臀| 亚洲小视频在线| 一本色道久久综合亚洲精品按摩| 欧美在线视频日韩| 欧美一进一出视频| 国产精品美女久久久久av超清 | 久久亚洲春色中文字幕| 国产一区二区精品丝袜| 日韩视频永久免费| 亚洲日本欧美天堂| 亚洲福利视频网站| 久久一区二区三区四区五区| 亚洲丝袜av一区| 欧美三级欧美一级| 亚洲国产日韩欧美在线动漫| 亚洲精选中文字幕| 亚洲永久精品国产| 欧美一级成年大片在线观看| 午夜在线不卡| 国产麻豆视频精品| 久久精品国产综合| 国产亚洲福利| 亚洲欧美另类久久久精品2019| 亚洲精品综合| 欧美午夜不卡视频| 在线中文字幕一区| 小黄鸭精品密入口导航| 夜色激情一区二区| 亚洲一区三区视频在线观看| 亚洲欧美第一页| 国产视频一区在线观看| 久久成人一区| 激情欧美日韩| 亚洲精品午夜精品| 亚洲欧美成人| 亚洲欧美日韩久久精品| 久久国产精品免费一区| 亚洲国产精品久久久久秋霞影院 | 亚洲精品麻豆| 99re66热这里只有精品3直播| 欧美精品一区二区三区高清aⅴ| 国产日韩亚洲欧美综合| 久久国产精彩视频| 亚洲人成艺术| 欧美一区二区三区在线免费观看| 国产日韩在线一区二区三区| 在线看日韩欧美| 欧美午夜国产| 国内外成人在线| 欧美久色视频| 欧美在线不卡| 国产精品久久久久免费a∨| 欧美freesex8一10精品| 一本大道久久a久久综合婷婷| 亚洲欧美日韩久久精品 | 久久xxxx| 亚洲性夜色噜噜噜7777| 国模精品一区二区三区色天香| 欧美精品三区| 久久久久久伊人| 国产欧美在线| 亚洲一区美女视频在线观看免费| 亚洲福利在线视频|