国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

二叉樹(shù)的遍歷的遞歸與非遞歸算法

這篇具有很好參考價(jià)值的文章主要介紹了二叉樹(shù)的遍歷的遞歸與非遞歸算法。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問(wèn)。

一.二叉樹(shù)的遍歷:

按照一定規(guī)律對(duì)二叉樹(shù)的每個(gè)結(jié)點(diǎn)進(jìn)行訪問(wèn)且僅訪問(wèn)一次;

這里的訪問(wèn):可以是計(jì)算二叉樹(shù)中的結(jié)點(diǎn)數(shù)據(jù),打印該結(jié)點(diǎn)的信息,也可以是對(duì)結(jié)點(diǎn)進(jìn)行的任何其它操作!

為什么需要遍歷二叉樹(shù)?

從過(guò)遍歷可以得到訪問(wèn)結(jié)點(diǎn)的順序序列,遍歷操作就是將二叉樹(shù)的結(jié)點(diǎn)按一定的規(guī)律線性化,目的就在于將非線性化的結(jié)構(gòu)變成線性化的訪問(wèn)序列。


二.3種遍歷方式:

1.先序遍歷:(DLR)根——左——右

若二叉樹(shù)非空

首先訪問(wèn)根結(jié)點(diǎn),其次按先序遍歷左子樹(shù),最后按先序遍歷右子樹(shù)

下面我們給出一個(gè)例子:

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言

如圖所示的二叉樹(shù):根據(jù)先序遍歷的特點(diǎn),我們先訪問(wèn)根結(jié)點(diǎn):A,再訪問(wèn)其左子樹(shù),而其左子樹(shù)又可以看成一個(gè)二叉樹(shù),我們依然用先序遍歷A的左子樹(shù),此時(shí)B為這個(gè)左子樹(shù)的根結(jié)點(diǎn),于是我們?cè)L問(wèn)B,A的左子樹(shù)此時(shí)還沒(méi)有訪問(wèn)完成,因此我們繼續(xù)訪問(wèn),此時(shí)B已經(jīng)訪問(wèn)過(guò),那么我們接著訪問(wèn)B的左孩子,而B(niǎo)沒(méi)有左孩子,所以我們接著訪問(wèn)B的右孩子,D作為右孩子的根結(jié)點(diǎn),直接訪問(wèn),對(duì)于D來(lái)說(shuō),接著訪問(wèn)D的左孩子F,最后訪問(wèn)D的右孩子G;此時(shí)A的左孩子遍歷完成;

那么我們接著遍歷A的右孩子,C作為右子樹(shù)的根結(jié)點(diǎn)直接訪問(wèn),C沒(méi)有左孩子,接著訪問(wèn)C的右孩子,E作為C的右孩子的根節(jié)點(diǎn),直接訪問(wèn),E沒(méi)有左孩子,訪問(wèn)E的右孩子H

綜上所述:上述二叉樹(shù)的先序遍歷的順序就為:A-B-D-F-G-C-E-H


2.中序遍歷:LDR(左——根——右)

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言

我們?nèi)匀灰陨鲜龆鏄?shù)為例來(lái)看:

根據(jù)?中序遍歷的特點(diǎn):首先按中序遍歷左子樹(shù):此時(shí)首先訪問(wèn)A的左子樹(shù),二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言我們將二叉樹(shù)的左子樹(shù)單獨(dú)拿出來(lái)看:如圖:B作為左子樹(shù)的根節(jié)點(diǎn),我們要找此時(shí)這個(gè)二叉樹(shù)的左孩子,而此時(shí)這個(gè)二叉樹(shù)沒(méi)有左孩子,因此我們直接訪問(wèn)這個(gè)二叉樹(shù)的根節(jié)點(diǎn)B,之后訪問(wèn)這個(gè)二叉樹(shù)的右孩子:同樣把這個(gè)二叉樹(shù)的右孩子單獨(dú)拿出來(lái):二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言此時(shí)我們依然以中序遍歷:首先訪問(wèn)這個(gè)二叉樹(shù)的左孩子,即F,接著訪問(wèn)這個(gè)二叉樹(shù)的根節(jié)點(diǎn)D,最后訪問(wèn)這個(gè)二叉樹(shù)的右孩子G;

