PAT知识点-树的基本操作
树的数据结构
二叉树
一般的度为2的树是不考虑左右子树的顺序的,也就是说,左右子树交换对该树没影响。但是!二叉树是规定左右子树顺序的!左右子树交换后就是另一个二叉树了!左右子树的交换方法下面会讲,就是在后序遍历的时候交换左右子树。
1 2 3 4 5 6
| struct Node { int data; Node *lchild; Node *rchild; }
|
一般题目都是给定结点序号,下面的更常用
1 2 3 4 5 6 7
| const int MAXN=1000; struct Node { int data; int lchild; int rchild; }node[MAXN];
|
完全二叉树
完全二叉树指除了最后一层可以不排满(但必须从左到右排)节点,其他层必须满节点的树。
性质:如果设根节点的序号为1,那么其左子节点的序号为1*2=2,其由节点的序号为1*2+1=3。只有规定了根节点序号为1,那么对于任意一个序号为n的节点,其左孩子序号为2n,其右孩子序号为2n+1。
由于指定序号,必须用静态方式(数组)存储。
1 2 3 4 5 6 7
| const int MAXN=1000; struct Node { int data; int lchild; int rchild; }node[MAXN];
|
普通的树
1 2 3 4 5 6 7
| const int MAXN=1000; struct Node { int data; vector<int> child; }node[MAXN];
|
树的建立
一般除了二叉查找树是给一系列数值信息,建立树一般都是给定当前节点序号和该节点的孩子的序号。下面就根据这种常见的建立方式编码。
二叉树的建立
1 2 3 4 5 6 7
| int lchild,rchild; for(int i=0;i<n;i++) { scanf("%d%d",&lchild,&rchild); node[i].lchild=lchild; node[i].rchild=rchild; }
|
树的建立
1 2 3 4 5 6 7 8 9 10 11
| int child; for(int i=0;i<n;i++) { int num; scanf("%d",&num); for(int j=0;j<num;j++) { scanf("%d",&child); node[i].push_back(child); } }
|
树的遍历
这里用静态树的遍历说明,思想都是一样的。
这里注意,无论是什么遍历,都是左子树先遍历。
前序遍历/深度优先遍历DFS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| void preOrder(int root) { if(root==-1) return; printf("%d",root); preOrder(node[root].lchild); preOrder(node[root].rchild); }
void preOrder(int root) { printf("%d",root); int len=node[root].size(); for(int i=0;i<len;i++) { preOrder(node[root].child[i]); } }
|
中序遍历
1 2 3 4 5 6 7 8
| void inOrder(int root) { if(root==-1) return; inOrder(node[root].lchild); printf("%d",root); inOrder(node[root].rchild); }
|
后序遍历
1 2 3 4 5 6 7 8
| void postOrder(int root) { if(root==-1) return; inOrder(node[root].lchild); inOrder(node[root].rchild); printf("%d",root); }
|
层序遍历/广度优先遍历BFS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| void levelOrder(int root) { queue<int> q; q.push(root); while(!q.empty()) { int front=q.front(); printf("%d",front); q.pop(); if(node[front].lchild!=-1) { q.push(node[front].lchild); } if(node[front].rchild!=-1) { q.push(node[front].rchild); } } }
void levelOrder(int root) { queue<int> q; q.push(root); while(!q.empty()) { int front=q.front(); printf("%d",front); q.pop(); int len=node[front].size(); int child; for(int i=0;i<len;i++) { child=node[front][i]; if(node[child]!=-1) { q.push(child); } } } }
|
前序/后续遍历+中序遍历还原二叉树
前序遍历+中序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
|
void build(int pre[],int lpre,int rpre,int in[],int lin,int rin) { if(lpre>rpre) return -1; int root=pre[lpre]; int k; for(k=lin;k<=rin;k++) { if(root==lin[k]) return; } int numLeft=k-lin; node[root].lchild=build(pre,lpre+1,lpre+1+numLeft-1,in,lin,k-1); node[root].rchild=build(pre,lpre+1+numLeft-1+1,rpre,in,k+1,rin); return root; }
|
后序遍历+中序遍历
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
void build(int post[],int lpost,int rpost,int in[],int lin,int rin) { if(lpost>rpost) return -1; int root=post[rpost]; int k; for(k=0;k<=rin;k++) { if(root==in[k]) return; } int numLeft=k-lin; node[root].lchild=build(post,lpost,rpost-1-numLeft+1-1,in,lin,k-1); node[root].rchild=build(post,rpost-1-numLeft+1,rpost-1,in,k+1,rin); return root; }
|