数据结构:二叉树

1 二叉树定义
二叉树是每个结点最多有两个子树的树结构。二叉树不允许存在度⼤于2的树。
它有五种最基本的形态:
二叉树可以是空集;
根可以有空的左子树或者右子树;
左右子树都是空。只有左子树或者右子树的叫做斜树。

2 二叉树的概念和性质

2.1 完全二叉树和满二叉树

满二叉树

 

完全二叉树
如果一棵深度为k,有n个结点的二叉树中各结点能够与深度为k的顺序编号的满二叉树从1到n标号的结点相对应的二叉树称为完全二叉树。
特点:
所有的叶结点都出现在第k层或k-1层
若任一结点,如果其右子树的最⼤层次为i,则其左子树的最⼤层次为i或i+1

 2.2 二叉树的性质(不用背,理解就行)

性质1: 在二叉树的第i层上的结点最多为2^(i-1) 个。(i ≥ 1)
性质2:   深度为k的二叉树至多有2^k -1个结点。(i ≥ 1)
性质3: 在一棵二叉树中,叶结点的数目比度为2的结点数目多一个。
             推导过程:
                             n = 各类结点个数之和 = n0 + n1 + n2
                             n = 分叉数+1 = n1 + 2*n2 + 1
                             n0 + n1 + n2 = n1 + 2*n2 + 1
                             故n0 = n2 + 1

 性质4:具有N个结点的完全二叉树的深度为log2 N+1。(向下取整)

性质5:(必须是完全二叉树)

如果有一棵n个结点的 完全二叉树 ,其结点编号按照层次序(从上到下,从左到右),则除根结点外,满足[i/2 , i, 2i,2i+1]的规则。
如图,i = 5时,i/2=2(2是5的父结点),2i=10,2i+1=11(10,11是5的孩子结点)
           i = 7时,i/2=3(3是7的父结点),2i=14,2i+1=15(14是7的孩子结点,15不存在)

3 二叉树的存储

3.1 顺序存储
顺序存储,有天然的下标索引[0,1,2,3,4,...n],我们需要规定下标的运算规则。
依靠性质5,可以将任意棵二叉树构造成满二叉树结构或完全二叉树结构,依据下标规则,就可以找到⽗结点,子结点。
如图,这棵树虽然不是完全二叉树,但我们可以将其构造成完全二叉树,把不存在的结点赋值为0。(这种方法只适用于接近完全二叉树的树,如果差的太多,需要补充的结点过多,浪费空间)。

 3.2 链式存储

由于二叉树的每个结点最多只能有两个子树,每个结点定义两个指针域和一个数据域即可。
找到根结点,顺着往下走就行

4 二叉树的遍历 

4.1遍历思想

遍历 :沿某条搜索路径周游二叉树,对树中的每一个节点访问一次且仅访问一次。
对线性结构⽽言,只有一条搜索路径(因为每个结点均只有一个后继),故不需要另加讨论。
二叉树是非线性结构,每个结点有两个后继,则存在如何遍历即按什么样的搜索路径进行遍历的问题。
<1>深度遍历
        每个结点的处理方式一样
        大任务分解成小任务,小任务处理完后,最终回到大任务处结束(递 归)
        先序遍历:
          访问结点
          结点左边
          结点右边
 根据访问结点的先后顺序,还有下面两种情况
        中序遍历:
          处理结点左边
          访问结点
          处理结点右边
        后序遍历:
          处理结点左边
          处理结点右边
          访问结点
先: ABCDEFGHK
中: BDC AEHGKF
后: DCBHKGFE A
先序遍历代码:
binaryTree.h
#ifndef BINARY_TREE_H
#define BINARY_TREE_H
#include <stdlib.h>
#include <stdio.h>

typedef int Element;
// 树的结点结构
typedef struct treeNode {
	Element data;
	struct treeNode *left;
	struct treeNode *right;
}TreeNode;

// 二叉树的描述信息结构
typedef struct {
	TreeNode *root;			// 二叉树的根结点
	int count;				// 二叉树的结点个数
}BinaryTree;

BinaryTree *createBinaryTree(TreeNode *root);
void releaseBinaryTree(BinaryTree *tree);