那么此時(shí)整個(gè)二叉樹(shù)的左孩子訪問(wèn)完成,繼續(xù)訪問(wèn)整個(gè)二叉樹(shù)的根節(jié)點(diǎn)A;

接著訪問(wèn)整個(gè)二叉樹(shù)的右孩子:

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言此時(shí)C作為整個(gè)二叉樹(shù)的右孩子的根節(jié)點(diǎn),我們首先訪問(wèn)這個(gè)二叉樹(shù)的左孩子,顯然這個(gè)二叉樹(shù)沒(méi)有左孩子,因此我們先訪問(wèn)根結(jié)點(diǎn)C;

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言此時(shí):E作為二叉樹(shù)的根結(jié)點(diǎn),此時(shí)該二叉樹(shù)沒(méi)有左孩子,直接訪問(wèn)根節(jié)點(diǎn)E,最后訪問(wèn)右孩子H

綜上所述:按照中序遍歷訪問(wèn)上述二叉樹(shù)的順序:B-F-D-G-A-C-E-H

3.后序遍歷:LRD(左——右——根)

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言

仍然以上述二叉樹(shù)作為例子來(lái)看:首先訪問(wèn)整個(gè)二叉樹(shù)的左子樹(shù):

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言按照后序遍歷訪問(wèn):B作為根結(jié)點(diǎn),訪問(wèn)這個(gè)二叉樹(shù)的左子樹(shù),左子樹(shù)為空,接著訪問(wèn)這個(gè)二叉樹(shù)的右孩子:

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言此時(shí)D作為根結(jié)點(diǎn),先訪問(wèn)其左孩子F,接著訪問(wèn)其右孩子G,最后訪問(wèn)根節(jié)點(diǎn)D;

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言那么經(jīng)過(guò)上述過(guò)程,該二叉樹(shù)的右孩子訪問(wèn)完成,接著訪問(wèn)根節(jié)點(diǎn)B,此時(shí)整個(gè)二叉樹(shù)的左子樹(shù)遍歷完成;

那么我們接著訪問(wèn)整個(gè)二叉樹(shù)的右子樹(shù):

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言C作為此時(shí)這個(gè)二叉樹(shù)的根結(jié)點(diǎn),先訪問(wèn)這個(gè)二叉樹(shù)的左子樹(shù),但是左子樹(shù)為空,接著訪問(wèn)右孩子:

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言E為此時(shí)這個(gè)二叉樹(shù)的根結(jié)點(diǎn),此時(shí)這個(gè)二叉樹(shù)沒(méi)有左子樹(shù),那么我們?cè)L問(wèn)其右孩子H,接著訪問(wèn)根節(jié)點(diǎn)E;

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言此時(shí)這個(gè)二叉樹(shù)的右孩子也訪問(wèn)完成,所以我們?cè)L問(wèn)根結(jié)點(diǎn)C;

整個(gè)二叉樹(shù)的左右子樹(shù)均已遍歷完成,于是我們最后訪問(wèn)根結(jié)點(diǎn)A;

綜上所述:按照后續(xù)遍歷訪問(wèn)上述二叉樹(shù)的順序?yàn)椋篎-G-D-B-H-E-C-A

總結(jié):

看了以上三種方式遍歷二叉樹(shù)的例子,應(yīng)該就能明白三種遍歷方式的本質(zhì)與區(qū)別,其實(shí)我們可以看出,無(wú)論以何種方式,我們只要把握好訪問(wèn)的順序,并且將每一個(gè)根結(jié)點(diǎn)下的子樹(shù)都看成一個(gè)新樹(shù),再按照某種遍歷方式訪問(wèn)這個(gè)這個(gè)新樹(shù)即可!

例子:中綴表達(dá)式轉(zhuǎn)后綴表達(dá)式;(前綴表達(dá)式:波蘭表達(dá)式;后綴表達(dá)式:逆波蘭表達(dá)式)

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言

