二叉树:
二叉树基本特点:
- 每个节点最多只有两个子树,没有子树或者只有一个子树都是ok的。
- 左右子树有序的,一个节点如果有两个子树必须区分左右子树,即使只有一个子树也必须区分其是左子树还是右子树。
二叉树基本形态:
- 空二叉树
- 只有一个空结点。
- 根结点只有左子树。
- 根结点只有右子树
- 根结点既有左子树还有右子树。
特殊二叉树及其特点:
- 斜树:即一颗树只有左结点或者只有右结点。只有左结点称为左斜树,只有右结点称为右斜树。特点是每一层只有一个结点,即树的深度等于结点的个数。
- 满二叉树:一颗树所有的分支结点既有左子树也有右子树,并且叶都在同一层上,即为满二叉树。
- 完全二叉树:简单来说,除了最后一层结点之外,其他层结点必须有两颗子树,并且最后一层结点必须左排列。满二叉树必是完全二叉树。至于完全二叉树名字的由来,其实是和其存储结构有关。[具体详见](完全二叉树看起来并不完全,为什么叫完全二叉树呢? - 知乎 (zhihu.com))
二叉树的性质:
性质1:二叉树第i层上的结点个数最多是2^(i - 1)。
性质2:深度为k的二叉树最多有2^(k) - 1个节点(满二叉树)。
性质3:包含n个结点的二叉树深度至少为log2(n)+ 1。
性质4:任意一棵二叉树中,叶子结点的个数为n0(度为0),度为2的结点的个数为n2,则有
n0 = n2 + 1。
性质5:对于完全二叉树而言,对于第i个结点,若其有左子树,则其左子结点为2i,若其有右子树,则右子 结点为2i + 1,当然,左右子结点的大小不能大于结点总数n。
二叉树的存储结构:
顺序存储结构:
一般只适用于完全二叉树,因为可以最大限度的利用内存空间而不至于被浪费。[具体详见](完全二叉树看起来并不完全,为什么叫完全二叉树呢? - 知乎 (zhihu.com))
链式存储结构:
链式存储结构避免了空间上的浪费。
一般形式为
typedef struct TreeNode{
Type data;
struct TreeNode* left;
struct TreeNode* right;
}
当然也可以再加一个指向其父母的指针。
typedef struct TreeNode{
Type data;
struct TreeNode* left;
struct TreeNode* right;
struct TreeNode* parents;
}
遍历二叉树:
其实对于任何一种数据结构来说,最重要的莫过于遍历了,不然存了啥数据用不了都白瞎,二叉树的遍历最基础的有两种方式,递归和迭代。根据遍历顺序的不同又可以分为前序遍历,中序遍历,后序遍历,层序遍历。
其中递归和迭代本质上都是对于栈的运用,递归是隐式栈,迭代则是运用了前面学过的栈来显式的模拟递归函数压栈的方式。
当然,层序遍历则是用队列来模拟。
typedef struct TreeNode{
int val;
struct TreeNode* left;
struct TreeNode* right;
}
1.前序遍历:
遍历顺序:中左右。深度优先
原题链接:
递归实现:
中左右,递归一直往左走,沿路处理中间结点,到头找右。
void pre(struct TreeNode* root,int* rev, int* returnSize){
if(root==NULL){
return;
}
rev[(*returnSize)++] = root->val;
//*(rev + (*returnSize)++) = root->val;
pre(root->left, rev, returnSize);
pre(root->right, rev, returnSize);
}
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int *rev = (int*)malloc(sizeof(int)*100);
*returnSize = 0;
if(root==NULL){
return rev;
}
pre(root, rev, returnSize);
return rev;
}
迭代实现:
通过栈来实现迭代前序遍历。如果指针不为空就往左走,沿路处理中间结点,将中间结点压栈(方便等会找到右结点),到左边为空之后把栈顶元素出栈,往右走。循环执行往左走,存中间,再往右的过程,最后遍历完成整棵树。
int* preorderTraversal(struct TreeNode* root, int* returnSize) {
int *rev = (int*)malloc(sizeof(int)*100);
*returnSize = 0;
struct TreeNode* stack[100];
if(root==NULL){
return rev;
}
struct TreeNode* p = root;
int top = 0;
while(top > 0||p != NULL){
while(p != NULL){
*(rev + (*returnSize)++) = p ->val;
stack[top++] = p;
p = p->left;
}
p = stack[--top];
p = p->right;
}
return rev;
}
2.中序遍历:
遍历顺序:左中右,深度优先。
原题链接:
递归实现:
void inorder(struct TreeNode* root, int* rev, int *returnSize){
if(root==NULL){
return;
}
inorder(root->left, rev, returnSize);
//*(rev+*returnSize++) = root->val;
*(rev + (*returnSize)++) = root->val;
inorder(root->right, rev, returnSize);
}
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
int* rev = (int*)malloc(sizeof(int)*100);
*returnSize = 0;
inorder(root, rev, returnSize);
return rev;
}
迭代实现:
int* inorderTraversal(struct TreeNode* root, int* returnSize) {
int* rev = (int*)malloc(sizeof(int)*100);
struct TreeNode* stack[100];
*returnSize = 0;
int top = 0;
while(root != NULL || top > 0){
while(root!=NULL){
stack[top++] = root;
root = root->left;
}
root = stack[--top];
*(rev + (*returnSize)++) = root->val;
root = root->right;
}
return rev;
}
3.后序遍历:
遍历顺序:左右中,深度优先。
题目链接:
递归实现:
void postorder(struct TreeNode* root ,int* nums, int* returnSize){
if(root==NULL){
return;
}
postorder(root->left, nums,returnSize);
postorder(root->right, nums,returnSize);
*(nums+(*returnSize)++) = root->val;
}
int* postorderTraversal(struct TreeNode* root, int* returnSize) {
*returnSize = 0;
int *nums = (int*)malloc(sizeof(int)*100);
postorder(root, nums,returnSize);
return nums;
}
迭代实现:
int *postorderTraversal(struct TreeNode *root, int *returnSize) {
int *res = malloc(sizeof(int) * 2001);
*returnSize = 0;
if (root == NULL) {
return res;
}
struct TreeNode* stack[100];
int top = 0;
struct TreeNode *prev = NULL;
while (root != NULL || top > 0) {
while (root != NULL) {
stack[top++] = root;//不为空一直找最左边的
root = root->left;
}
root = stack[--top];//为空了弹出栈顶结点往右边找
//为了先一直找到最右边的再标记找过右边的不往右边找
if (root->right == NULL || root->right == prev) {
res[(*returnSize)++] = root->val;
prev = root;//记录找过右边的结点
root = NULL;//置root为空跳过第一个while不往左边找
} else {
stack[top++] = root;
root = root->right;
}
}
return res;
}
4.层序遍历:
遍历顺序:层层遍历,从左到右,广度优先。
原题链接:
迭代实现:
int** levelOrder(struct TreeNode* root,int* returnSize,int** returnColumnSizes){
*returnSize = 0;
if(root == NULL)
return NULL;
int** res = (int**)malloc(sizeof(int*) * 2000);
*returnColumnSizes = (int*)malloc(sizeof(int) * 2000);
struct TreeNode* queue[2000];
int front = 0;
int rear = 0;
struct TreeNode* p;
queue[rear++] = root;
while(front != rear){
int colSize = 0,last = rear;
res[*returnSize] = (int*)malloc(sizeof(int) * (last - front));
while(front < last){
p = queue[front++];
res[*returnSize][colSize++] = p ->val;
if(p -> left != NULL)//结点不为空左右结点先后入队
queue[rear++] = p -> left;
if(p -> right != NULL)
queue[rear++] = p -> right;
}
(*returnColumnSizes)[*returnSize] = colSize;
(*returnSize)++;
}
return res;
}