TreeNode *createTreeNode(Element e);
void insertBinaryTree(BinaryTree *tree, TreeNode *parent, TreeNode *left, TreeNode *right);
void visitTreeNode(TreeNode *node);
void preOrderBTreeRecur(BinaryTree *tree);				// 先序遍历tree,使用递归方法
#endif
binaryTree.c
#include "binaryTree.h"

/* 申请二叉树的信息体,若有根结点,那么指向根结点,并更新树的结点个数 */
BinaryTree *createBinaryTree(TreeNode *root) {
	BinaryTree *tree = (BinaryTree *) malloc(sizeof(BinaryTree));
	if (tree == NULL) {
		fprintf(stderr, "tree malloc failed!\n");
		return NULL;
	}
	if (root) {
		tree->root = root;
		tree->count = 1;
	} else {
		tree->root = NULL;
		tree->count = 0;
	}
	return tree;
}
/* 产生新结点,初始化左右指针为NULL */
TreeNode *createTreeNode(Element e) {
	TreeNode *node = (TreeNode *) malloc(sizeof(TreeNode));
	if (node == NULL) {
		fprintf(stderr, "tree node malloc failed!\n");
		return NULL;
	}
	node->data = e;
	node->left = node->right = NULL;
	return node;
}
/* 向父节点插入左右结点 */
void insertBinaryTree(BinaryTree *tree, TreeNode *parent, TreeNode *left, TreeNode *right) {
	if (tree && parent) {
		parent->left = left;
		parent->right = right;
		if (left)
			tree->count++;
		if (right)
			tree->count++;
	}
}

static void preOrder(TreeNode *node) {
	if (node) {
		visitTreeNode(node);
		preOrder(node->left);
		preOrder(node->right);
	}
}

void preOrderBTreeRecur(BinaryTree *tree) {
	if (tree) {
		preOrder(tree->root);
	}
}

void visitTreeNode(TreeNode *node) {
	printf("\t%c", node->data);
}
main.c
#include "binaryTree.h"

BinaryTree *initTree1() {
	TreeNode *nodeA = createTreeNode('A');
	TreeNode *nodeB = createTreeNode('B');
	TreeNode *nodeC = createTreeNode('C');
	TreeNode *nodeD = createTreeNode('D');
	TreeNode *nodeE = createTreeNode('E');
	TreeNode *nodeF = createTreeNode('F');
	TreeNode *nodeG = createTreeNode('G');
	TreeNode *nodeH = createTreeNode('H');
	TreeNode *nodeK = createTreeNode('K');

	BinaryTree *tree = createBinaryTree(nodeA);
	insertBinaryTree(tree, nodeA, nodeB, nodeE);
	insertBinaryTree(tree, nodeB, NULL, nodeC);
	insertBinaryTree(tree, nodeE, NULL, nodeF);
	insertBinaryTree(tree, nodeC, nodeD, NULL);
	insertBinaryTree(tree, nodeF, nodeG, NULL);
	insertBinaryTree(tree, nodeG, nodeH, nodeK);
	return tree;
}

int main() {
	BinaryTree *tree = initTree1();
	printf("preOrder: ");
	preOrderBTreeRecur(tree);
	printf("\n");
	return 0;
}
在CLion上的运行结果:
根据遍历结果重构二叉树
<2>广度遍历
          层次遍历:根据 ⽗子关系,知道了⽗,那么就把其所有的子结点都看一遍
          如图所示,我们可以用队列来实现广度遍历
          que:A
          A入队,再出队后,可以访问A,可以知道A的左右孩子B和E,B和E入队
          que: B, E
          que:E
          B出队,访问B,同时可以知道B的右孩子C,C入队
          que:E,C
          E出队,访问E,可以知道E的右孩子F,F入队
          que:C,F
          依次类推,直到队列为空,得到树的广度遍历:A B E C F D G H K
          
          

二叉树完整代码:[增加了广度遍历(用到队列),中序遍历,后续遍历,非递归遍历(用到栈)]

arrayQueue.h

#ifndef ARRAY_QUEUE_H
#define ARRAY_QUEUE_H
#include "binaryTree.h"