前綴表達(dá)式:-+a*bc/de(也就是以先序遍歷訪問(wèn)上述二叉樹(shù)的順序)

中綴表達(dá)式:a+b*c-d/e(也就是以中序遍歷訪問(wèn)上述二叉樹(shù)的順序)

后綴表達(dá)式:abc*+de/-(也就是以后序遍歷訪問(wèn)上述二叉樹(shù)的順序)

我們其實(shí)可以看出:中綴表達(dá)式是我們?cè)跀?shù)學(xué)計(jì)算中的習(xí)慣順序,但是其實(shí)在計(jì)算機(jī)種,后綴表達(dá)式才是計(jì)算機(jī)能理解的表達(dá)式;

(關(guān)于中綴表達(dá)式轉(zhuǎn)后綴表達(dá)式的例題:二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言

有興趣的讀者可以自行嘗試:下面給出利用棧來(lái)解決逆波蘭表達(dá)式的代碼:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MaxSize 1010

typedef struct
{
    char* ch;
    int top;

}CharStack;

typedef struct
{
    double* num;
    int top;
}NumStack;

CharStack S;
NumStack St;

void InitCharStack(CharStack& S);
void InitNumStack(NumStack& St);
void PushCharStack(CharStack& S, char c);
void PushNumStack(NumStack& St, double num);
char PopCharStack(CharStack& S);
double PopNumStack(NumStack& St);
char GetStackTop(CharStack& S);
bool PriorJudge(char c1, char c2);//priority優(yōu)先級(jí) judge判斷
char* InfixToSuffex(char* a, char* b);//infix前綴 suffex后綴 
double CaculatValue(char* b, NumStack& St);

int main()
{
    InitCharStack(S);

    char a[MaxSize] = { 0 };
    char b[MaxSize] = { 0 };
    InfixToSuffex(a, b);

    double ans = CaculatValue(b, St);

    printf("%.2f\n", ans);

    return 0;
}

void InitCharStack(CharStack& S)
{
    S.ch = (char*)malloc(MaxSize * sizeof(char));

    if (S.ch == NULL)
    {
        printf("內(nèi)存分配失??!");
        return;
    }
    S.top = -1;

    return;
}

void PushCharStack(CharStack& S, char c)
{
    if (S.top == MaxSize)
    {
        printf("棧已滿(mǎn)");
        return;
    }
    S.top++;
    *++S.ch = c;

    return;
}

char PopCharStack(CharStack& S)
{
    if (S.top == -1)
    {
        printf("棧為空");;
        exit(1);
    }
    S.top--;
    char c = *S.ch--;

    return c;
}

char GetStackTop(CharStack& S)
{
    char c = *S.ch;

    return c;
}

bool PriorJudge(char c1, char c2)//priority優(yōu)先級(jí) judge判斷 
{
    if (c1 == '*' || c1 == '/')
        if (c2 == '+' || c2 == '-' || c2 == '(')
            return true;
    if (c1 == '+' || c1 == '-')
        if (c2 == '(') return true;

    return false;
}

char* InfixToSuffex(char* a, char* b)//infix中綴 suffex后綴 
{
    fgets(a, MaxSize - 1, stdin);//從輸入流中讀取字符串 

    int t = strlen(a);

    int k = 0;
    //    12.25 + 25.02 / 2.36 + 8 * 2.01 =
    for (int i = 0; i < t; ++i)
    {
        if (a[i] >= '0' && a[i] <= '9')
        {
            b[k++] = a[i];
            if (a[i + 1] == '.') b[k++] = '.', ++i;
            else if (a[i + 1] < '0' || a[i + 1] > '9')
                b[k++] = '#';
        }

        else if (a[i] == ' ' || a[i] == '=') continue;

        else
        {
            if (S.top == -1 || a[i] == '(')
                PushCharStack(S, a[i]);

            else if (a[i] == ')')
            {
                while (S.top != -1 && GetStackTop(S) != '(')
                    b[k++] = PopCharStack(S);

                if (S.top != -1) PopCharStack(S);

            }

            else
            {
                while (S.top != -1 && !PriorJudge(a[i], GetStackTop(S)))
                    b[k++] = PopCharStack(S);

                PushCharStack(S, a[i]);
            }
        }

    }

    b[k] = '\0';//這句話加不加應(yīng)該無(wú)所謂,因?yàn)樵撟址麛?shù)組已初始化為0 

    return b;
}

void InitNumStack(NumStack& St)
{
    St.num = (double*)malloc(MaxSize * sizeof(double));
    St.top = -1;

    return;
}

void PushNumStack(NumStack& St, double n)
{
    if (St.top == MaxSize)
    {
        printf("棧已滿(mǎn)");
        return;
    }

    St.top++;

    *++St.num = n;

    return;
}

double PopNumStack(NumStack& St)
{
    if (St.top == -1)
    {
        printf("棧為空");
        exit(1);
    }
    St.top--;
    double num = *St.num--;

    return num;
}

double CaculatValue(char* b, NumStack& St)
{
    InitNumStack(St);
    int t = strlen(b);
    char str[MaxSize] = { 0 };

    for (int i = 0, k = 0; i < t; ++i)
    {
        if (b[i] == '.' || (b[i] >= '0' && b[i] <= '9'))
        {
            str[k++] = b[i];
            if (b[i + 1] == '#')
            {
                str[k] = '\0';
                double num = atof(str);
                PushNumStack(St, num);
                k = 0;
            }
        }

        else
        {
            if (b[i] == '#') continue;

            double n = PopNumStack(St);
            double m = PopNumStack(St);

            if (b[i] == '+') PushNumStack(St, m + n);

            else if (b[i] == '-') PushNumStack(St, m - n);

            else if (b[i] == '*') PushNumStack(St, m * n);

            else PushNumStack(St, m / n);
        }

    }

    return PopNumStack(St);
}

三.二叉樹(shù)的遍歷算法:

在理解了上述所說(shuō)的遍歷過(guò)程之后,我們來(lái)看二叉樹(shù)的遍歷算法,可以分為遞歸算法與非遞歸算法

1.遞歸算法:

(1)先序遍歷的遞歸算法

首先定義二叉樹(shù)的鏈表結(jié)點(diǎn)結(jié)構(gòu):

#define DataType int 
//首先定義二叉樹(shù):

typedef struct Node
{
	DataType data;
	struct Node* LChild;
	struct Node* RChild;
}BiTNode,*BiTree;
//先序遍歷二叉樹(shù)的遞歸算法

void PreOrder(BiTree root)
{
	//root為指向二叉樹(shù)或者其某一子樹(shù)的根結(jié)點(diǎn)的指針

	if (root != NULL)
	{
		printf("%d", root->data);//訪問(wèn)根結(jié)點(diǎn);//文章開(kāi)頭就已經(jīng)說(shuō)過(guò),二叉樹(shù)的遍歷可以是多種形式
		PreOrder(root->LChild);//按照先序遍歷訪問(wèn)左子樹(shù)
		PreOrder(root->RChild);//按照先序遍歷訪問(wèn)右子樹(shù)
	}
}

(2)中序遍歷遞歸算法

void InOrder(BiTree root)
{
	if (root != NULL)
	{
		InOrder(root->LChild);//首先按中序遍歷左子樹(shù)
		printf("%d", root->data);//訪問(wèn)根結(jié)點(diǎn)
		InOrder(root->RChild);//中序遍歷右子樹(shù)
	}
}

(3)后續(xù)遍歷遞歸算法

void PostOrder(BiTree root)
{
	if (root != NULL)
	{
		PostOrder(root->LChild);//首先按照后序遍歷左子樹(shù)
		PostOrder(root->RChild);//按照后序遍歷右子樹(shù)
		printf("%d", root->data);//訪問(wèn)根結(jié)點(diǎn)
	}
}

遞歸:把復(fù)雜問(wèn)題變成相同性質(zhì)的子問(wèn)題,因此遞歸算法表面上看上去很簡(jiǎn)單,其實(shí)整個(gè)的邏輯十分復(fù)雜;

比如:當(dāng)訪問(wèn)到的結(jié)點(diǎn)不為空時(shí),我們先訪問(wèn)其左子樹(shù),再訪問(wèn)其右子樹(shù);但是當(dāng)根結(jié)點(diǎn)為空時(shí),就不需要參與遞歸運(yùn)算,那此時(shí)訪問(wèn)的為空,應(yīng)該返回什么值?在這里程序就會(huì)自動(dòng)設(shè)置堆棧來(lái)保存本層地址(涉及到堆棧的知識(shí):當(dāng)遞歸函數(shù)調(diào)用時(shí),應(yīng)按照“后調(diào)用先返回”(因?yàn)榇髥?wèn)題一直在分解成小問(wèn)題,當(dāng)分解為基問(wèn)題時(shí),才會(huì)返回一個(gè)值,因此最后調(diào)用的先返回)的原則處理調(diào)用過(guò)程,因此函數(shù)之間的信息傳遞和控制轉(zhuǎn)移必須通過(guò)棧來(lái)實(shí)現(xiàn)(符合棧的特點(diǎn):last in first out)

