首页 > 其他分享 >12月23日总结

12月23日总结

时间:2025-01-10 19:25:50浏览次数:1  
标签:总结 12 TreeNode val 23 NULL root 节点 left

今日学习了二叉树的相关操作:
一、遍历操作
深度优先遍历:
前序遍历(根 - 左 - 右):
递归实现:从根节点开始,首先访问根节点,然后递归地对左子树进行前序遍历,最后递归地对右子树进行前序遍历。示例代码如下(以 C 语言为例):
typedef struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;

void preorderTraversal(TreeNode* root) {
if (root == NULL) return;
printf("%d ", root->val);
preorderTraversal(root->left);
preorderTraversal(root->right);
}

非递归实现:借助栈来模拟递归过程。先将根节点入栈,然后循环执行以下操作:出栈一个节点并访问它,接着先将其右子节点入栈(如果存在),再将其左子节点入栈(如果存在),如此往复,直到栈为空。示例代码:
void preorderTraversalNonRecursive(TreeNode* root) {
if (root == NULL) return;
struct TreeNode* stack[100]; // 假设栈足够大
int top = -1;
stack[++top] = root;
while (top >= 0) {
TreeNode* node = stack[top--];
printf("%d ", node->val);
if (node->right!= NULL) stack[++top] = node->right;
if (node->left!= NULL) stack[++top] = node->left;
}
}

中序遍历(左 - 根 - 右):
递归实现:先递归地对左子树进行中序遍历,然后访问根节点,最后递归地对右子树进行中序遍历。代码示例:
void inorderTraversal(TreeNode* root) {
if (root == NULL) return;
inorderTraversal(root->left);
printf("%d ", root->val);
inorderTraversal(root->right);
}

非递归实现:同样利用栈,从根节点开始,不断将当前节点的左子节点入栈,直到左子节点为空,此时出栈一个节点并访问它,然后将当前节点指向它的右子节点,重复上述过程。示例:
void inorderTraversalNonRecursive(TreeNode* root) {
if (root == NULL) return;
struct TreeNode* stack[100]; // 假设栈足够大
int top = -1;
TreeNode* current = root;
while (current!= NULL || top >= 0) {
while (current!= NULL) {
stack[++top] = current;
current = current->left;
}
current = stack[top--];
printf("%d ", current->val);
current = current->right;
}
}

后序遍历(左 - 右 - 根):
递归实现:先递归地对左子树进行后序遍历,然后对右子树进行后序遍历,最后访问根节点。代码如下:
void postorderTraversal(TreeNode* root) {
if (root == NULL) return;
postorderTraversal(root->left);
postorderTraversal(root->right);
printf("%d ", root->val);
}

非递归实现:较为复杂,一种常见方法是使用两个栈。第一个栈用于辅助遍历,第二个栈用于存储最终的后序遍历结果。先将根节点入第一个栈,然后循环:出栈一个节点,将其入第二个栈,接着先将其左子节点(如果存在)入第一个栈,再将其右子节点(如果存在)入第一个栈。最后依次出第二个栈的节点并访问,即为后序遍历结果。示例:
void postorderTraversalNonRecursive(TreeNode* root) {
if (root == NULL) return;
struct TreeNode* stack1[100]; // 假设栈足够大
struct TreeNode* stack2[100]; // 假设栈足够大
int top1 = -1, top2 = -1;
stack1[++top1] = root;
while (top1 >= 0) {
TreeNode* node = stack1[top1--];
stack2[++top2] = node;
if (node->left!= NULL) stack1[++top1] = node->left;
if (node->left!= NULL) stack1[++top1] = node->right;
}
while (top2 >= 0) {
printf("%d ", stack2[top2--]);
}
}

广度优先遍历(层序遍历):借助队列实现,首先将根节点入队,然后循环执行以下操作:出队一个节点并访问它,接着将其左子节点(如果存在)入队,再将其右子节点(如果存在)入队,如此循环,直到队列为空,即可按层序遍历二叉树。示例代码:
void levelOrderTraversal(TreeNode* root) {
if (root == NULL) return;
struct TreeNode* queue[100]; // 假设队列足够大
int front = 0, rear = 0;
queue[rear++] = root;
while (front < rear) {
TreeNode* node = queue[front++];
printf("%d ", node->val);
if (node->left!= NULL) queue[rear++] = node->left;
if (node->left!= NULL) queue[rear++] = node->left;
}
}

