当前位置: 首页 > news >正文

hello算法笔记 03

1 二叉树

/* 二叉树节点结构体 */
typedef struct TreeNode {int val;                // 节点值int height;             // 节点高度struct TreeNode *left;  // 左子节点指针struct TreeNode *right; // 右子节点指针
} TreeNode;/* 构造函数 */
TreeNode *newTreeNode(int val) {TreeNode *node;node = (TreeNode *)malloc(sizeof(TreeNode));node->val = val;node->height = 0;node->left = NULL;node->right = NULL;return node;
}
  1. 二叉树节点结构体(TreeNode)这个结构体定义了二叉树中每个节点的组成:

    • int val:存储节点的值,这里定义为整数类型
    • int height:存储节点的高度,这在平衡二叉树(如 AVL 树)中非常重要,用于维持树的平衡性
    • struct TreeNode *left:指向左子节点的指针
    • struct TreeNode *right:指向右子节点的指针
  2. 构造函数(newTreeNode)这个函数用于创建一个新的二叉树节点:

    • 首先声明一个 TreeNode 类型的指针node
    • 使用malloc函数为新节点分配内存空间,大小为sizeof(TreeNode)
    • 初始化节点的值为传入的参数val
    • 初始化节点高度为 0(表示叶子节点初始高度)
    • 初始化左右子节点指针为NULL(新节点初始没有子节点)
    • 返回创建好的节点指针

使用示例:

// 创建一个值为5的新节点
TreeNode *node = newTreeNode(5);
// 为该节点添加左子节点,值为3
node->left = newTreeNode(3);
// 为该节点添加右子节点,值为7
node->right = newTreeNode(7);

这段代码的特点是包含了height字段,这表明它很可能是为实现平衡二叉树(如 AVL 树)而设计的,普通二叉树通常不需要维护节点高度。

节点高度在平衡树中用于判断树是否失衡,以及需要进行何种旋转操作来恢复平衡。

2 二叉树的结构体

在 C 语言中,结构体(struct)的使用需要遵循特定的语法规则,结合你提供的代码,我们来详细说明结构体 TreeNode 的常见用法:

1. 结构体的声明与定义

你提供的代码首先声明了一个名为 TreeNode 的结构体:

typedef struct TreeNode {int val;                // 节点值int height;             // 节点高度struct TreeNode *left;  // 左子节点指针struct TreeNode *right; // 右子节点指针
} TreeNode;

这里的 typedef 是关键:它为 struct TreeNode 起了一个别名 TreeNode,因此后续可以直接用 TreeNode 代替 struct TreeNode 来使用,简化代码。

2. 结构体变量的创建

有两种方式可以创建 TreeNode 类型的变量:

(1)直接声明变量
TreeNode node1;  // 创建一个TreeNode类型的变量node1(在栈上分配内存)

此时可以通过 . 运算符访问其成员:

node1.val = 10;       // 给val赋值
node1.height = 1;     // 给height赋值
node1.left = NULL;    // 左子节点为空
node1.right = NULL;   // 右子节点为空
(2)创建指针(动态分配内存)

通过 malloc 在堆上分配内存,返回指针(更常用,尤其在树结构中):

TreeNode *node2 = (TreeNode*)malloc(sizeof(TreeNode));  // 动态分配内存

通过 -> 运算符访问指针指向的结构体成员:

node2->val = 20;      // 给val赋值
node2->height = 1;    // 给height赋值
node2->left = NULL;   // 左子节点为空
node2->right = NULL;  // 右子节点为空

3. 结合构造函数 newTreeNode 使用

你提供的 newTreeNode 函数本质上是一个封装了动态内存分配和初始化的工具函数,简化了节点创建过程:

TreeNode *newTreeNode(int val) {TreeNode *node;node = (TreeNode *)malloc(sizeof(TreeNode));  // 分配内存node->val = val;                              // 初始化值node->height = 0;                             // 初始化高度node->left = NULL;                            // 初始化左子节点node->right = NULL;                           // 初始化右子节点return node;                                  // 返回新节点指针
}

使用时直接调用即可创建一个初始化完成的节点:

TreeNode *root = newTreeNode(5);  // 创建值为5的根节点
root->left = newTreeNode(3);      // 为根节点添加左子节点(值为3)
root->right = newTreeNode(7);     // 为根节点添加右子节点(值为7)

4. 结构体指针的特殊用法(树结构核心)

在二叉树中,节点的 left 和 right 成员本身就是 TreeNode* 类型的指针,用于指向其子节点,从而构建树形结构:

// 构建一个简单的二叉树
TreeNode *root = newTreeNode(1);
root->left = newTreeNode(2);         // 根节点的左子节点是2
root->right = newTreeNode(3);        // 根节点的右子节点是3
root->left->left = newTreeNode(4);   // 节点2的左子节点是4

此时树的结构为:

plaintext

    1/ \2   3/
4

5. 注意事项

  • 内存释放:动态分配的节点(通过 malloc 或 newTreeNode 创建)在不需要时需用 free 释放,避免内存泄漏:
    free(node);  // 释放单个节点
    
    (释放整棵树需要递归遍历所有节点)
  • struct TreeNode* 与 TreeNode*:由于使用了 typedef,两者完全等价,后者更简洁。
  • height 成员:如前所述,这个成员用于平衡二叉树(如 AVL 树)中维护平衡性,普通二叉树可忽略。

通过以上方式,结构体 TreeNode 被用来构建二叉树的基本单元,配合指针操作实现树形结构的创建和遍历。

3 二叉树(java)

/* 二叉树节点类 */
class TreeNode {int val;         // 节点值TreeNode left;   // 左子节点引用TreeNode right;  // 右子节点引用TreeNode(int x) { val = x; }
}

初始化二叉树

// 初始化节点
TreeNode n1 = new TreeNode(1);
TreeNode n2 = new TreeNode(2);
TreeNode n3 = new TreeNode(3);
TreeNode n4 = new TreeNode(4);
TreeNode n5 = new TreeNode(5);
// 构建节点之间的引用(指针)
n1.left = n2;
n1.right = n3;
n2.left = n4;
n2.right = n5;

插入与删除节点

TreeNode P = new TreeNode(0);
// 在 n1 -> n2 中间插入节点 P
n1.left = P;
P.left = n2;
// 删除节点 P
n1.left = n2;

4 数组的长度是固定的

由于数组的长度是固定的,因此插入一个元素必定会导致数组尾部元素“丢失”。我们将这个问题的解决方案留在“列表”章节中讨论。

在 C 语言中,传统数组的长度确实是固定的,必须在定义时就确定大小,且后续无法直接修改。这是数组和动态内存(如 malloc 分配的空间)最核心的区别。

C 语言没有原生的 “动态数组” 类型(像 Java 的 ArrayList 那样能自动扩容的结构),但可以通过 “指针 + 内存分配函数” 手动实现动态数组的功能,核心是自己管理内存的申请、扩容和释放。

1. 传统数组的 “固定长度” 特性

  • 定义时必须指定长度:数组的大小必须是编译时就能确定的常量(如字面量、#define 定义的宏),不能用变量动态指定。
    // 正确:长度是字面量 5(编译时确定)
    int arr1[5] = {1,2,3,4,5};// 正确:长度是宏定义的常量(编译时替换为 10)
    #define SIZE 10
    int arr2[SIZE];// 错误:length 是变量(C99 前不支持,C99 支持变长数组但有局限性)
    int length = 5;
    int arr3[length]; 
    
  • 长度不可修改:数组一旦定义,占用的内存空间就固定了,无法像 Java 的 ArrayList 那样 “自动扩容” 或 “缩短”。若想调整大小,只能重新定义新数组,再手动拷贝数据。

2. 为什么会有 “长度固定” 的限制?

  • 内存分配机制:传统数组的内存是在 “栈区” 分配的,栈区内存的大小在编译时就需要确定,程序运行时会直接划分固定空间,无法动态调整。
  • 性能考量:固定长度能让编译器提前计算数组的内存地址,访问元素时(如 arr[i])可以直接通过公式定位,效率极高。

3. 如何实现 “动态长度” 需求?

如果需要类似 “可变长度” 的数组,可以用 C 语言的动态内存分配函数(需包含 stdlib.h),本质是在 “堆区” 申请内存,而非传统的栈区数组:

#include <stdlib.h>int main() {int length = 5;// 1. 动态申请内存(类似“创建可变长度数组”)int *dynamicArr = (int*)malloc(length * sizeof(int));// 2. 使用动态数组(和普通数组用法类似)for (int i = 0; i < length; i++) {dynamicArr[i] = i + 1; // 赋值:1,2,3,4,5}// 3. 动态调整大小(扩容到 10)length = 10;dynamicArr = (int*)realloc(dynamicArr, length * sizeof(int));// 4. 释放内存(堆区内存需手动释放,否则内存泄漏)free(dynamicArr);dynamicArr = NULL; // 避免野指针return 0;
}