整個(gè)遞歸過(guò)程其實(shí)與上述所描述的遍歷算法類(lèi)似,讀者可嘗試分析;


遞歸算法的時(shí)間復(fù)雜度分析:

假設(shè)二叉樹(shù)有n個(gè)結(jié)點(diǎn),對(duì)每個(gè)結(jié)點(diǎn)都要進(jìn)行一次入棧和出棧的操作,即入棧和出棧均執(zhí)行了n此,對(duì)結(jié)點(diǎn)的訪問(wèn)也是n次,這些二叉樹(shù)的遞歸遍歷算法的時(shí)間復(fù)雜度就為O(n);


A.遞歸遍歷算法的應(yīng)用:

a.輸出二叉樹(shù)中的結(jié)點(diǎn):
//輸出二叉樹(shù)的結(jié)點(diǎn)

void PreOrder(BiTree root)
{
	if (root != NULL)
	{
		printf("%d",root->data);
		PreOrder(root->LChild);
		PreOrder(root->RChild);
	}
}

輸出二叉樹(shù)的結(jié)點(diǎn)并沒(méi)有次序要求,因此三種次序均可以使用

b.輸出二叉樹(shù)中的葉子結(jié)點(diǎn):

相比于上述應(yīng)用,葉子節(jié)點(diǎn):無(wú)后繼,因此有條件限制