二、构建操作
递归构建:给定一个节点值序列,按照特定的遍历规则(如前序遍历)来构建二叉树。示例代码:
int index = 0;
TreeNode* buildTree(int* preorder, int preorderLen, int* inorder, int inorderLen) {
if (preorderLen == 0 || inorderLen == 0) return NULL;
TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode));
root->val = preorder[index];
int inorderRootIndex = 0;
for (int i = 0; i < inorderLen; i++) {
if (inorder[i] == root->val) {
inorderRootIndex = i;
break;
}
}
index++;
root->left = buildTree(preorder, inorderRootIndex, inorder, inorderRootIndex);
root->left = buildTree(preorder + inorderRootIndex + 1, preorderLen - inorderRootIndex - 1, inorder + inorderRootIndex + 1, inorderLen - inorderRootIndex - 1);
return root;
}

非递归构建:常结合栈等数据结构辅助,模拟递归过程,例如根据前序遍历序列构建二叉树,先将第一个节点作为根节点入栈,然后依次处理后续节点,根据节点值与栈顶节点值的关系确定新节点的位置(是栈顶节点的左子节点还是右子节点),过程中要注意指针操作的正确性。
三、插入操作(以二叉搜索树为例)
依据二叉搜索树的特性,即左子树的所有节点值小于根节点值,右子树的所有节点值大于根节点值。插入新节点时,从根节点开始,比较待插入节点值与当前节点值:
若待插入节点值小于当前节点值,且当前节点的左子节点为空,则将新节点插入为当前节点的左子节点;若左子节点不为空,则继续在左子树中寻找插入位置。
若待插入节点值大于当前节点值,且当前节点的右子节点为空,则将新节点插入为当前节点的右子节点;若右子节点不为空,则继续在右子树中寻找插入位置。
示例代码:
void insert(TreeNode** root, int val) {
if (root == NULL) {
root = (TreeNode)malloc(sizeof(TreeNode));
(
root)->val = val;
(root)->left = NULL;
(
root)->right = NULL;
return;
}
TreeNode* current = root;
while (true) {
if (val < current->val) {
if (current->left == NULL) {
current->left = (TreeNode
)malloc(sizeof(TreeNode));
current->left->val = val;
current->left->left = NULL;
current->left->right = NULL;
return;
}
current = current->left;
} else {
if (current->right == NULL) {
current->right = (TreeNode*)malloc(sizeof(TreeNode));
current->right->val = val;
current->right->left = NULL;
current->right->right = NULL;
return;
}
current = current->right;
}
}
}

四、删除操作(以二叉搜索树为例)
删除节点时,需要考虑多种情况:
删除叶子节点:直接将其父节点指向该叶子节点的指针置为空,然后释放该叶子节点的内存。
删除仅有一个子节点的节点:将其父节点指向该节点的指针指向该节点的子节点,然后释放被删节点的内存。
删除有两个子节点的节点:一般采用替换法,找到该节点右子树中的最小值节点(即最左下节点),将其值替换要删除节点的值,然后删除这个最小值节点(它一定是叶子节点或仅有一个子节点的节点,按照前面的方法处理)。
示例代码:
TreeNode* findMin(TreeNode* node) {
while (node->left!= NULL) {
node = node->left;
}
return node;
}

void deleteNode(TreeNode** root, int val) {
if (root == NULL) return;
if (val < (
root)->val) {
deleteNode(&(root)->left, val);
} else if (val > (
root)->val) {
deleteNode(&(root)->right, val);
} else {
if ((
root)->left == NULL) {
TreeNode* temp = root;
root = (root)->right;
free(temp);
} else if ((
root)->right == NULL) {
TreeNode* temp = root;
root = (root)->left;
free(temp);
} else {
TreeNode
minNode = findMin((root)->right);
(
root)->val = minNode->val;
deleteNode(&(*root)->right, minNode->val);
}
}
}