【用malloc的是动态数组?什么是动态数组,是一开始对于数组长度不是硬编码的数组吗?根据输入自动分配吗?】

用 malloc(或 calloc/realloc)实现的数组可以称为动态数组,它的核心特点是数组长度可以在程序运行时动态确定和调整,而不是像传统数组那样在编译时就固定(硬编码)。

1. 什么是动态数组?

动态数组是指长度可以在程序运行时根据需求动态分配、调整的数组,它的关键特性包括:

  • 长度不固定:不需要在代码中硬编码数组大小(如 int arr[10] 中的 10 就是硬编码)。
  • 运行时确定:数组长度可以由用户输入、程序计算结果等动态决定(例如根据 scanf 读取的数值分配大小)。
  • 可调整大小:可以在程序运行中通过 realloc 扩容或缩容,灵活适应数据量变化。

简单说:动态数组的大小 “由程序运行时的情况决定”,而不是 “写代码时就固定死”。

2. 动态数组 vs 传统数组:核心区别在 “长度何时确定”

类型长度确定时机长度是否可修改内存分配位置典型代码示例
传统数组编译时(写代码时)不可修改(固定大小)栈内存或全局区int arr[5];(硬编码长度 5)
动态数组运行时(程序执行时)可修改(支持扩容)堆内存int *arr = malloc(n * sizeof(int));(n 由运行时确定)

3. 为什么 malloc 能实现动态数组?

malloc 的作用是在程序运行时从堆内存中申请指定大小的内存块,这正好满足动态数组的核心需求:

  • 例如 int n; scanf("%d", &n); int *arr = malloc(n * sizeof(int)); 中,n 是用户输入的数值(运行时确定),malloc 根据 n 的值分配对应大小的内存,实现了 “长度由输入动态决定”。
  • 后续如果需要更大的空间,可以用 realloc(arr, new_n * sizeof(int)) 调整内存大小,实现 “动态扩容”。

4. 动态数组的典型使用场景

当数组长度无法在写代码时确定,需要根据运行时的情况(如用户输入、文件内容大小等)灵活分配时,就需要动态数组:

  • 例如读取一个未知行数的文件,需要先统计行数,再动态分配对应大小的数组存储内容。
  • 又如实现一个可动态添加元素的列表(类似 Java 的 ArrayList),初始分配小空间,元素满了就自动扩容。

总结

动态数组的核心是 “长度在运行时动态确定和调整”,而 malloc 等函数提供了在运行时申请 / 调整内存的能力,因此是实现动态数组的基础工具。解决了传统数组 “长度硬编码、无法修改” 的局限,让数组能更灵活地适应不同场景的需求。

简单说:“动态” 体现在长度可以随程序运行时的情况变化,而不是写死在代码里

http://www.dtcms.com/a/406951.html

相关文章:

  • 沂水网站开发付钱做编程题目的网站
  • C++笔记(基础)string基础
  • 雨晨Win11PE_25H2_26200.6588紧急维护系统
  • 【鸿蒙心迹】摸蓝图,打地基
  • 小型教育网站的开发建设开题报告建设网咨询
  • 二级网站建设情况说明汕尾网站网站建设
  • 从零起步学习Redis || 第二章:Redis中数据类型的深层剖析讲解(下)
  • C++设计模式_创建型模式_原型模式Prototype
  • 简单直播TV1.4.3 | 一个软件观看四大平台,免去多应用切换烦恼
  • 设计模式-3D引擎中的设计模式
  • Linux安装配置Redis 7.2.3教程
  • 山西省城乡住房建设厅网站网站建设需要多少钱小江
  • 网站建设背景需要写些什么考研哪个培训机构比较好
  • JavaEE 初阶第二十五期:IP协议,网络世界的 “身份通行证”(一)
  • 有一个做炫舞官网活动的网站企业邮箱注册申请126
  • 服务器跨域问题CORS的解决
  • MyBatis进行级联查询
  • MySQL8.0.26-Linux版安装
  • 济南网站建设_美叶网络网址域名查询
  • 深入了解linux网络—— UDP网络通信
  • 招商加盟的网站应该怎么做宝坻做网站哪家好
  • 视频网站开发工具网站备案中是什么意思
  • 物理媒介和分组交换原理
  • Linux常用命令53——file
  • 西双版纳 网站建设网络建设与运维初级
  • 【Python】文件处理(一)
  • win10怎么做网站wordpress wooyun
  • 织梦网站登录网上做网站赚钱吗
  • Linux数据安全与备份策略完全指南
  • 哈尔滨网站建设服务公司暴雪游戏服务中心