//輸出葉子結(jié)點(diǎn)數(shù)

void PreOrder(BiTree root)
{
	if (root != NULL)
	{
		if (root->LChild == NULL && root->RChild == NULL)
		{
			printf("%d", root->data);
		}
		PreOrder(root->LChild);
		PreOrder(root->RChild);
	}
}
c.統(tǒng)計(jì)葉子結(jié)點(diǎn)數(shù)目
(1)一般的后序遍歷
void leaf(BiTree root)
{
	if (root != NULL)
	{
		leaf(root->LChild);
		leaf(root->RChild);
		if (root->LChild == NULL && root->RChild == NULL)
		{
			leafCount++;//保存葉子結(jié)點(diǎn)數(shù)目的全局變量,調(diào)用之前的初始化為0;別忘了在main函數(shù)中先調(diào)用初始化函數(shù)
		}
	}
}

在這里:根據(jù)文章開(kāi)頭所說(shuō)的,究竟什么才是對(duì)根結(jié)點(diǎn)進(jìn)行訪問(wèn)?在這里leafCount++就可以是,因此在這里,我們使用的是后續(xù)遍歷;

(2)分治算法:

如果二叉樹(shù)為空樹(shù),返回0,如果二叉樹(shù)只有一個(gè)根結(jié)點(diǎn),返回1,否則返回左子樹(shù)和右子樹(shù)的葉子節(jié)點(diǎn)數(shù)之和(也是遞歸算法)

