数据结构-树

:是n(n>=0)个结点的有限集。n=0称为空树。n>0有以下特点:

特点:
 - 1、有且仅有一个特点的结点,称为根节点
 - 2、当n>1,其余结点可分为m(m>0)个互不相交的有限集,其中每一个集合本身又是一棵树,并且称为根的子树。

树的相关概念:
度:结点拥有的子树数目。
结点层次:从根开始定义起,根为第一层,根的孩子为第二层,以此类推。
树的深度:树中结点的最大层数称为树的深度或者高度。

二叉树:二叉树是n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成。

特点:
1、每个结点最多有两颗子树,所以二叉树中不存在度大于2的结点。
2、左子树和右子树是有顺序的,次序不能任意颠倒。
3、即使树中某结点只有一棵子树,也要区分它是左子树还是右子树。

满二叉树:在一棵二叉树中。如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。

特点:
1、叶子只能出现在最下一层。出现在其它层就不可能达成平衡。
2、非叶子结点的度一定是2。
3、在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。

完全二叉树:对一颗具有n个结点的二叉树按层编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。(满二叉树一定是完全二叉树,但反过来不一定成立。)

特点:
1、叶子结点只能出现在最下层和次下层。
2、最下层的叶子结点集中在树的左部。
3、倒数第二层若存在叶子结点,一定在右部连续位置。
4、如果结点度为1,则该结点只有左孩子,即没有右子树。
5、同样结点数目的二叉树,完全二叉树深度最小。

二叉排序树(二叉查找树、二叉搜索树):要么是空树,要么满足以下特点:

特点:
1、若任意节点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2、若任意节点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
3、 任意节点的左、右子树也分别为二叉查找树;
4、 没有键值相等的节点。

平衡二叉树(AVL):它或者是一颗空树,或者具有以下性质的二叉排序树:它的左子树和右子树的深度之差(平衡因子)的绝对值不超过1,且它的左子树和右子树都是一颗平衡二叉树。

特点:
1、它必须是二叉查找树
2、每个节点的左子树和右子树的高度差至多为1。

红黑树(R-B Tree):它一种特殊的二叉查找树。红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。

特点:
1、每个节点或者是黑色,或者是红色
2、根节点是黑色
3、每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
4、如果一个节点是红色的,则它的子节点必须是黑色的
5、从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点

注意:
特性1中的叶子节点,是只为空(NIL或null)的节点。
特性5,确保没有一条路径会比其他路径长出俩倍。因而,红黑树是相对是接近平衡的二叉树。

二叉树的遍历

  • 前序遍历:DLR–前序遍历(根在前,从左往右,一棵树的根永远在左子树前面,左子树又永远在右子树前面 )

  • 中序遍历:LDR–中序遍历(根在中,从左往右,一棵树的左子树永远在根前面,根永远在右子树前面)

  • 后续遍历:LRD–后序遍历(根在后,从左往右,一棵树的左子树永远在右子树前面,右子树永远在根前面)

  • 层序遍历:层序遍历嘛,就是按层,从上到下,从左到右遍历

时间复杂度的计算:
T(n),表示算法需要执行的运算次数。
定义:存在常数 c 和函数 f(N),使得当 N >= c 时 T(N) <= f(N),表示为 T(n) = O(f(n)) 。
算法的时间复杂度,用来度量算法的运行时间,记作: T(n) = O(f(n))。它表示随着 输入大小n 的增大,算法执行需要的时间的增长速度可以用 f(n) 来描述。
显然如果 T(n) = n^2,那么 T(n) = O(n^2),T(n) = O(n^3),T(n) = O(n^4) 都是成立的,但是因为第一个 f(n) 的增长速度与 T(n) 是最接近的,所以第一个是最好的选择,所以我们说这个算法的复杂度是 O(n^2) 。

