MIT:算法导论——7.2.二叉树

基于WorkBody与Markdown构建公众号自动化发布工作流 自动化技术旨在通过标准化流程减少重复性劳动,其核心原理是将人工操作转化为可编程的指令序列。在内容创作领域,自动化工具的技术价值在于将创作者从繁琐的排版、格式调整与发布操作中解放出来,使其能更专注于核心的创意与深度写作。Markdown作为一种轻量级标记语言,以其纯文本、语义化与平台无关的特性,成为构建自动化内容管道的理想基础格式。结合如WorkBody等开源工具,可以搭建从本地写作到多平台发布的完整工作流,实现图片自动上传、格式转换与定时发布,特别适用于技术博客、文档维护与新媒体运营等场景。本文以公众号运营 阅读详情







====================博客补充====================================================

摘要

  书中第10章10.4小节介绍了有根树,简单介绍了二叉树和分支数目无限制的有根树的存储结构,而没有关于二叉树的遍历过程。为此对二叉树做个简单的总结,介绍一下二叉树基本概念、性质、二叉树的存储结构和遍历过程,主要包括先根遍历、中根遍历、后根遍历和层次遍历。

1、二叉树的定义

  二叉树(Binary Tree)是一种特殊的树型结构,每个节点至多有两棵子树,且二叉树的子树有左右之分,次序不能颠倒。

  由定义可知,二叉树中不存在度(结点拥有的子树数目)大于2的节点。二叉树形状如下下图所示:

2、二叉树的性质

(1)在二叉树中的第i层上至多有2^(i-1)个结点(i>=1)。备注:^表示此方

(2)深度为k的二叉树至多有2^k-1个节点(k>=1)。

(3)对任何一棵二叉树T,如果其终端结点数目为n0,度为2的节点数目为n2,则n0=n2+1。

满二叉树:深度为k且具有2^k-1个结点的二叉树。即满二叉树中的每一层上的结点数都是最大的结点数。

完全二叉树:深度为k具有n个结点的二叉树,当且仅当每一个结点与深度为k的满二叉树中的编号从1至n的结点一一对应。

可以得到一般结论:满二叉树和完全二叉树是两种特殊形态的二叉树,满二叉树肯定是完全二叉树,但完全二叉树不不一定是满二叉树。

举例如下图是所示:

(4)具有n个节点的完全二叉树的深度为log2n + 1。

3、二叉树的存储结构

  可以采用顺序存储数组和链式存储二叉链表两种方法来存储二叉树。经常使用的二叉链表方法,因为其非常灵活,方便二叉树的操作。二叉树的二叉链表存储结构如下所示:

1 typedef struct binary_tree_node
2 {
3     int elem;
4     struct binary_tree_node *left;
5     struct binary_tree_node *right;
6 }binary_tree_node,*binary_tree;

举例说明二叉链表存储过程,如下图所示:

从图中可以看出:在还有n个结点的二叉链表中有n+1个空链域。

4、遍历二叉树

  遍历二叉树是按照指定的路径方式访问书中每个结点一次,且仅访问一次。由二叉树的定义,我们知道二叉数是由根结点、左子树和右子树三部分构成的。通常遍历二叉树是从左向右进行,因此可以得到如下最基本的三种遍历方法:

(1)先根遍历(先序遍历):如果二叉树为空,进行空操作;否则,先访问根节点,然后先根遍历左子树,最后先根遍历右子树。采用递归形式实现代码如下:

复制代码
1 void preorder_traverse_recursive(binary_tree root)
2 {
3     if(NULL != root)
4     {
5         printf("%d\t",root->elem);
6         preorder_traverse_recursive(root->left);
7         preorder_traverse_recursive(root->right);
8     }
9 }
复制代码

具体过程如下图所示:

(2)中根遍历(中序遍历):如果二叉树为空,进行空操作;否则,先中根遍历左子树,然后访问根结点,最后中根遍历右子树。递归过程实现代码如下:

复制代码
1 void inorder_traverse_recursive(binary_tree root)
2 {
3     if(NULL != root)
4     {
5         inorder_traverse_recursive(root->left);
6         printf("%d\t",root->elem);
7         inorder_traverse_recursive(root->right);
8     }
9 }
复制代码