int leaf1(BiTree root)
{
	int leafCount;
	if (root == NULL)
	{
		leafCount = 0;
	}
	else if (root->LChild == NULL && root->RChild == NULL)
	{
		leafCount = 1;
	}
	else
	{
		leafCount = leaf1(root->LChild) + leaf1(root->RChild);
	}
	return leafCount;
}

在這里也是后序遍歷:根據(jù)表達(dá)式:C=A+B,在C語(yǔ)言中是先計(jì)算A,再計(jì)算B,最后計(jì)算A+B,賦值給C,因此依然是先遍歷左子樹(shù),再遍歷右子樹(shù),最后訪問(wèn)根;

(關(guān)于求二叉樹(shù)的高度:讀者可嘗試)
int PostTreeDepth(BiTree bt)
{
	int hl, hr,max;
	if (bt != NULL)
	{
		hl=PostTreeDepth(bt->LChild);
		hr=PostTreeDepth(bt->RChild);
		max = hl > hr ? hl : hr;
		return(max + 1);//還要加上第一個(gè)根結(jié)點(diǎn)
	}
	else
	{
		return 0;
	}
}
d:按橫向樹(shù)形顯示二叉樹(shù):

其實(shí)就是按照“逆中序”的方式來(lái)遍歷整個(gè)二叉樹(shù)(所謂的逆中序,就是先訪問(wèn)右子樹(shù),再訪問(wèn)根,最后訪問(wèn)右子樹(shù),與正常的中序遍歷相反)

(在這里只給出思路,具體代碼實(shí)現(xiàn),讀者可以自行嘗試,有問(wèn)題歡迎在評(píng)論區(qū)交流!)

2.非遞歸算法:

基于棧的遞歸消除:

關(guān)于遞歸的消除:我們可以將這些遞歸問(wèn)題,轉(zhuǎn)化為重復(fù)的循環(huán)問(wèn)題,即用循環(huán)代替遞歸,但是在工作量大,復(fù)雜的情況下,就不適宜采用循環(huán),因此我們可以采用工作棧的方式來(lái)消除遞歸。

(1)中序遍歷二叉樹(shù)的非遞歸算法

下面我們以中序遍歷二叉樹(shù)的非遞歸算法為例來(lái)看:

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言

整個(gè)過(guò)程都依據(jù)在上述框圖下進(jìn)行:

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言?(以此二叉樹(shù)為例)

首先:A:A不為空——進(jìn)棧;p=A的LChild,到B;B不為空——進(jìn)棧;p指向B的左子樹(shù),到C;C不為空,C進(jìn)棧;p指向C的左子樹(shù);C的左子樹(shù)為空,判斷棧是否為空,棧此時(shí)非空,C出棧,p指向C的右子樹(shù),D,C的右子樹(shù)不為空,D入棧,p指向D的左子樹(shù),p為空,那么判斷棧是否為空,棧非空,D退棧,p指向D的右子樹(shù),D的右子樹(shù)為空,棧非空,退棧到B,p指向B的右子樹(shù),為E,不是空,那么E進(jìn)棧,E的左子樹(shù)為空,棧非空,那么E出棧,p指向E的右子樹(shù),E的右子樹(shù)為空,棧非空,A出棧,A的右子樹(shù)為F,不為空F進(jìn)棧,p指向F的左子樹(shù),F(xiàn)的左子樹(shù)為G不為空,G進(jìn)棧,p指向G的左子樹(shù),G的左子樹(shù)為空,棧非空,G出棧,p指向G的右子樹(shù),G的右子樹(shù)為空,棧非空,F(xiàn)出棧,p指向F的右子樹(shù),F(xiàn)的右子樹(shù)為空,棧為空,那么遍歷結(jié)束;(出棧的同時(shí)訪問(wèn)根結(jié)點(diǎn),在這里我省略了,注意一下?。。?/strong>)(可能有點(diǎn)長(zhǎng),可以耐心看一下

此時(shí):我們需要自己設(shè)置棧來(lái)保存結(jié)點(diǎn)的信息;為什么能夠?qū)崿F(xiàn)中序?基于棧的一些特點(diǎn),我們可以直到,先進(jìn)棧的后出棧,因此左子樹(shù)先進(jìn)棧后,一直保存在棧中,而到退棧的地步時(shí),說(shuō)明左子樹(shù)一定為空,所以此時(shí)來(lái)訪問(wèn)右子樹(shù);并且在結(jié)點(diǎn)判斷為非空時(shí),其一直保存在棧中,并沒(méi)有對(duì)其進(jìn)行訪問(wèn),所以一定是先訪問(wèn)左子樹(shù)的,直到判斷左子樹(shù)為空時(shí),我們才將左子樹(shù)對(duì)應(yīng)的根結(jié)點(diǎn)出棧,先訪問(wèn)根結(jié)點(diǎn),再去訪問(wèn)右子樹(shù)。