#define MaxQueue 20
// 定义顺序队列的结构
typedef struct {
	pTreeNode data[MaxQueue];
	int front;
	int rear;
}ArrayPTreeQueue;

ArrayPTreeQueue *createArrayQueue();
void releaseArrayQueue(ArrayPTreeQueue *queue);

int enArrayQueue(ArrayPTreeQueue *queue, pTreeNode e);
int deArrayQueue(ArrayPTreeQueue *queue, pTreeNode *e);
#endif

arrayQueue.c

#include "arrayQueue.h"

ArrayPTreeQueue *createArrayQueue() {
	ArrayPTreeQueue *queue = (ArrayPTreeQueue *)malloc(sizeof(ArrayPTreeQueue));
	if (queue == NULL) {
		printf(".....!\n");
		return NULL;
	}
	queue->front = queue->rear = 0;
	return queue;
}

void releaseArrayQueue(ArrayPTreeQueue *queue) {
	if (queue) {
		free(queue);
	}
}

int enArrayQueue(ArrayPTreeQueue *queue, pTreeNode e) {
	// 先判断队列满不满
	if ((queue->rear + 1) % MaxQueue == queue->front) {
		printf("Queue full!\n");
		return -1;
	}
	queue->rear = (queue->rear + 1) % MaxQueue;
	queue->data[queue->rear] = e;
	return 0;
}

int deArrayQueue(ArrayPTreeQueue *queue, pTreeNode *e) {
	// 先判断队列空不空
	if (queue->rear == queue->front) {
		// printf("Queue empty!\n");
		return -1;
	}
	queue->front = (queue->front + 1) % MaxQueue;
	*e = queue->data[queue->front];
	return 0;
}

arrayStack.h

#ifndef ARRAYSTACK_H
#define ARRAYSTACK_H
/* 顺序栈 满递增栈*/
#include "binaryTree.h"
#define MaxStackSize 20
typedef struct {
	pTreeNode data[MaxStackSize];
	int top;
}ArrayStack;

ArrayStack *createArrayStack();
void releaseArrayStack(ArrayStack *stack);

int pushArrayStack(ArrayStack *stack, pTreeNode e);
int popArrayStack(ArrayStack *stack, pTreeNode *e);
#endif

arrayStack.c

#include <stdlib.h>
#include <stdio.h>
#include "arrayStack.h"

ArrayStack *createArrayStack() {
	ArrayStack *stack = (ArrayStack *) malloc(sizeof(ArrayStack));
	if (stack == NULL) {
		printf("malloc failed\n");
		return NULL;
	}
	stack->top = -1;
	return stack;
}

/* 数据能存放的位置 : [0, 1, ... max-1] */
int pushArrayStack(ArrayStack *stack, pTreeNode e) {
	// 上溢出的问题排除
	if (stack->top >= MaxStackSize - 1) {
		return -1;
	}
	stack->data[++stack->top] = e;
	return 0;
}

int popArrayStack(ArrayStack *stack, pTreeNode *e) {
	if (stack->top < 0) {
		return -1;
	}
	*e = stack->data[stack->top--];
	return 0;
}

void releaseArrayStack(ArrayStack *stack) {
	if (stack) {
		free(stack);
	}
}

binaryTree.h

#ifndef BINARY_TREE_H
#define BINARY_TREE_H
#include <stdlib.h>
#include <stdio.h>

typedef int Element;
// 树的节点结构
typedef struct treeNode {
	Element data;
	struct treeNode *left;
	struct treeNode *right;
}TreeNode;
typedef TreeNode* pTreeNode;

// 二叉树的描述信息结构
typedef struct {
	TreeNode *root;			// 二叉树的根节点
	int count;				// 二叉树的节点个数
}BinaryTree;

BinaryTree *createBinaryTree(TreeNode *root);
void releaseBinaryTree(BinaryTree *tree);

TreeNode *createTreeNode(Element e);
void insertBinaryTree(BinaryTree *tree, TreeNode *parent, TreeNode *left, TreeNode *right);
void visitTreeNode(TreeNode *node);
void preOrderBTreeRecur(BinaryTree *tree);				// 先序遍历tree,使用递归方法
void inOrderBTreeRecur(BinaryTree *tree);				// 中序遍历tree,使用递归方法
void postOrderBTreeRecur(BinaryTree *tree);				// 后序遍历tree,使用递归方法
// 层级遍历、广度遍历
void levelOrderBTree(BinaryTree *tree);

