一、树形结构
1、树的定义
树是一种非线性的数据结构,它是由 n(n>=0) 个有限结点组成一个具有层次关系的集合。我们之所以把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
2、基本概念
3、重要结论
- 节点的高度:节点到叶子节点的最长路径(边数)。
- 节点的深度 = 根节点到这个节点所经历的边的个数。
- 节点的层数 = 节点的深度+1。
- 树的高度 = 根节点的高度。
二、二叉树
1、结构定义
二叉树的每个节点最多两个子节点,分别是左子节点和右子节点。二叉树并不要求每个节点都有两个子节点,有的节点只有左子节点,有的节点只有右子节点。
2、二叉树的性质
- 二叉树的每个节点最多有两个子节点,所以它的度数不超过2。
- 左子树和右子树是有顺序的,即使节点只有一个子节点,也必须明确它是左子节点还是右子节点。
- 左子树和右子树也是二叉树,它们本身也遵循上述两个性质。
- 叶节点是没有子节点的节点,即度数为0的节点。
- 二叉树中度为0的节点比度为2的节点多一个。
3、代码实现
Ⅰ、结构
定义二叉树节点结构体,包含节点值、左子节点指针和右子节点指针
1 2 3 4
| typedef struct Node { int key; struct Node *lchild, *rchild; } Node;
|
Ⅱ、新增二叉树的节点
创建新节点并初始化函数,为节点分配内存空间,设定节点值,将左右子节点指针置空
1 2 3 4 5 6
| Node *getNewNode(int key) { Node *p = (Node *)malloc(sizeof(Node)); p->key = key; p->lchild = p->rchild = NULL; return p; }
|
(1)分配内存并创建一个新的 Node
类型的对象。
(2)将传入的参数 key
赋值给新节点的 key
变量。
(3)初始化该节点的左右孩子指针为 NULL
。
Ⅲ、销毁二叉树
1 2 3 4 5 6 7
| void clear(Node *root) { if (root == NULL) return; clear(root->lchild); clear(root->rchild); free(root); return; }
|
(1)检查节点是否为空,如果为空则返回,终止递归。
(2)递归地清理当前节点的左子树。
(3)递归地清理当前节点的右子树。
(4)释放当前节点的内存。
Ⅳ、插入节点
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Node* insert(Node* root, int key) { if (root == NULL) { return getNewNode(key); } if (key < root->key) { root->lchild = insert(root->lchild, key); } else if (key > root->key) { root->rchild = insert(root->rchild, key); } return root; }
|
Ⅴ、广度优先遍历
广度优先遍历(BFS)是使用使用队列来实现层次遍历。在 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
| int head, tail;
void bfs(Node *root) { head = tail = 0; queue[tail++] = root; while (head < tail) { Node *node = queue[head]; printf("\nnode : %d\n", node->key); if (node->lchild) { queue[tail++] = node->lchild; printf("\t%d->%d (left)\n", node->key, node->lchild->key); } if (node->rchild) { queue[tail++] = node->rchild; printf("\t%d->%d (right)\n", node->key, node->rchild->key); } head++; } return; }
|
Ⅵ、深度优先遍历
深度优先遍历(DFS)是使用栈来实现遍历,DFS 会从起始节点开始,将其压入栈中。然后,它会弹出栈顶节点并访问其未被访问的相邻节点,将这些节点压入栈中。接下来,它会重复弹出栈顶节点并访问其相邻节点的过程,直到栈为空。
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
| int tot = 0;
void dfs(Node *root) { if (root == NULL) return; int start, end; tot += 1; start = tot; if (root->lchild) dfs(root->lchild); if (root->rchild) dfs(root->rchild); tot += 1; end = tot; printf("%d : [%d, %d]\n", root->key, start, end); return; }
|
4、二叉树的遍历
Ⅰ、前序遍历
二叉树的前序遍历是指按照根节点、左子树、右子树的顺序遍历二叉树的所有节点。
1 2 3 4 5 6 7 8 9
|
void preorder(Node *root) { if (root != NULL) { printf("%d ", root->key); preorder(root->lchild); preorder(root->rchild); } }
|
Ⅱ、中序遍历
二叉树的中序遍历是指按照左子树、根节点、右子树的顺序遍历二叉树的所有节点。
1 2 3 4 5 6 7 8
| void inorder(Node *root) { if (root != NULL) { inorder(root->lchild); printf("%d ", root->key); inorder(root->rchild); } }
|
Ⅲ、后序遍历
二叉树的后序遍历是指按照左子树、右子树、根节点的顺序遍历二叉树的所有节点。
1 2 3 4 5 6 7 8
| void postorder(Node *root) { if (root != NULL) { postorder(root->lchild); postorder(root->rchild); printf("%d ", root->key); } }
|