在這里其實(shí)進(jìn)棧的一直都是根結(jié)點(diǎn)!

上述便是中序遍歷的非遞歸算法;(極大地利用了棧的特點(diǎn)?。。。?/p>

(先序遍歷的非遞歸算法與中序類(lèi)似,與中序遍歷的不同之處在于,中序保存根結(jié)點(diǎn),而先序遍歷我們可以保存右孩子的地址,這樣比較簡(jiǎn)單,我們不再贅述;)

(2)后序遍歷二叉樹(shù)的非遞歸算法

我們首先說(shuō)明一下:中序遍歷的非遞歸與后序遍歷的非遞歸算法的異同:

(1)相同點(diǎn):根結(jié)點(diǎn)進(jìn)棧

(2)而后序遍歷在p=NULL時(shí),不能直接出棧訪問(wèn)根結(jié)點(diǎn),因?yàn)榇藭r(shí)我們不能判斷右孩子是否已經(jīng)訪問(wèn)

那我們?cè)撛谑裁磿r(shí)候訪問(wèn)?

(1)根的右子樹(shù)為空;(2)根有右孩子,但是根的右子樹(shù)已經(jīng)訪問(wèn)過(guò)了;

對(duì)于第二中情況,我們?nèi)绾闻袛喔挠易訕?shù)是否已經(jīng)訪問(wèn)?那我們就可以多設(shè)置一個(gè)指針,記憶之前已經(jīng)訪問(wèn)的結(jié)點(diǎn);

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言(以此二叉樹(shù)為例)

我們初始時(shí)設(shè)置p,q兩個(gè)指針,p用于移動(dòng),q用于記憶已訪問(wèn)過(guò)的結(jié)點(diǎn),q初始值設(shè)置為NULL

首先:根結(jié)點(diǎn)A不為空,入棧,p指向A的左子樹(shù):

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言此時(shí)p為B,B不為空,那么B入棧,p指向B的左子樹(shù),那么p為空,棧非空,并且此時(shí)B的右子樹(shù)不為空,并且q=NULL,并不等于B的右子樹(shù),說(shuō)明B的右子樹(shù)還沒(méi)有訪問(wèn)過(guò),那么我們不訪問(wèn)根結(jié)點(diǎn),p指向B的右子樹(shù);

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言p此時(shí)指向D,D的左子樹(shù)不為空D入棧,p指向D的左子樹(shù),此時(shí)p指向E,E不為空,E入棧,p指向E的左子樹(shù),p此時(shí)為空,棧非空,讀取棧頂元素E,并且q指向E的右子樹(shù),E的右子樹(shù)為空,那么滿(mǎn)足可以訪問(wèn)根結(jié)點(diǎn)的條件,因此我們?cè)L問(wèn)E,E出棧,此時(shí)設(shè)置q為E,上面已經(jīng)判斷E的右子樹(shù)為空,p為空,棧非空,那么讀取棧頂元素D,此時(shí)D的右子樹(shù)不為空,并且q也不等于D的右子樹(shù),那么說(shuō)明D的右子樹(shù)沒(méi)有訪問(wèn)過(guò),此時(shí)p=D的右子樹(shù)F,p的右子樹(shù)不為空,F(xiàn)入棧,p指向F的左子樹(shù),F(xiàn)的左子樹(shù)為空,棧非空,讀取棧頂元素F,p指向F的右子樹(shù),且F的右子樹(shù)為空,那么我們?cè)L問(wèn)結(jié)點(diǎn)F,F(xiàn)出棧,并且令q=F,并且由于p為空,棧非空,讀取棧頂元素D,此時(shí)q=D的右子樹(shù)F,滿(mǎn)足訪問(wèn)條件,訪問(wèn)D,并且令q=D,D出棧;(下面分析:出棧之后讀取棧頂元素)讀取棧頂元素,B,此時(shí)q=B的右子樹(shù),滿(mǎn)足訪問(wèn)的條件,我們?cè)L問(wèn)B,并將q=B,B出棧(分析:訪問(wèn)完就出棧),讀取棧頂元素,A,此時(shí)A的右子樹(shù)不為空,并且q不等于A的右子樹(shù),不滿(mǎn)足訪問(wèn)根的條件,因此我們?cè)L問(wèn)A的右子樹(shù)。