void preOrderBTreeNoRecur(BinaryTree *tree);
void inOrderBTreeNoRecur(BinaryTree *tree);
void postOrderBTreeNoRecur(BinaryTree *tree);
#endif

binaryTree.c

#include "binaryTree.h"
#include "arrayQueue.h"
#include "arrayStack.h"

/* 申请二叉树的信息体,若有根节点,那么指向根节点,并更新树的节点个数 */
BinaryTree *createBinaryTree(TreeNode *root) {
	BinaryTree *tree = (BinaryTree *) malloc(sizeof(BinaryTree));
	if (tree == NULL) {
		fprintf(stderr, "tree malloc failed!\n");
		return NULL;
	}
	if (root) {
		tree->root = root;
		tree->count = 1;
	} else {
		tree->root = NULL;
		tree->count = 0;
	}
	return tree;
}
/* 后序递归遍历,释放节点 */
static void destroyTreeNode(BinaryTree *tree, TreeNode *node) {
	if (node) {
		destroyTreeNode(tree, node->left);
		destroyTreeNode(tree, node->right);
		free(node);
		tree->count--;
	}
}
/* 释放二叉树,通过后序遍历的方式,将节点逐个释放 */
void releaseBinaryTree(BinaryTree *tree) {
	if (tree) {
		destroyTreeNode(tree, tree->root);
		printf("tree have %d node!\n", tree->count);
		free(tree);
	}
}
/* 产生新节点,初始化左右指针为NULL */
TreeNode *createTreeNode(Element e) {
	TreeNode *node = (TreeNode *) malloc(sizeof(TreeNode));
	if (node == NULL) {
		fprintf(stderr, "tree node malloc failed!\n");
		return NULL;
	}
	node->data = e;
	node->left = node->right = NULL;
	return node;
}
/* 向父节点插入左右节点 */
void insertBinaryTree(BinaryTree *tree, TreeNode *parent, TreeNode *left, TreeNode *right) {
	if (tree && parent) {
		parent->left = left;
		parent->right = right;
		if (left)
			tree->count++;
		if (right)
			tree->count++;
	}
}

static void preOrder(TreeNode *node) {
	if (node) {
		visitTreeNode(node);
		preOrder(node->left);
		preOrder(node->right);
	}
}

static void inOrder(TreeNode *node) {
	if (node) {
		inOrder(node->left);
		visitTreeNode(node);
		inOrder(node->right);
	}
}

static void postOrder(TreeNode *node) {
	if (node) {
		postOrder(node->left);
		postOrder(node->right);
		visitTreeNode(node);
	}
}

void preOrderBTreeRecur(BinaryTree *tree) {
	if (tree) {
		preOrder(tree->root);
	}
}

void visitTreeNode(TreeNode *node) {
	printf("\t%c", node->data);
}

void levelOrderBTree(BinaryTree *tree) {
	ArrayPTreeQueue *queue = createArrayQueue();
	pTreeNode node;
	enArrayQueue(queue, tree->root);			// 初始化任务队列
	while (deArrayQueue(queue, &node) != -1) {
		// 访问这个节点
		visitTreeNode(node);
		// 根据这个节点的左右情况,再次更新任务(如果左,就把左节点的地址入队,如果右...)
		if (node->left) {
			enArrayQueue(queue, node->left);
		}
		if (node->right) {
			enArrayQueue(queue, node->right);
		}
	}
	releaseArrayQueue(queue);
}

void inOrderBTreeRecur(BinaryTree *tree) {
	if (tree) {
		inOrder(tree->root);
	}
}

void postOrderBTreeRecur(BinaryTree *tree) {
	if (tree) {
		postOrder(tree->root);
	}
}

/* 非递归的先序遍历:
 * 1. 压入根节点到栈
 * 2. 弹出元素,访问该元素
 * 3. 将该元素的所知道的任务压入栈(有右压右,有左压左)
 * */
