通过下面的学习,读者应能完全不使用指针,而简单使用数组来完成二叉树的所有操作。
在定义二叉树时,采用的是二叉链表的结构,如下所示:
struct node{ typename data; //数据域 node* lchild; //指向左子树根结点的指针 node* rchild; //指向右子树根结点的指针 };
在这个定义中,为了能够实时控制新生成结点的个数,结构体node中的左右指针域都使用了指针,但是指针对于一些刚入门的读者来说可能容易犯错,因此有必要想办法避免指针的使用,采用的方法就是使用静态的二叉链表。
所谓的静态二叉链表是指,结点的左右指针域使用int型代替,用来表示左右子树的根结点在数组中的下标。为此需要建立一个大小为结点上限个数的node型数组,所有动态生成的结点都直接使用数组中的结点,所有对指针的操作都改为对数组下标的访问。于是,结点node的定义变为如下:
struct node{ typename data; //数据域 int lchild; //指向左子树根结点的指针 int rchild; //指向右子树根结点的指针 }Node[maxn]; //结点数组,maxn为结点上限个数
再这样的定义一下,结点的动态生成就可以转变成如下的静态指定:
int index = 0; int newNode(int v){ //分配一个Node数组中的结点给新的结点,index为其下标 Node[index].data = v; //数据域为v Node[index].lchild = -1; //以-1或maxn表示空,因为数组范围是0~maxn-1 Node[index].rchild = -1; return index++; }
下面给出二叉树的查找、插入、建立的代码,读者会发现这其实就是在之前使用指针的代码上进行了少量的修改,读者不妨将它们与原先的写法对比一下:
//查找,root为根结点在数组中的下标 void search(int root, int x, int newdata){ if(root==-1){ //用-1代表NULL return; //空树,死胡同(递归边界) } if(Node[root].data == x){ //找到数据域为x的结点,把它修改成newdata Node[root].data = newdata; } search(Node[root].lchild,x,newdata); //往左子树搜索x(递归式) search(Node[root].rchild,x,newdata); //往右子树搜索x(递归式) } //插入,root为根结点在数组中的下标 void insert(int &root, int x){ //记得加引用 if(root==-1){ root = newNode(x); return; } if(由二叉树的行知x应该插在左子树){ insert(Node[root].lchild,x); //往左子树搜索(递归式) } else { insert(Node[root].rchild,x); //往右子树搜索(递归式) } } //二叉树的建立,函数返回根结点root的下标 int Create(int data[], int n) { int root = -1; //新建根结点 for(int i=0;i<n;i++){ insert(root,data[i]); //将data[0]~data[n-1]插入二叉树中 } return root; //返回二叉树根结点下标 }
下面是静态二叉树的先序遍历、中序遍历、后序遍历、层次遍历的代码:
//先序遍历 void preorder(int root){ if(root==-1){ return; //到达空树,递归边界 } //访问根结点root,例如将其数据域输出 printf("%d\n",root->data); //访问左子树 preorder(root->lchild); //访问右子树 preorder(root->rchild); } //中序遍历 void inorder(int root){ if(root==-1){ return; //到达空树,递归边界 } //访问左子树 inorder(root->lchild); //访问根结点root,例如将其数据域输出 printf("%d\n",root->data); //访问右子树 inorder(root->rchild); } //后序遍历 void postorder(int root){ if(root==-1){ return; //到达空树,递归边界 } //访问左子树 postorder(root->lchild); //访问右子树 postorder(root->rchild); //访问根结点root,例如将其数据域输出 printf("%d\n",root->data); } //层次遍历 void LayerOrder(int root) { queue<node*> q; //注意队列里是存地址 q.push(root); //将根结点地址入队 while(!q.empty()) { node* now = q.front(); //取队首元素 q.pop(); printf("%d ",now->data); //访问队首元素 if(now->lchild!=NULL){ //左子树非空 q.push(now->lchild); } if(now->rchild!=NULL){ //右子树非空 q.push(now->rchild); } } }
于是二叉树便可以完全静态化。