具体过程如下图所示:

(3)后根遍历(后序遍历):如果二叉树为空,进行空操作;否则,先后根遍历左子树,然后后根遍历右子树,最后访问根结点。递归实现代码如下:

复制代码
1 void postorder_traverse_recursive(binary_tree root)
2 {
3     if(NULL != root)
4     {
5         postorder_traverse_recursive(root->left);
6         postorder_traverse_recursive(root->right);
7         printf("%d\t",root->elem);
8     }
9 }
复制代码

具体过程如下图所示:

  写一个完整的程序练习二叉树的三种遍历,采用递归形式创建二叉树,然后以递归的形式遍历二叉树,后面会接着讨论如何使用非递归形式实现这三种遍历,程序采用C语言实现,完整程序如下:

#include <iostream>
#include <stack>
#include <queue>
#include <cstdlib>
using namespace std;

typedef struct binary_tree_node
{
    int elem;
    struct binary_tree_node *left;
    struct binary_tree_node *right;
}binary_tree_node,*binary_tree;

void init_binary_tree(binary_tree *root);
void create_binary_tree(binary_tree *root);
void preorder_traverse(binary_tree root);
void inorder_traverse(binary_tree root);
void inorder_traverse_two(binary_tree root);
void postorder_traverse(binary_tree root);
void levelorder_traverse(binary_tree root);

int main()
{
    binary_tree root;
    create_binary_tree(&root);
    cout<<"preodrer traverse: ";
    preorder_traverse(root);
    cout<<"\ninodrer traverse: ";
    inorder_traverse_two(root);
    cout<<"\npostodrer traverse: ";
    postorder_traverse(root);
    cout<<"\nleverorder traverse: ";
    levelorder_traverse(root);
    exit(0);
}

void init_binary_tree(binary_tree *root)
{
    *root = NULL;
}

void create_binary_tree(binary_tree* root)
{
    int elem;
    cout<<"Enter the node value(0 is end): ";
    cin>>elem;
    if(elem == 0)
        *root = NULL;
    else
    {
        *root = (binary_tree)malloc(sizeof(binary_tree_node));
        if(NULL == root)
        {
             cout<<"malloc error.\n";
             exit(-1);
        }
        (*root)->elem = elem;
        cout<<"Creating the left child node.\n";
        create_binary_tree(&((*root)->left));
        cout<<"Createing the right child node.\n";
        create_binary_tree(&((*root)->right));
    }
}

void preorder_traverse(binary_tree root)
{
    if(NULL != root)
    {
        stack<binary_tree_node*> s;
        binary_tree_node *ptmpnode;
        s.push(root);
        while(!s.empty())
        {
            ptmpnode = s.top();
            cout<<ptmpnode->elem<<" ";
            s.pop();
            if(NULL != ptmpnode->right)
                s.push(ptmpnode->right);
            if(NULL != ptmpnode->left)
                s.push(ptmpnode->left);

        }
    }
}
void inorder_traverse(binary_tree root)
{
    if(NULL != root)
    {
        stack<binary_tree_node*> s;
        binary_tree_node *ptmpnode;
        s.push(root);
        while(!s.empty())
        {
            ptmpnode = s.top();
            while(NULL != ptmpnode)
            {
                s.push(ptmpnode->left);
                ptmpnode = s.top();
            }
            s.pop();
            if(!s.empty())
            {
                ptmpnode = s.top();
                cout<<ptmpnode->elem<<" ";
                s.pop();
                s.push(ptmpnode->right);
            }
        }
    }
}

void inorder_traverse_two(binary_tree root)
{
    if(NULL != root)
    {
        stack<binary_tree_node*> s;
        binary_tree_node *ptmpnode;
        ptmpnode = root;
        while(NULL != ptmpnode || !s.empty())
        {
            //将左子树结点入栈
            if(NULL != ptmpnode)
            {
                s.push(ptmpnode);
                ptmpnode = ptmpnode->left;
            }
            else
            {
                //出栈遍历
                ptmpnode = s.top();
                s.pop();
                cout<<ptmpnode->elem<<" ";
                //右子树结点
                ptmpnode = ptmpnode->right;
            }
        }
    }
}

