树
引入
树中的元素之间并不像顺序表和链表一样,存在天然的直接后继或直接前驱关系。不过只要附加某种约束(比如遍历),也可以在树中的元素之间确定某种线性次序,因此树属于半线性结构(semi-linear structure)。以下是一些易混淆的概念:
分支结点:除叶结点外的其他结点,又称为非终端结点。
树的度:树中结点的度的最大值。
有序树:树中结点的各棵子树是有次序的
无序树:树中结点的各棵子树之间的次序是不重要的,可以互相交换位置。
二叉树
存储表示
树的顺序存储是通过一维数组实现的,通过索引关系来确定子树,对于索引为$i$的节点来说,其关系可以表述为:如果$i=1$则结点$i$是二叉树的根,无双亲;其他情况下,其父节点为$i/2$,左子树为$2*i$,右子树为$2*i+1$,如果存在的话。树的顺序存储实现简单,操作方便,但是如果不是存储完全二叉树可能造成较大的空间浪费且树高受限。
树的链式存储主要通过结构题来实现,以下是一个常用的结构:
struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode() : val(0), left(nullptr), right(nullptr) {} TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} };
树的静态链表结合考虑了顺序存储和链式存储,能够充分利用数组的空间。
遍历
前序的递归实现,其他两种改变输出位置即可:
void Front(TreeNode* root){ if(root == nullptr){ return; } if(!root -> left && !root -> right){ cout << root -> val; return; } cout << root -> val; Front(root -> left); Front(root -> right); }
层序遍历通过队列来实现:
void Layer(TreeNode* root){ queue<TreeNode*> q; q.push(root); while(!q.empty()){ TreeNode* now = q.front(); q.pop(); cout << now -> val << '\t'; if(!now -> left){ q.push(now -> left); } if(!now -> right){ q.push(now -> right); } } }
前序和中序还原二叉树
map<int, int> idx;//建立先序数字到位置的映射 TreeNode* build(vector<int>& p, int pl,int pr,int il,int ir){ if(pl > pr){ return NULL; } TreeNode* ans = new TreeNode(p[pl]); if(pl == pr){ return ans; } int i = idx[p[pl]]; ans -> left = build(p, pl + 1, pl + i - il, il, i - 1); ans -> right = build(p, i - il + pl + 1, pr, i + 1, ir); return ans; } TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { int n = inorder.size(); for(int i = 0; i < n;i++){ idx[inorder[i]] = i; } return build(preorder, 0, n - 1, 0, n - 1); }