二叉樹(shù)的遍歷的遞歸與非遞歸算法,算法,數(shù)據(jù)結(jié)構(gòu),c語(yǔ)言此時(shí)p=C,不為空,C入棧,p指向C的左子樹(shù),C的左子樹(shù)為空,p=NULL,棧非空,且C的右子樹(shù)不為空,q也不等于C的右子樹(shù)G,那么不滿(mǎn)足訪問(wèn)的條件,令p=C的右子樹(shù)G,G不為空,G入棧,p=G的左子樹(shù),G的左子樹(shù)為空,棧非空,G的右子樹(shù)為空,滿(mǎn)足訪問(wèn)的條件,因此訪問(wèn)G,令q=G,G出棧;再讀取棧頂元素C,此時(shí)q=C的右子樹(shù)G,滿(mǎn)足訪問(wèn)的條件,訪問(wèn)C,并且令q=C,C出棧;讀取棧頂元素A,此時(shí)q=A的右子樹(shù),那么滿(mǎn)足訪問(wèn)的條件,訪問(wèn)A,令q=A,A出棧,此時(shí)棧為空且q=A,說(shuō)明已經(jīng)訪問(wèn)到最后一個(gè)結(jié)點(diǎn)處。遍歷結(jié)束!

經(jīng)過(guò)以上過(guò)程的分析:我們其實(shí)可以發(fā)現(xiàn),每次在有元素出棧后,我們就開(kāi)始讀取棧頂元素;而在我們?cè)L問(wèn)完成結(jié)點(diǎn)后,就進(jìn)行出棧操作!

綜上所述:就是后續(xù)遍歷二叉樹(shù)的非遞歸算法,與中序遍歷不同的是:

加了一個(gè)起到記憶的作用的指針q,用于記憶已經(jīng)訪問(wèn)過(guò)的結(jié)點(diǎn);

加了一個(gè)讀取棧頂元素的操作:因?yàn)椴荒芰⒓丛L問(wèn)根結(jié)點(diǎn),出棧,所以要先確定訪問(wèn)之后,再出棧,所以我們先讀取,不出棧即可。

除了上述區(qū)別之外,中序遍歷與后序遍歷大同小異?。ê罄m(xù)遍歷更為復(fù)雜?。。。┳x者可以跟著上述內(nèi)容,自己走一遍這個(gè)二叉樹(shù)的遍歷的過(guò)程,就能夠理解上述思想!


以上就是我對(duì)于二叉樹(shù)的遍歷的遞歸與非遞歸算法的一些個(gè)人總結(jié)與理解,歡迎讀者有更好的方法再評(píng)論區(qū)交流!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-852494.html

到了這里,關(guān)于二叉樹(shù)的遍歷的遞歸與非遞歸算法的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來(lái)自互聯(lián)網(wǎng)用戶(hù)投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺(jué)得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包