void postorder_traverse(binary_tree root)
{
    if(NULL != root)
    {
        stack<binary_tree_node*> s;
        binary_tree_node *ptmpnode;
        int flags[100];
        ptmpnode = root;
        while(NULL != ptmpnode || !s.empty())
        {
            //将结点左子树结点入栈
            while(NULL != ptmpnode)
            {
                s.push(ptmpnode);
                flags[s.size()] = 0;   //标记未访问
                ptmpnode=ptmpnode->left;
            }
            //输出访问的结点
            while(!s.empty() && flags[s.size()] == 1)
            {
                ptmpnode = s.top();
                s.pop();
                cout<<ptmpnode->elem<<" ";
            }
            //从右子树开始遍历
            if(!s.empty())
            {
                ptmpnode = s.top();
                flags[s.size()] = 1;  //登记访问了
                ptmpnode = ptmpnode->right;
            }
            else
                break;
        }
    }
}
void levelorder_traverse(binary_tree root)
{
    if(NULL != root)
    {
        queue<binary_tree_node*> q;
        binary_tree_node *ptmpnode;
        q.push(root);
        while(!q.empty())
        {
            ptmpnode = q.front();
            q.pop();
            cout<<ptmpnode->elem<<" ";
            if(NULL != ptmpnode->left)
                q.push(ptmpnode->left);
            if(NULL != ptmpnode->right)
                q.push(ptmpnode->right);
        }
    }
}


程序测试结果如下:

  现在来讨论一下如何采用非递归实现这以上三种遍历。将递归形式转换为非递归形式,引入了额外的辅助结构栈。另外在讨论一次二叉树的层次遍历,可以借助队列进行实现。具体讨论如下:

(1)先根遍历非递归实现

  先根遍历要求顺序是根左右,可以借助栈s来实现。先将根root入栈,然后循环判断s是否为空,非空则将结点出栈,记为节点p,然后依次先将结点p的右子树结点入栈,然后将结点p的左子树结点入栈,循环操作直到栈中所有元素都出栈为止,出栈顺序即是先根遍历的结果。采用C++中模板库stack实现先根遍历如下:

复制代码
 1 void preorder_traverse(binary_tree root)
 2 {
 3     if(NULL != root)
 4     {
 5         stack<binary_tree_node*> s;
 6         binary_tree_node *ptmpnode;
 7         s.push(root);
 8         while(!s.empty())
 9         {
10             ptmpnode = s.top();
11             cout<<ptmpnode->elem<<" ";
12             s.pop();
13             if(NULL != ptmpnode->right)
14                 s.push(ptmpnode->right);
15             if(NULL != ptmpnode->left)
16                 s.push(ptmpnode->left);
17 
18         }
19     }
20 }
复制代码

(2)中根遍历非递归实现

  中根遍历要求顺序是左根右,借助栈s实现。先将根root入栈,接着从根root开始查找最左的子孩子结点直到为空为止,然后将空节点出栈,再将左子树节点出栈遍历,然后判断该左子树的右子树节点入栈。循环此过程,直到栈为空为止。此时需要注意的是入栈过程中空结点也入栈了,用以判断左孩子是否结束和左孩子是否有右孩子结点。采用C++中模板库stack实现先根遍历如下:

复制代码
 1 void inorder_traverse(binary_tree root)
 2 {
 3     if(NULL != root)
 4     {
 5         stack<binary_tree_node*> s;
 6         binary_tree_node *ptmpnode;
 7         s.push(root);
 8         while(!s.empty())
 9         {
10             ptmpnode = s.top();
11             while(NULL != ptmpnode)
12             {
13                 s.push(ptmpnode->left);
14                 ptmpnode = s.top();
15             }
16             s.pop();//空结点出栈
17             if(!s.empty())
18             {
19                 ptmpnode = s.top();
20                 cout<<ptmpnode->elem<<" ";
21                 s.pop();
22                 //右子树结点如栈
23                 s.push(ptmpnode->right);
24             }
25         }
26     }
27 }
复制代码

另外一种简洁的实现方法如下:

复制代码
 1 void inorder_traverse_two(binary_tree root)
 2 {
 3     if(NULL != root)
 4     {
 5         stack<binary_tree_node*> s;
 6         binary_tree_node *ptmpnode;
 7         ptmpnode = root;
 8         while(NULL != ptmpnode || !s.empty())
 9         {
10             //将左子树结点入栈
11             if(NULL != ptmpnode)
12             {
13                 s.push(ptmpnode);
14                 ptmpnode = ptmpnode->left;
15             }
16             else
17             {
18                 //出栈遍历
19                 ptmpnode = s.top();
20                 s.pop();
21                 cout<<ptmpnode->elem<<" ";
22                 //右子树结点
23                 ptmpnode = ptmpnode->right;
24             }
25         }
26     }
27 }
复制代码

(3)后根遍历递归实现

  后根遍历要求访问顺序是左右根,采用辅助栈实现时,需要一个标记,判断结点是否访问了,因为右子树是通过跟结点的信息得到的。实现过程是先将根结点及其左子树入栈,并初始标记为0,表示没有访问,然后通过判断栈是否为空和标记的值是否为1来判断是否访问元素。

参考:http://www.cnblogs.com/hicjiajia/archive/2010/08/27/1810055.html

采用C++模板库stack具体实现程序如下:

复制代码
 1 void postorder_traverse(binary_tree root)
 2 {
 3     if(NULL != root)
 4     {
 5         stack<binary_tree_node*> s;
 6         binary_tree_node *ptmpnode;
 7         int flags[100];
 8         ptmpnode = root;
 9         while(NULL != ptmpnode || !s.empty())
10         {
11             //将结点左子树结点入栈
12             while(NULL != ptmpnode)
13             {
14                 s.push(ptmpnode);
15                 flags[s.size()] = 0;   //标记未访问
16                 ptmpnode=ptmpnode->left;
17             }
18             //输出访问的结点
19             while(!s.empty() && flags[s.size()] == 1)
20             {
21                 ptmpnode = s.top();
22                 s.pop();
23                 cout<<ptmpnode->elem<<" ";
24             }
25             //从右子树开始遍历
26             if(!s.empty())
27             {
28                 ptmpnode = s.top();
29                 flags[s.size()] = 1;  //登记访问了
30                 ptmpnode = ptmpnode->right;
31             }
32             else
33                 break;
34         }
35     }
36 }
复制代码

(4)层次遍历实现

  层次遍历要求从根向下、从左向右进行访问,可以采用队列实现。先将根入队,然后队列进程出队操作访问结点p,再将结点p的左子树和右子树结点入队,循环执行此过程直到队列为空。出队顺序即是层次遍历结果。采用C++的模板库queue实现如下:

复制代码
 1 void levelorder_traverse(binary_tree root)
 2 {
 3     if(NULL != root)
 4     {
 5         queue<binary_tree_node*> q;
 6         binary_tree_node *ptmpnode;
 7         q.push(root);
 8         while(!q.empty())
 9         {
10             ptmpnode = q.front();
11             q.pop();
12             cout<<ptmpnode->elem<<" ";
13             if(NULL != ptmpnode->left)
14                 q.push(ptmpnode->left);
15             if(NULL != ptmpnode->right)
16                 q.push(ptmpnode->right);
17         }
18     }
19 }
复制代码

 综合上面的分析过程写个完整的程序测试二叉树遍历的非递归实现,采用C++语言,借助stack和queue实现,完整程序如下所示:

View Code

程序测试结果如下:





Coursera北大《数据结构基础》之二叉树 本文基于Coursera北大课程《数据结构基础》,所有文中非标注图片均来自课件,侵删 目录 1. 二叉树(binary tree)概念 1.1 二叉树定义 1.2 二叉树的五种形态 1.3 二叉树相关术语 1.4 几种特殊的二叉树 1.4.1 满二叉树 1.4.2 完全二叉树 1.4.3 扩充二叉树 1.4.4 表达式二叉树 1.5 二叉树主要性质 2. 二叉树的抽象数据类... 阅读详情

相关推荐

Java 17 VS Java 8: 新旧对决,这些Java 17新特性你不容错过

