9.1 树与二叉树
1.二叉树的存储结构
struct node
{
typename data;
node* lchild; // 指向左子树根节点的指针
node* rchild; // 指向右子树根节点的指针
}
二叉树在建树之前根节点不存在,地址一般为 NULL
。node *root = NULL;
新建结点: (往二叉树中插入结点)
// 生成一个新结点,v为结点权值
node* newNode(int v)
{
node* Node = new node; // 申请一个 node 型变量的地址空间
Node->data = v; // 结点权值为v
Node->lchild = Node->rchild = NULL; // 初始状态下没有左右孩子
return Node; // 返回新建节点的地址
}
2.二叉树结点的查找与修改
void search(node* root, int x, int newdata)
{
if(root == NULL)
return; // 空树
if(root->data == x) // 找到数据域中为x的结点,修改
root->data = newdata;
search(root->lchild, x, newdata); // 往左子树中搜索x
search(root->rchild, x, newdata); // 往右子树中搜索x
}
3.二叉树结点插入
// 在二叉树中插入一个数据域为x的新结点
void insert(node* &root, int x)
{
if(root == NULL) // 空树,查找失败,即插入的位置 (递归边界)
{
root = newNode(x);
return;
}
if(由二叉树的性质,x应该插在左子树)
insert(root->lchild, x); // 往左子树搜索
else
insert(root->rchild, x); // 往右子树搜索
}
4.二叉树的创建
// 二叉树的建立
void Create(int data[], int n)
{
node* root = NULL; // 新建空根节点root
for(int i=0; i<n; i++)
{
insert(root, data[i]); // 将data[0] - data[i-1] 插入到树中
}
return root;
}
9.2 二叉树的遍历
先序遍历
void preorder(node* root)
{
if(root == NULL) // 到达空树,递归边界
return;
// 访问根节点
printf("%d\n", root->data);
// 访问左子树
preorder(root->lchild);
// 访问右子树
preorder(root->rchild);
}
中序遍历
void inorder(node* root)
{
if(root == NULL)
return;
inorder(root->lchild);
printf("%d\n", root->data);
inorder(root->rchild);
}
后序遍历
void postorder(node* root)
{
if(root == NULL)
return;
postorder(root->lchild);
postorder(root->rchild);
printf("%d\n", root->data);
}
层序遍历
void LayerOrder(node *root)
{
queue<node*> q;
q.push(root); // 根结点地址入队
while(!q.empty())
{
node* now = q.front();
q.pop();
printf("%d\n", now->data); // 访问队首元素
if(now->lchild != NULL) q.push(now->lchild); // 左子树非空
if(now->rchild != NULL) q.push(now->rchild);
}
}
在结构体变量中增加一个记录层次的变量:
struct node
{
int data; // 数据域
int layer; // 层次
node* lchild; // 左指针域
node* rchild; // 右指针域
}
void LayerOrder(node* root)
{
queue<node*> q;
root->layer = 1;
q.push(root);
while(!q.empty())
{
node* now = q.front();
q.pop();
printf("%d\n", now->data);
if(now->lchild != NULL)
{
now->lchild->layer = now->layer + 1;
q.push(now->lchild);
}
if(now->rchild != NULL)
{
now->rchild->layer = now->layer + 1;
q.push(now->rchild);
}
}
}
给定一棵二叉树的先序遍历序列和中序遍历序列,重建这棵二叉树
二叉树的静态实现
// 定义二叉树
struct node
{
typename data; // 数据域
int lchild; // 指向左子树根节点的指针
int rchild; // 指向右子树根节点的指针
}Node[maxn]; // 节点数组,maxn为结点上限个数
// 结点的动态生成
int index = 0;
int newNode(int v)
{
Node[index].data = v;
Node[index].lchild = -1;
Node[index].rchild = -1;
return index++;
}
// 查找,root为根结点在数组中的下标
void search(int root, int x, int newdata)
{
if(root == -1)
return; // 空树
if(Node[root].data == x)
Node[root].data = newdata;
search(Node[root].lchild, x, newdata);
search(Node[root].rchild, x, newdata);
}
// 插入,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);
}
// 二叉树的建立
int Create(int data[], int n)
{
int root = -1; // 新建根结点
for(int i=0; i<n; i++)
{
insert(root, data[i]); // 将每一个节点插入二叉树
}
return root; // 返回根结点下标
}
遍历
// 先序遍历
void preorder(int root)
{
if(root == -1)
return; // 到达空树,递归边界
// 访问根节点 root
printf("%d\n", Node[root].data);
// 访问左子树
preorder(Node[root].lchild);
// 访问右子树
preorder(Node[root].rchild);
}
// 中序遍历
void inorder(int root)
{
if(root == -1)
return; // 到达空树 递归边界
inorder(Node[root].lchild);
printf("%d\n", Node[root].data);
inorder(Node[root].rchild);
}
// 后序遍历
void postorder(int root)
{
if(root == -1)
return;
postorder(Node[root].lchild);
postorder(Node[root].rchild);
printf("%d\n", Node[root].data);
}
// 层序遍历
void LayerOrder(int root)
{
queue<int> q;
q.push(root);
while(!q.empty())
{
int now = q.front();
q.pop();
printf("%d\n", Node[now].data);
if(Node[now].lchild != -1) q.push(Node[now].lchild);
if(Node[now].rchild != -1) q.push(Node[now].rchild);
}
}
9.3 树的遍历
struct node
{
typename data; // 数据域
int child[maxn]; // 指针域,存放所有子结点的下标
}Node[maxn];
使用 vector
重新定义为
struct node
{
typename data;
vector child;
}Node[maxn];
新建一个节点
int index = 0;
int newNode(int v)
{
Node[index].data = v;
Node[index].child.clear();
return index++; // 返回节点下标,令index自增
}
先根遍历
void preorder(int root)
{
printf("%d ", Node[root].data);
for(int i=0; i<Node[root].child.size(); i++)
{
preorder(Node[root].child[i]); // 递归访问结点root的所有子结点
}
}
层序遍历
void LayerOrder(int root)
{
queue<int> q;
q.push(root);
while(!q.empty())
{
int now = q.front();
q.pop();
printf("%d\n", Node[now].data);
for(int i=0; i<Node[now].child.size(); i++)
{
q.push(Node[now].child[i]);
}
}
}
如果题目中涉及到层数,在结构体中增加一个变量存储层数信息。
struct node
{
int layer; // 记录层号
int data;
vector<int> child;
}
// 层序遍历可以更新为
void LayerOrder(int root)
{
queue<int> q;
q.push(root);
Node[root].layer = 0; // 根节点的层号为0
while(!q.empty())
{
int now = q.front();
q.pop();
printf("%d\n", Node[now].data);
for(int i=0; i<Node[now].child.size(); i++)
{
int child = Node[now].child[i];
Node[child].layer = Node[now].layer + 1;
q.push(child);
}
}
}
9.4 二叉查找树
// 查找二叉查找树中数据域为x的结点
void search(node* root, int x)
{
if(root == NULL) // 空树查找失败
return;
if(root->data == x)
printf("%d\n", root->data);
else if(x < root->data)
search(root->lchild); // 左子树搜索x
else
search(root->rchild); // 右子树搜索x
}
// 插入一个数据域为x的新结点
void insert(node* &root, int x)
{
if(root == NULL) // 空树,查找失败即插入的位置
{
roor = newNode(x);
return;
}
if(x == root->data) // 已经存在,无需插入
return;
else if(x < root->data)
insert(root->lchild, x);
else
insert(root->rchild, x);
二叉搜索树建立
node *Create(int data[], int n)
{
node *root = NULL;
for(int i=0; i<n; i++)
{
insert(root, data[i]);
}
return root; // 返回根结点
}
9.6 并查集
使用一个数组实现
int father[N];
初始化,每个元素都是一个独立的集合
for(int i=1; i<=N; i++)
{
father[i] = i;
}
查找
// 递推实现
int findFather(int x)
{
while(x != father[x]) // 不是根结点继续循环
x = father[x]; // 或者自己的父亲节点
return x;
}
// 递归实现
int findFather(int x)
{
if(x == father[x]) return x; // 如果找到根结点,返回根结点编号x
else return findFather(father[x]); // 否则递归判断x的父亲节点是否是根结点
}
合并
只对两个不同的集合进行合并,保证结果是一棵树。
void Union(int a, int b)
{
int faA = findFather(a);
int fbB = findFather(b);
if(FaA != fbB)
{
father[faA] = fbB;
}
}
路径压缩
// 递推
int findFather(int x)
{
int a = x; // 保存当前节点
while(x != father[x])
x = father[x];
// x存放的是根结点
while(a != father[a])
{
int z = a;
a = father[a]; // 指向上一个
father[z] = x; // 最下方节点指向根父节点
}
return x; // 返回根结点
}
// 递归
int findFather(int v)
{
if(v == father[v]) return v; // 找到根结点
else
{
int F = findFather(father[v]); // 递归查找父节点的根结点
father[v] = F; // 根结点F赋值给father[v]
return F; // 返回根结点 F
}
}
// 更简单写法
int findFather(int x)
{
if(x != father[x]) father[x] = findFather(father[x]);
return father[x];
}
9.7 堆
从最后一个元素,从下往上,从右往左进行调整。没有下一级可以直接被跳过。
// 堆的表示
const int maxn = 100;
// heap为堆,n为元素个数
int heap[maxn], n=10;
// 对heap数组在[low, high]范围向下调整
// 其中low为欲调整结点的数组下标,high一般为堆的最后一个元素的数组下标
void downAdjust(int low, int high)
{
int i=low, j=2*i; // i为欲调整结点,j为其左孩子
while(j<high) // 存在孩子节点
{
// 右孩子存在且右孩子的值大于左孩子
if(j+1<=high && heap[j+1]>heap[j])
j = j+1; // j存储右孩子下标(更大的一个)
// 孩子中最大的权值比欲调整结点i大
if(heap[j]>heap[i])
{
swap(heap[j], heap[i]); // 交换最大权值的孩子与欲调整结点i
i = j; // i为欲调整的节点
j = i*2; // j还是i的左孩子
}
else
break; // 孩子的权值均比当前域调整的权值小,调整结束
}
}
// 建堆
void createHeap()
{
for(int i=n/2; i>=1; i--)
{
downAdjust(i, n);
}
}
// 删除堆顶元素
void deleteTop()
{
heap[1] = heap[n--] // 用最后一个元素覆盖堆顶元素,元素个数减1
downAdjust(1, n-1); // 向下调整堆顶元素
}
// 对heap数组在[low, high]范围进行向上调整
// 其中low一般设置为1,high表示欲调整结点的数组下标
void upAdjust(int low, int high)
{
int i=high, j= i/2; // i为欲调整节点,j为其父亲节点
while(j>=low)
{
// 父亲权值小于与调整结点i的权值
if(heap[j]<heap[i])
{
swap(heap[j], heap[i]);
i = j; // i为欲调整结点,j为i父亲
j = i/2;
}
else
break;
}
}
// 添加元素
void insert(int x)
{
heap[++n] = x; // 元素个数加1,
upAdjust(1, n); // 向上调整新加入的结点n
}
// 堆排序
void heapSort()
{
createHeap();
for(int i=n; i>1; i--) // 倒着枚举,直到堆中只有一个元素
{
swap(heap[1], heap[i]); // 交换 heap[i] 与堆顶
downAdjust(1, i-1); // 调整堆顶
}
}