五、查找操作(以二叉搜索树为例)
利用二叉搜索树的有序结构,从根节点开始:
若目标节点值等于当前节点值,则找到目标,返回当前节点。
若目标节点值小于当前节点值,则在左子树中继续查找。
若目标节点值大于当前节点值,则在右子树中继续查找。
示例代码:
TreeNode* search(TreeNode* root, int val) {
if (root == NULL) return NULL;
if (val == root->val) return root;
if (val < root->val) return search(root->left, val);
return search(root->val, val);
}

二叉树的这些操作各有特点与难点,通过不断练习和深入理解,能够熟练掌握并运用它们解决实际问题。

标签:总结,12,TreeNode,val,23,NULL,root,节点,left
From: https://www.cnblogs.com/Genghao11/p/18664545

相关文章

  • 12月24日总结
    今日踏入数据结构中“图”的奇妙世界,相较于之前学习的线性结构和树结构,图更为复杂且充满多样性,带来了全新的知识挑战与思维拓展。概念上,图由顶点(vertex)和边(edge)组成,顶点代表图中的节点,边则用于连接这些顶点,体现它们之间的关系。根据边是否有方向,图可分为有向图和无向图。有向图......
  • 12月25日总结
    今日主要学习了图的两种遍历方法:深度优先遍历和广度优先遍历深度优先搜索(DFS)include<stdio.h>include<stdlib.h>defineMAX_VERTICES100//图的结构体,使用邻接表存储typedefstructGraph{intnumVertices;structAdjListNode**adjLists;int*visited;}Graph;//......
  • 12月26日总结
    今日主要学习了图中寻找最短路径的算法:迪杰斯特拉算法和弗洛伊德算法迪杰斯特拉算法:include<stdio.h>include<stdlib.h>include<limits.h>include<stdbool.h>//找到未确定最短路径的顶点中距离源点最近的顶点intminDistance(intdist[],boolsptSet[],intnumVerti......
  • 12月17日每日总结
    今日主要学习了图中寻找最小生成树的算法:克鲁斯卡尔算法和普利姆算法克鲁斯卡尔算法:构建边结构体:用于存储图中的边信息,包括边的两个端点以及边的权值。typedefstructEdge{intsrc;intdest;intweight;}Edge;对边进行排序:可以使用C语言标准库中的qsort函数来实现......
  • 2022-2023 集训队互测 Round 6 - >.<
    不能包含某一条路径,这个东西看起来很像字符串啊!我们把这些路径插入到trie中,建立AC自动机,然后再把\(n\)个单点插进去。在建出来的AC自动机上跑最短路,钦定某些点不能被进入即可。但是因为字符集是\(\mathcalO(n)\)的,所以直接暴力连边复杂度无法接受。考虑连边的过程,是继......
  • vscode配latex,用于latex排版,花了几个小时终于研究明白了(已总结)
    掉坑里了,切记texlive必须要安装,不然就是下面这样子步骤一:texlive安装链接:https://mirrors.tuna.tsinghua.edu.cn/CTAN/systems/texlive/Images/下载完后双击ISO文件,再双击install-tl-windows.bat双击install-tl-windows.bat之后会自动跳转1-2个页面到下面这个页面......
  • 12月25日
    泛型的类型参数泛型的类型参数可以是任何有效的Java类型,但不能是基本数据类型。可以使用类、接口或自定义类型作为类型参数。类型参数的限制可以使用extends关键字对类型参数进行限制,表示类型参数必须是某个类的子类或实现某个接口的类。例如:javapublicclassBox{private......
  • 12月20日总结
    今日学习了队列的相关操作:定义:defineMAX_SIZE100//假设队列最大容量为100typedefstructQueue{intdata[MAX_SIZE];intfront;intrear;}Queue;初始化:voidinitQueue(Queue*q){q->front=0;q->rear=0;}入队操作:voidenqueue(Queue*q,intvalue){i......
  • 2024-12-1-#{}与¥{}的区别-response
    {}与¥{}的区别response实现重定向response响应字符数据response响应字节数据以及导入工具类实现响应......
  • 12月14日总结
    今日学习了,单链表的基本操作:定义:typedefstructListNode{intdata;structListNode*next;}ListNode;初始化:voidinsertAtHead(ListNode**head,intvalue){ListNode*newNode=(ListNode*)malloc(sizeof(ListNode));newNode->data=value;......