Java是一门非常流行的编程语言,由于其跨平台性、可移植性以及强大的面向对象特性而备受青睐。Java最初由Sun Microsystems公司于1995年推出,随着时间的推移,Java发展迅速,版本不断更新。本篇博客将重点介绍Java 17与Java 8的对比,以及Java 17的新特性。

陈书予 2万+

算法导论 — 7.2 快速排序的性能

笔记 本节只给出快速排序运行时间的简单分析。快速排序的运行时间取决于PARTITION划分是否平衡,而划分的平衡与否又取决于用于划分的主元的选择。   1. 最坏情况时间复杂度   对于一个含nnn个元素的数组,PARTITION划分的最坏情况为:划分产生的两个子数组的大小分别为n−1n-1n−1和000时。假设快速排序每一次递归调用PARTITION都产生了最坏情况划分,那么算法的运行时间T(n...

yangtzhou的专栏 1506

【Java】JAVA知识总结浅析

Java是一门功能强大的编程语言,广泛应用于多个领域。Java的编程思想,包括面向过程和面向对象编程,Java的发展历史,各版本的特点,JVM原理,数据类型,Java SE与Java EE的区别,应用场景,JDK与JDBC,Java Web技术,Spring Boot系列,以及MyBatis框架的基本实现,最后还将讨论Java在大数据、人工智能、嵌入式和Android等领域的应用。

wnm23的专栏 5221

《算法导论》第三版第7章 快速排序 练习&思考题 个人答案

7.1 快速排序的描述 7.1-1 解: 13, 19, 9, 5, 12, 8, 7, 4, 21, 2, 6, 11 9, 19, 13, 5, 12, 8, 7, 4, 21, 2, 6, 11 9, 5, 13, 19, 12, 8, 7, 4, 21, 2, 6, 11 9, 5, 8, 19, 12, 13, 7, 4, 21, 2, 6, 11 9, 5, 8, 7, 12, 13,...

张劲声的博客 1853

Java 8 至 Java 23 版本特性对比表

Java 8 至 Java 23 版本特性对比

小天一直在路上 2647

JDK、JRE、Java SE、Java EE和Java ME的详细解析

JDK、JRE、Java SE、Java EE和Java ME的详细解析

shaozheng0503的博客 2336

算法导论第七章课后答案

7.1-1 参照图7-1的方法,说明PARTITION在数组A={13,9,9,5,12,8,7,4,21,2,6,11}上的操作过程。 A={13,19,9,5,12,8,7,4,21,2,6,11}   ={13,19,9,5,12,8,7,4,21,2,6,11}   ={13,19,9,5,12,8,7,4,21,2,6,11}   ={9,19,13,5,12,8,7,4,2

z84616995z的专栏 1万+

2021 Java 这一年

Java2021年都干什么了?

qq_43529978的博客 7171

Java笔记——java学习文档_java文档

一JavaSE学习:一.Java基础1.Java的发展Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程 。Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点 [2] 。Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等。

a25847a的博客 1799

Flutter如何调用java接口如何导入java包

Flutter 无法直接调用 Java 接口或导入 Java 包。使用可以让 Flutter 与 Java 通信,从而间接调用 Java 方法。如果需要导入第三方 Java 包,可以通过 Android 模块配置并使用 Platform Channels 来集成其功能。结束语Flutter是一个由Google开发的开源UI工具包,它可以让您在不同平台上创建高质量、美观的应用程序,而无需编写大量平台特定的代码。我将学习和深入研究Flutter的方方面面。

江上清风山间明月的博客 1969

Macos安装java环境,快速切换Java版本

前言 java环境对我来说是非常重要的环境,平时用的工具很多是Java开发的。所以环境必不可少,目标主流的Java环境有java8和java11,因为很多软件需要的Java版本不一样。所以这2个环境我都需要同时安装。下面记录一下我的安装过程。 安装brew 我个人推荐使用brew安装Java,大部分软件安装我推荐使用brew【包管理】安装。如果没有安装brew,命令如下: ruby -e $(curl -fsSLhttps://raw.githubusercontent.com/Homebr.

xijieyuan2qi的博客 6113

【Java二十周年】回忆我与Java的点点滴滴

借Java二十周年之际,静下心来思考自己与Java的故事,屡屡思绪,记忆深处关于Java的点滴回忆便一层层铺展开来。本科——我的Java“启蒙老师”对于计算机这个口水颇多的专业,基本每个学校都会开设Java语言基础这个课程,只不过有的学校是选修课有的是必修课,这也是为什么毕业后听到很多同行好友说只懂C/C++不懂Java。幸运的是,在我们那个不知名的小学校,Java与C/C++一样是必修课,为什么

程序媛想事儿 8359

一文搞懂 Java、Java EE、Java SE 和 Java Web

Java 是一种开源的、面向对象的编程语言,由 Sun Microsystems(现为 Oracle)于 1995 年推出。Java 的口号是“一次编写,到处运行”(),意味着用 Java 编写的程序可以在任何支持 Java 的平台上运行。Java SE:基础版,适用于桌面和控制台应用程序的开发。Java EE:企业版,专为复杂的企业级应用程序设计。Java Web:基于 Web 的开发技术,包括 Servlets、JSP 和框架如 Spring。Java ME:面向嵌入式设备和移动设备的版本。

brhhh_sehe的博客 837

Java入门基础知识—java的搭建环境

如果程序中的代码没有问题,那么编译成功后,会在该目录下,产生字节码文件:HelloWorld.class文件。step2:将源文件进行编译,通过编译器:java的编译的命令:javac.exe。step1:编写Java源代码:(程序员干的,就是你),后缀是.java的文件。step3:解释执行字节码文件:解释器:java的解释的命令:java.exe。step2:需要编译器进行编译:产生字节码文件,后缀是.class的文件。bin目录:存放java的可执行文件,编译器,街十字等工具。

关注我!带你一路 "狂飙" 到底! 1380

Java基础之Java枚举

絮叨 昨天刚好有遇到一个枚举的小问题,然后发现自己并不是那么熟悉它,然后在开发中,枚举用的特别多,所以有了今天的文章。 什么是枚举 Java中的枚举是一种类型,顾名思义:就是一个一个列举出来。所以它一般都是表示一个有限的集合类型,它是一种类型,在维基百科中给出的定义是: 在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常(但不总是)重叠.。枚举是一个被命名的整型常数的集合,枚举在日常生活中很常见,例如表示星期的SUNDAY、MO

bieber007的博客 4730

Java自学路线图之Java系统自学

文章目录第一阶段 Java零基础自学路线图--Java基础自学第二阶段 Java零基础自学路线图--Java语言进阶第三阶段 Java零基础自学路线图--JavaWeb自学第四阶段 Java零基础自学路线图--第一部分框架第五阶段 Java零基础自学路线图--第二部分框架Java自学资源 Java自学不是一朝一夕的事情。可以采用“懒开始”的方法,但是必须要坚持下去,才能真正自学Java掌握编程技...

CZ-001的博客 1529

Java笔记——Java基础_随着java技术的不断发展

提到面向对象,先想到的时面向过程。PrintStream和OutputStream相比,除了添加了一组print()/println()方法,可以打印各种数据类型,比较方便外,它还有一个额外的优点,就是不会抛出IOException,这样我们在编写代码的时候,就不必捕获IOException。(Java Platform Standard Edition):标准版,是为开发普通桌面应用的解决方案,JavaEE和JavaME都由此发展而来,包括了Java的核心库,如数据类型,集合,数据库连接和网络编程等。

a25847a的博客 1052

Java笔记——Java基础入门

我想给大家说的是,写错代码都是很正常的,刚才有同学在使用IDEA编写程序时,可能会觉得字体比较小,背景色是黑色的,注释是灰色的,看不清,长时间可能对眼睛不好。讲完IDEA相关配置之后,接下来给大家讲一个很重要的IDEA的使用技巧,这就是IDEA的快捷键,所谓快捷键就是通过键盘上的按键组合起来,就可以帮我们生成代码。为了引导大家正确的完成IDEA的下载和安装,给小伙伴们单独提供了一个文档《IDEA安装、使用、配置.pdf》,文档中提供了IDEA详细的安装和使用步骤,大家只需要按照文档的步骤一步一步操作就行。

a25847a的博客 773
上一篇: MIT:算法导论——8.散列表
下一篇: MIT:算法导论——9.二叉搜索树
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值