特点:
1、当 T(n) = c,c 为一个常数的时候,我们说这个算法的时间复杂度为 O(1);如果 T(n) 不等于一个常数项时,直接将常数项省略。
2、如果一个算法的执行次数是 T(n),那么只保留最高次项,同时忽略最高项的系数后得到函数 f(n),此时算法的时间复杂度就是 O(f(n))。为了方便描述,下文称此为 大O推导法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: (Tree)是一种非线性的数据结构,它由n(n>=0)个结点构成,其中一个结点为根结点,其余结点可分为m(m>=0)个互不相交的子集T1、T2、……、Tm,其中每一个子集本身又是一棵,并称为根的子的特点是:每个结点有零个或多个子结点;没有父结点的结点称为根结点;每一个非根结点有且只有一个父结点;除了根结点之外,每个子结点可以分为多个不相交的子。 下面是一个基本的的结构定义: ```c // 的结构体定义 typedef struct TreeNode { int data; // 数据域 struct TreeNode *firstChild;// 第一个子节点 struct TreeNode *nextSibling;// 兄弟节点 }TreeNode, *Tree; ``` 其中,data表示结点的数据域,firstChild指向该结点的第一个子节点,nextSibling指向该结点的下一个兄弟节点。 下面是一个创建的函数: ```c // 创建 Tree createTree(int data) { Tree root = (Tree)malloc(sizeof(TreeNode)); root->data = data; root->firstChild = NULL; root->nextSibling = NULL; return root; } ``` 下面是一个向中添加子节点的函数: ```c // 添加子节点 void addChild(Tree parent, int data) { Tree child = (Tree)malloc(sizeof(TreeNode)); child->data = data; child->firstChild = NULL; child->nextSibling = NULL; if (parent->firstChild == NULL) parent->firstChild = child; else { Tree p = parent->firstChild; while (p->nextSibling != NULL) p = p->nextSibling; p->nextSibling = child; } } ``` 下面是一个先序遍历的函数: ```c // 先序遍历 void preOrderTraversal(Tree root) { if (root != NULL) { printf("%d ", root->data); preOrderTraversal(root->firstChild); preOrderTraversal(root->nextSibling); } } ``` 下面是一个后序遍历的函数: ```c // 后序遍历 void postOrderTraversal(Tree root) { if (root != NULL) { postOrderTraversal(root->firstChild); postOrderTraversal(root->nextSibling); printf("%d ", root->data); } } ``` 下面是一个层次遍历的函数: ```c // 层次遍历 void levelOrderTraversal(Tree root) { Queue q; initQueue(&q); if (root != NULL) { enQueue(&q, root); while (!isQueueEmpty(&q)) { Tree p = deQueue(&q); printf("%d ", p->data); if (p->firstChild != NULL) enQueue(&q, p->firstChild); if (p->nextSibling != NULL) enQueue(&q, p->nextSibling); } } } ``` 其中,Queue是一个队列结构体。 以上就是一个简单的的实现。 ### 回答2: 是一种常见的数据结构,用于存储具有层次关系的数据。它由节点和边组成,通常包含一个根节点和若干子节点。 在C语言中,我们可以使用结构体来定义的节点。一个最基本的节点结构体可能包含两个成员变量:数据和指向子节点的指针。例如: ``` typedef struct TreeNode { int data; struct TreeNode* left; struct TreeNode* right; } TreeNode; ``` 上述代码定义了一个名为TreeNode的结构体,它包含一个整型数据成员和两个指向左子节点和右子节点的指针。 为了方便操作,我们可以定义一些基本的函数。其中,创建节点的函数可以使用动态内存分配来分配新节点的内存空间。例如: ``` TreeNode* createNode(int data) { TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode)); newNode->data = data; newNode->left = NULL; newNode->right = NULL; return newNode; } ``` 我们还可以定义插入节点、删除节点、搜索节点等函数来操作的结构。 除此之外,还可以实现一些遍历的算法,如先序遍历、中序遍历和后序遍历。这些遍历方法可以递归地遍历的节点,并对节点进行指定的操作。 编写的操作函数时,需要考虑到不同的特点,例如二叉搜索要保持左子节点的值小于根节点,右子节点的值大于根节点。 总之,编写一个经典C语言数据结构-需要定义节点的结构体,实现节点的创建、插入、删除和搜索等操作函数,同时可以实现遍历的算法。 ### 回答3: 是一种经典的数据结构,C语言可以很方便地实现的相关操作。首先,我们可以定义一个的节点结构体: ```c typedef struct Node { int data; // 节点存储的数据 struct Node* left; // 左子指针 struct Node* right; // 右子指针 } Node; ``` 接下来,我们可以实现一些的基本操作,例如创建、插入节点、删除节点和遍历。下面是一个简单的示例: ```c // 创建一个节点 Node* createNode(int data) { Node* newNode = (Node*)malloc(sizeof(Node)); newNode->data = data; newNode->left = NULL; newNode->right = NULL; return newNode; } // 在中插入一个节点 Node* insertNode(Node* root, int data) { if (root == NULL) { return createNode(data); } else if (data < root->data) { root->left = insertNode(root->left, data); } else if (data > root->data) { root->right = insertNode(root->right, data); } return root; } // 在中删除一个节点 Node* deleteNode(Node* root, int data) { if (root == NULL) { return root; } else if (data < root->data) { root->left = deleteNode(root->left, data); } else if (data > root->data) { root->right = deleteNode(root->right, data); } else { if (root->left == NULL) { Node* temp = root->right; free(root); return temp; } else if (root->right == NULL) { Node* temp = root->left; free(root); return temp; } Node* temp = findMinNode(root->right); root->data = temp->data; root->right = deleteNode(root->right, temp->data); } return root; } // 遍历:前序遍历(中-左-右) void preOrderTraversal(Node* root) { if (root != NULL) { printf("%d ", root->data); preOrderTraversal(root->left); preOrderTraversal(root->right); } } ``` 这只是的基本实现,还有很多其他操作,例如查找节点、判断是否为空、计算的高度等。可以根据具体需求进行扩展。通过以上实现,我们可以使用经典的C语言来构建和操作结构。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值