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

【1】

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

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

因?yàn)楝F(xiàn)實(shí)世界中存在這“樹”這種結(jié)構(gòu)——族譜、等級制度、目錄分類等等,而為了研究這類問題,必須能夠?qū)鋬Υ妫绾蝺Υ鎸⑷Q于所需要的操作。這里有個(gè)問題,是否允許存在空樹。有些書認(rèn)為樹都是非空的,因?yàn)闃浔硎镜氖且环N現(xiàn)實(shí)結(jié)構(gòu),而0不是自然數(shù);我用過的教科書都是說可以有空樹,當(dāng)然是為了和二叉樹統(tǒng)一。這個(gè)沒有什么原則上的差別,反正就是一種習(xí)慣。

二叉樹

二叉樹可以說是人們假想的一個(gè)模型,因此,允許有空的二叉樹是無爭議的。二叉樹是有序的,左邊有一個(gè)孩子和右邊有一個(gè)的二叉樹是不同的兩棵樹。做這個(gè)規(guī)定,是因?yàn)槿藗冑x予了左孩子和右孩子不同的意義,在二叉樹的各種應(yīng)用中,你將會清楚的看到。下面只講解鏈?zhǔn)浇Y(jié)構(gòu)。看各種講數(shù)據(jù)結(jié)構(gòu)的書,你會發(fā)現(xiàn)一個(gè)有趣的現(xiàn)象:在二叉樹這里,基本操作有計(jì)算樹高、各種遍歷,就是沒有插入、刪除——那樹是怎么建立起來的?其實(shí)這很好理解,對于非線性的樹結(jié)構(gòu),插入刪除操作不在一定的法則規(guī)定下,是毫無意義的。因此,只有在具體的應(yīng)用中,才會有插入刪除操作。

節(jié)點(diǎn)結(jié)構(gòu)

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

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種遍歷方法,先、中、后根,逐層。當(dāng)初我對這個(gè)很迷惑,搞這么多干什么?到了后面才明白,這是不同的應(yīng)用需要的。例如,判斷兩個(gè)二叉樹是否相等,只要子樹根節(jié)點(diǎn)不同,那么就不等,顯然這時(shí)要用先序遍歷;而刪除二叉樹,必須先刪除左右子樹,然后才能刪除根節(jié)點(diǎn),這時(shí)就要用后序遍歷。

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

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函數(shù)print如下

private:

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

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

當(dāng)有parent指針時(shí),可以不用棧實(shí)現(xiàn)非遞歸的中序遍歷,書上提到了有這種方法,但沒給出例程。

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;

上面的函數(shù)能使current指針向前移動一個(gè)位置,如果要遍歷整棵二叉樹,需要使current指向中序序列的第一個(gè)節(jié)點(diǎn),例如下面的成員函數(shù):

public:

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

【2】 

線索化二叉樹

這是數(shù)據(jù)結(jié)構(gòu)課程里第一個(gè)碰到的難點(diǎn),不知道你是不是這樣看,反正我當(dāng)初是費(fèi)了不少腦細(xì)胞——當(dāng)然,惱人的矩陣壓縮和相關(guān)的加法乘法運(yùn)算不在考慮之列。我費(fèi)了不少腦細(xì)胞是因?yàn)樗伎迹核麄兏墒裁茨兀亢苄老驳目吹皆谶@本黃皮書上,這章被打了*號,雖然我不確定作者是不是跟我一個(gè)想法——線索化二叉樹在現(xiàn)在的PC上是毫無用處的!——不知我做了這個(gè)結(jié)論是不是會被人罵死,^_^

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

我們來看看線索化二叉樹究竟能不能達(dá)到上面的兩個(gè)目標(biāo)。

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

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

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

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

二叉搜索樹

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

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;

}

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

對于輸出格式,注意的是到了第1248號節(jié)點(diǎn)要換行,并且在同一行中,第一個(gè)節(jié)點(diǎn)的域?qū)捠呛笮蚬?jié)點(diǎn)的一半。上面的函數(shù)在樹的層次少于等于5height<=4)的時(shí)候能正常顯示,再多的話就必須輸出到文件中去ofstream outfile("out.txt");——如果層次再多的話,打印出來也沒什么意義了。

二叉搜索樹的實(shí)現(xiàn)

實(shí)際上就是在二叉樹的基礎(chǔ)上增加了插入、刪除、查找。

#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);

       }

};

以上代碼有點(diǎn)費(fèi)解,有必要說明一下——非線性鏈?zhǔn)浇Y(jié)構(gòu)操作的實(shí)現(xiàn)都是很讓人費(fèi)神。insertremove都是以find為基礎(chǔ)的,因此必須讓find能最大限度的被這兩個(gè)操作利用。

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

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

?         p->leftp->right中至少有一個(gè)為NULL