void preOrderBTreeNoRecur(BinaryTree *tree) {
	TreeNode *node;
	if (tree) {
		ArrayStack *stack = createArrayStack();
		pushArrayStack(stack, tree->root);
		while (popArrayStack(stack, &node) != -1 && node) {
			visitTreeNode(node);
			if (node->right) {
				pushArrayStack(stack, node->right);
			}
			if (node->left) {
				pushArrayStack(stack, node->left);
			}
		}
		releaseArrayStack(stack);
	}
}
/* 非递归的中序遍历:
 * 以根节点开始,整条左边进栈
 * 从栈中弹出节点,访问,然后以这个节点的右孩子为新节点
 * 再次安装整条左边进栈,再弹栈
 * */
void inOrderBTreeNoRecur(BinaryTree *tree) {
	TreeNode *node;
	if (tree->root) {
		ArrayStack *stack = createArrayStack();
		node = tree->root;
		while (stack->top >= 0 || node) {
			if (node) {
				pushArrayStack(stack, node);
				node = node->left;
			} else {
				popArrayStack(stack, &node);
				visitTreeNode(node);
				node = node->right;
			}
		}
		releaseArrayStack(stack);
	}
}
/* 1. 非递归的后序遍历,需要两个栈,第一个栈作为辅助,最后一个栈作为输出
 * 2. 第一个栈压入根节点后,弹出第二个栈,根节点就变成最后输出
 * 3. 后序遍历的倒数第二个应该是头节点的右孩子,所以辅助栈,先左后右
 * 4. 辅助栈弹出元素放入到第二个栈,这个节点先左后右放第一个栈
 * */
void postOrderBTreeNoRecur(BinaryTree *tree) {
	TreeNode *node;
	if (tree) {
		ArrayStack *stack1 = createArrayStack();
		ArrayStack *stack2 = createArrayStack();
		pushArrayStack(stack1, tree->root);	// 初始化辅助栈
		while (popArrayStack(stack1, &node) != -1) {
			pushArrayStack(stack2, node);
			if (node->left) {
				pushArrayStack(stack1, node->left);
			}
			if (node->right) {
				pushArrayStack(stack1, node->right);
			}
		}
		while (popArrayStack(stack2, &node) != -1) {
			visitTreeNode(node);
		}
		releaseArrayStack(stack1);
		releaseArrayStack(stack2);
	}
}

main.c

#include "binaryTree.h"

BinaryTree *initTree1() {
	TreeNode *nodeA = createTreeNode('A');
	TreeNode *nodeB = createTreeNode('B');
	TreeNode *nodeC = createTreeNode('C');
	TreeNode *nodeD = createTreeNode('D');
	TreeNode *nodeE = createTreeNode('E');
	TreeNode *nodeF = createTreeNode('F');
	TreeNode *nodeG = createTreeNode('G');
	TreeNode *nodeH = createTreeNode('H');
	TreeNode *nodeK = createTreeNode('K');

	BinaryTree *tree = createBinaryTree(nodeA);
	insertBinaryTree(tree, nodeA, nodeB, nodeE);
	insertBinaryTree(tree, nodeB, NULL, nodeC);
	insertBinaryTree(tree, nodeE, NULL, nodeF);
	insertBinaryTree(tree, nodeC, nodeD, NULL);
	insertBinaryTree(tree, nodeF, nodeG, NULL);
	insertBinaryTree(tree, nodeG, nodeH, nodeK);
	return tree;
}

int main() {
	BinaryTree *tree = initTree1();
	printf("树的节点数: %d\n", tree->count);
	printf("先序遍历: ");
	preOrderBTreeRecur(tree);
	printf("\n中序遍历: ");
	inOrderBTreeRecur(tree);
	printf("\n后序遍历: ");
	postOrderBTreeRecur(tree);
	printf("\n层次遍历: ");
	levelOrderBTree(tree);
	printf("\n非递归先序遍历: ");
	preOrderBTreeNoRecur(tree);
	printf("\nInorder: ");
	inOrderBTreeNoRecur(tree);
	printf("\nPostOrder:");
	postOrderBTreeNoRecur(tree);
	printf("\n");
	releaseBinaryTree(tree);
	return 0;
}

在CLion上的运行结果:

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值