数据结构 (二叉树)1

二叉树:每个节点最多有两棵字数,左子树和右子树

完全二叉树:叶节点只能出现在最下层和次下层,而且最下面一层的结点都集中在该层最左边的若干位置的二叉树。每层节点都完全填满,在最后一层上如果不是满的,则只缺少右边的若干节点。

满二叉树:要么叶子节点,要么节点同时具有左右子树

存储方式:

1)顺序存储: 遍历速度上有优势,但占空间大,是非主流二叉树。二叉树通常以链式存储

    typedef char datatype;

    typedef struct node{

         datatype data;

         int lchild, rchild;

        int parent;

   } Node;

Node tree[Length];

2)链式存储: 

    typedef char datatype;

    typedef struct node{

   datatype data;

  struct node *lchild;

      struct node *rchild;

   }node;

node *tree;

二叉树的遍历

前序遍历:根节点->左子树->右子树

中序遍历:左子树->根节点->右子树

后序遍历:左子树->右子树->根节点

实现:

问题:

取地址符&

  int a=0;

  int *p=&a;

  int *M=& 0X01000; wrong, 对于一个数值常量,没有地址。 变量有地址要有一个存储单元对变量进行标识

引用&

  int a=0;

  int &r=a;

new delete 和malloc free: new 是operator, malloc是函数

创建:

void creatBinTree(node * t)
{
char c;
cin>>c;
if(c=='#')
t=NULL;
else
{
// t=(node *) malloc(sizeof(node *));
t=new node;
t->data=c;
creatBinTree(t->lchild);
creatBinTree(t->rchild);
}

}



int main()
{

node * tree1;
creatBinTree(tree1);

visit(tree1);
return 0;
}

错误,tree没有创建成功,因为没有改变tree;

将函数改为:

void creatBinTree(node * &t)

成功

分析:

C++中的值传递,指针传递,引用传递

1)值传递:

  形参是实参的拷贝,改变形参的值不会影响外部实参的值(如果函数内部需要修改参数,并且不希望这个改变影响其他调用,采用值传递)

2)指针传递:

     形参为指向实参地址的指针,当对形参的指向操作时,就相当于对实参本身进行操作。

3)引用传递

    形参相当于实参的“别名”, 对形参的操作,就是对实参的操作。在引用传递过程中,被调函数的形式参数虽然作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。

void change1(int n)
{

    cout<<"值传递--函数操作地址"<<&n<<endl;
    n++;
    }
void change2(int &n)
{
    cout<<"引用传递--函数操作地址"<<&n<<endl;
    n++;
}

void change3(int *n)
{
    cout<<"指针传递--函数操作地址"<<n<<endl;
    *n=*n+1;
}
int main()
{
    int n=10;
    cout<<"实参的地址"<<&n<<endl;
    change1(n);
    cout<<n<<endl;
    change2(n);
    cout<<n<<endl;
    change3(&n);
    cout<<n<<endl;
    return 0;
}

引用于指针传递的区别:

1)引用被创建的同时必须被初始化(指针可在任何时候初始化)

2)不能有NULL引用,必须有合法的存储单元关联

3)引用一旦初始化,就不能改变引用的关系(指针可以随时改变所指的对象)

指针传递的本质是值传递,传递一个地址值。

1)递归方式访问二叉树:

void visit(node * root)
{
    if(root)
    {
        cout<<root->data<<endl;
        if(root->lchild)
        {
            visit(root->lchild);
        }

        if(root->rchild)
        {
            visit(root->rchild);
        }
    }
}

2)非递归的访问二叉树:

(递归操作遍历过的根节点还要回来,所以需要存起来)-->栈存储  

  对于前序遍历,优先访问根节点,然后在分别访问左孩子和右孩子。

  对于任一节点P:

  1)访问节点P,并将节点P入栈;

  2)判断节点P的左孩子是否为空,若为空,则取栈顶节点并出栈,将栈顶节点的右孩子置为当前的节点P,看其是否为空,若不为空,则循环1), 如为空,则继续出栈。

    若左子树不为空,将P的左孩子置为当前的节点P,重复1)

  3)直到P为NUL并且栈为空

存在栈中是为了倒叙访问右节点

ABD入先后入栈,并在入栈时输出,

D的左孩子为空,将D出栈,但不输出,将其有孩子作为当前节点,为空,将B出栈,并将右节点E作为当前节点,入栈,输出E。

E和D情况相同,类推。。。

void preOrder2(node *root)
{
    stack <node*>s;
    node * p=root;
    while(p!=NULL||!s.empty())
    {
        while(p!=NULL)
        {
            s.push(p);
            cout<<p->data<<endl;
            p=p->lchild;
        }
        if(!s.empty())
        {
            p=s.top();
            s.pop();
            p=p->rchild;
        }
    }

}

中序遍历为出栈的时候再输出

后序遍历稍稍复杂

原文地址:https://www.cnblogs.com/fanhaha/p/7051958.html