數(shù)據(jù)結(jié)構(gòu)學(xué)習(xí)(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>
            一本一道久久综合狠狠老精东影业 | 99视频国产精品免费观看| 亚洲一区久久久| 亚洲高清视频在线| 久久精品国产第一区二区三区| 国产精品对白刺激久久久| 一本色道久久综合亚洲精品小说| 亚洲国产欧美日韩精品| 蜜臀av在线播放一区二区三区| 伊人久久噜噜噜躁狠狠躁| 久久噜噜噜精品国产亚洲综合| 午夜精品影院| 国产亚洲一区二区三区| 久久久久久久精| 久久精品人人做人人爽电影蜜月| 国内外成人在线视频| 久久综合色综合88| 麻豆精品在线视频| 亚洲人成精品久久久久| 亚洲精品视频免费观看| 欧美午夜美女看片| 欧美午夜精品久久久久免费视| 亚洲午夜伦理| 亚洲图片欧美午夜| 国产精品自在线| 久久美女性网| 美国成人直播| 一本色道久久加勒比精品| 亚洲最新视频在线播放| 国产精品免费一区二区三区观看 | 一区二区三区国产精品| 欧美激情第9页| 一区二区三区免费看| 一区二区三区视频在线播放| 国产精品无码专区在线观看| 久久影音先锋| 欧美激情综合亚洲一二区| 亚洲资源在线观看| 久久精品伊人| 亚洲天堂偷拍| 久久国产精品久久久久久| 亚洲三级观看| 亚洲女女女同性video| 亚洲国产免费看| 正在播放亚洲一区| 在线精品视频在线观看高清| 一二三区精品福利视频| 激情综合色丁香一区二区| 亚洲精品日韩综合观看成人91| 国产日韩专区在线| 91久久久亚洲精品| 国产在线播放一区二区三区| 亚洲精品乱码| 伊人久久婷婷色综合98网| 亚洲人成毛片在线播放| 国产一区再线| 一本色道精品久久一区二区三区| 韩国亚洲精品| 亚洲一区二区三区在线看| 亚洲啪啪91| 欧美在线一二三四区| 亚洲一区二区三区视频播放| 久久综合网络一区二区| 欧美在线在线| 国产精品毛片a∨一区二区三区|国 | 午夜天堂精品久久久久| 亚洲精品自在久久| 久久大逼视频| 欧美一级理论性理论a| 欧美日韩国产一区精品一区| 欧美成人一区二区| 国内精品久久久久影院色| 亚洲综合第一页| 亚洲一区二区三区影院| 欧美精品性视频| 欧美阿v一级看视频| 国产一区二区剧情av在线| 亚洲天堂免费观看| 亚洲少妇中出一区| 欧美日韩a区| 一区二区久久久久| 女人天堂亚洲aⅴ在线观看| 欧美综合国产精品久久丁香| 欧美性做爰猛烈叫床潮| 亚洲人成在线播放| 日韩一二三区视频| 欧美精品一区在线发布| 亚洲国产精品久久久久婷婷884 | 亚洲欧美激情一区二区| 亚洲欧美精品一区| 欧美日韩在线第一页| 亚洲日本一区二区三区| 亚洲国产精品欧美一二99| 久久久之久亚州精品露出| 久久中文字幕导航| 亚洲国产精品123| 欧美成人精品在线视频| 亚洲国产清纯| 在线亚洲精品| 国产精品伦子伦免费视频| 亚洲欧美日本日韩| 久久只有精品| 日韩午夜av电影| 欧美日韩综合网| 亚洲一区二区三区四区五区午夜| 性8sex亚洲区入口| 韩国v欧美v日本v亚洲v| 麻豆国产va免费精品高清在线| 亚洲国产欧美一区二区三区久久| av成人黄色| 国产精品视频免费一区| 久久久精品国产免费观看同学| 欧美激情91| 亚洲一区国产| 国产一区二区看久久| 蜜臀av性久久久久蜜臀aⅴ| 91久久在线观看| 亚洲一区二区三区国产| 国产亚洲精品bt天堂精选| 久久一区二区视频| 一本色道久久综合亚洲精品按摩 | 亚洲在线免费视频| 国产一区清纯| 欧美日韩国产色视频| 午夜精品视频| 亚洲电影免费在线观看| 亚洲欧美美女| 在线视频国产日韩| 国产精品jizz在线观看美国| 欧美一区二区三区视频免费| 亚洲丰满少妇videoshd| 小黄鸭精品aⅴ导航网站入口| 在线不卡欧美| 国产精品福利在线| 久久亚洲高清| 亚洲图片在线| 欧美夫妇交换俱乐部在线观看| 一本色道88久久加勒比精品| 国产亚洲午夜高清国产拍精品| 蜜桃av噜噜一区二区三区| 亚洲在线视频观看| 亚洲国产精品热久久| 久久九九热re6这里有精品 | 亚洲在线免费观看| 91久久精品国产91性色tv| 国产精品美女久久久久久免费| 麻豆久久婷婷| 欧美在线网站| 亚洲永久免费精品| 亚洲国产高清在线| 美女福利精品视频| 久久成人国产精品| 亚洲无亚洲人成网站77777| 亚洲国产精品一区| 韩国美女久久| 国产亚洲成av人片在线观看桃| 欧美日本高清| 欧美激情精品久久久久久久变态 | 欧美日韩精品| 快播亚洲色图| 欧美一区国产在线| 亚洲午夜在线观看| 99国内精品久久久久久久软件| 久久综合久久美利坚合众国| 亚洲欧美日韩视频一区| 宅男在线国产精品| 在线视频日本亚洲性| 99视频超级精品| 亚洲精品视频在线| 91久久夜色精品国产网站| 亚洲第一黄色网| 国产在线精品一区二区夜色| 国产日韩在线看| 国产一区二区成人| 国产视频一区在线| 国产日韩精品一区| 国产亚洲精久久久久久| 韩日精品视频| 黄色亚洲精品| 一区国产精品| 亚洲黄页一区| 亚洲久久一区二区| 一本久久精品一区二区| 中日韩美女免费视频网站在线观看| 一区二区三区日韩在线观看| 亚洲婷婷免费| 亚洲欧美在线看| 久久99伊人| 免费av成人在线| 欧美激情中文不卡| 亚洲精品在线视频观看| 一本色道久久综合| 午夜精品一区二区三区在线视 | 亚洲深夜激情| 欧美一区二区三区播放老司机| 欧美专区亚洲专区| 免费av成人在线| 欧美日韩免费在线视频| 国产欧美精品xxxx另类| 精品91视频|