相关介绍:

 二叉树是树的一种特殊形态,在二叉树中一个节点至多有左、右两个子节点,而在树中一个节点可以包含任意数目的子节点,对于森林,其是多棵树所组成的一个整体,树与树之间彼此相互独立,互不干扰,但其又是一个整体。树与二叉树之间、森林与二叉树之间可以相互的进行转换,且这种转换是一一对应的。树与森林转换成二叉树之后,森林与或树的相关操作都转换为二叉树的操作。在此,将讨论树的存储结构、树与森林,二叉树之间的对应关系与转换过程及相关代码。

二叉树的存储结构:

 在实际应用中,可根据具体的操作的特点将树设计成不同的存储结构。但无论采用何种存储方式,都要求树的存储结构不但能够存储各个节点本身的数据域信息,还要求能准确反映树中各个节点之间的逻辑关系。于此,介绍四种链式存储方式:

1.双亲链表存储结构

 双亲链表存储结构中的每一个节点存放的信息既包含节点本身的数据域信息,又包含指示双亲节点在存储结构中的位置。所以,这种存储结构可以设计成:以一组地址连续的存储单元来存放树中的各个节点,每一个节点中有两个域,一个为数据域,用来存储树中该节点本身的值;另一个是指针域,用来存储该节点的双亲节点在存储结构中的位置信息。下图演示了该存储结构:

分析: 采用双亲链表存储方式实现查找一个指定节点的双亲节点非常容易,但是要实现查找一个指定节点的孩子节点却并不容易,需要对整个链表扫描一遍

其双亲节点链表的代码描述如下:

相关代码:

public class Forest
{
private TreeNode[] tree; public Forest(int n)
{
tree=new TreeNode[n];
} class TreeNode
{
//数据域对象
Object data;
//父对象在数组中的位置
int parent;
}
}

2.孩子链表存储结构

 孩子链表存储结构中除了存放节点本身的数据域信息之外,还存放了其所有孩子节点在存储结构中的位置信息。由于每一个节点的子节点树不同,则可将一个节点的所有孩子的位置信息按从左到右的顺序链接成一个单链表,称此单链表为该链表的孩子链表,因此,该存储结构可设计为:以一组地址连续的存储单元来存放树中的各个节点,,每一个节点有两个域,一个为数据域,用来存储树中该节点的值;另一个为指针域,用来存放该节点的孩子链表的头指针。下图演示了该存储结构:

分析:

这种存储结构与双亲链表存储结构正好相反,它便于实现查找树中指定节点的孩子节点,但不便于实现查找树中指定节点的双亲节点。

其孩子链表存储结构的示例代码如下:

相关代码:

package all_in_tree;

import java.util.LinkedList;
import java.util.List; public class Forest
{
private TreeNode[] tree; public Forest(int n)
{
tree=new TreeNode[n];
} class TreeNode
{
//数据域对象
Object data;
//孩子链表的指针
List firstChild=new LinkedList();
}
}

3.双亲孩子链表存储结构

 双亲孩子链表存储结构的设计方法与孩子链表存储结构类似,其主体仍然是一个存储树中各个节点信息的数组。只不过数组中的元素含有三个域,比孩子链表存储结构中多了一个存放该节点的双亲节点在数组中位置的指针域。下图演示了该存储结构:

分析:

这种存储结构既便于实现查找树中指定节点的孩子节点,又便于实现查找树中指定节点的双亲节点。

其双亲孩子链表存储结构的示例代码如下:

相关代码:

package all_in_tree;

import java.util.LinkedList;
import java.util.List; public class Forest
{
private TreeNode[] tree; public Forest(int n)
{
tree=new TreeNode[n];
} class TreeNode
{
//数据域对象
Object data;
//双亲节点在数组中的位置
int parent;
//孩子链表的指针
List firstChild=new LinkedList();
}
}

4.孩子兄弟链表存储结构

 孩子兄弟链表存储结构又称为“左孩/右兄”二叉链式存储结构,它类似于二叉树的二叉链式存储结构,不同点在于链表中每个节点的左指针是指向该节点的第一个孩子,而右指针是指向该节点的右邻兄弟。其本质就是先将一棵二叉树转化为一棵二叉树后存储在二叉链式结构之中。下图演示了该存储结构:

分析:

这种存储结构与树所对应的二叉树的二叉链式存储结构相同,一切对于树的操作都将通过这种方式转化成对二叉树的操作。为此,该种存储方式应用更加广泛

其孩子兄弟链表存储结构的示例代码如下:

相关代码:

package all_in_tree;
public class Forest
{
private TreeNode root; class TreeNode
{
//数据域对象
Object data;
//其左孩子,右兄弟的指针
TreeNode left,right;
}
}

树转换为二叉树:

 二叉树中的节点有左右孩子之分,而在无序树中节点的各个孩子之间是无次序之分的。为了操作方便,假设树是一棵有序树,树中每个节点的孩子按照从左到右的顺序进行编号,依次定义为第一个孩子、第二个孩子、....、第i个孩子。

 将树转换为二叉树的方法可以归纳为“加线”、“删除”、”旋转“3个步骤,其具体描述如下:

  1. 加线:将树中所有相邻的兄弟之间加一条连线。

  2. 删线:对树中的每一个节点,只保留它与第一个孩子节点之间的连线,删去它与其他孩子节点之间的连线。

  3. 旋转:以树的根节点为轴心,将树平面顺时针旋转一定的角度并适当的进行调整,使得转化后所得的二叉树看起来比较规整(该步骤不是必须)

下图给出了将树装换成二叉树的过程的示意图:

由转换过程可知,树与由它转换成的二叉树是一一对应的,树中的任意一个节点都对应着二叉树中的一个节点,树中每一个节点的第一个孩子节点在二叉树中是对应节点的左孩子,而树中每一节点的右邻兄弟在二叉树中是对应节点的右孩子(简而言之,左孩子,右兄弟)。以下,相关的代码中采用孩子链表存储结构来存储相关的树。

 注意到,对于任意一个节点,其孩子链表中的第一个节点的值为其当前节点的左孩子节点;孩子链表中的任意一个节点为其下一个孩子节点的双亲节点,同时,下一个孩子节点为该节点的右孩子节点。为此,我们可以采用以下的方式,实现树与二叉树之间的相互转换

相关代码:

package all_in_tree;

import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue; /**
* 该类用于演示树转化为二叉树的相关代码,其中,树采用孩子链表的存储结构
* 同时,借助一个Map,用于记录中间转化过程,以实现后期对二叉树的构造
* @author 学徒
*
*/
public class Forest
{
//树中节点的数目
private int nodeCount;
//用于记录树中各个存储节点的指针,便于树的构造过程
private Map<Integer,Node> nodeMap=new HashMap<Integer,Node>();
//用于描述树的孩子链表的存储结构
private TreeNode[] tree;
//用于记录树的中间转化过程中的相关信息
private MapNode[] map;
//转化后的二叉树的根节点索引
private Node root;
/**
* 中间记录转化过程的节点
* @author 学徒
*
*/
class MapNode
{
//存储相关的数据
Object data;
//存储其左孩子节点在数组中的位置的下标,初始化为-1表示没有
int left=-1;
//存储其右孩子节点在数组中的位置的下标,初始化为-1表示没有
int right=-1;
}
/**
* 用于创建一棵树具有n个节点的数
* @param n 树的节点数目
*
*/
public Forest(int n)
{
//存储树的节点的数目
this.nodeCount=n;
//用于初始化
tree=new TreeNode[n];
map=new MapNode[n];
for(int i=0;i<n;i++)
{
tree[i]=new TreeNode();
map[i]=new MapNode();
nodeMap.put(i, new Node());
}
root=(Node)nodeMap.get(0);
}
/**
* 用于设置树中某个节点的数据域的相关值
* @param index 树中节点在数组中的下标
* @param data 树的数据域
*/
public void setTreeData(int index,Object data)
{
tree[index].data=data;
map[index].data=data;
nodeMap.get(index).data=data;
}
/**
* 用于增加树中某个节点的孩子节点
* @param index
* @param childIndex
*/
public void addTreeChild(int index,int childIndex)
{
if(tree[index].firstChild==null)
tree[index].firstChild=new LinkedList<Integer>();
tree[index].firstChild.add(childIndex);
} /**
* 用于实现树转化为二叉树的过程,并返回该二叉树
*
*/
public Node toTranslateBinaryTree()
{
//用于遍历树的双亲孩子节点存储结构的数组,同时修改和记录其中间结果的值,便于后序二叉树的构造
for(int i=0 ;i<this.nodeCount;i++)
{
//孩子节点的链表
Queue<Integer> childList=tree[i].firstChild;
//当前孩子节点的下标编号
int now=-1;
//上一孩子节点的下标编号,将其默认值设置为当前节点
int previous=i;
//当其孩子链表存在的时候
if(childList!=null)
{
//当孩子链表存在时,由于第一个节点为其当前节点的左孩子节点。为此,用于设置其左孩子节点
if(!childList.isEmpty())
{
now=childList.poll();
map[previous].left=now;
previous=now;
}
//处理剩下的孩子节点
while(!childList.isEmpty())
{
now=childList.poll();
map[previous].right=now;
previous=now;
}
}
}
//根据记录的中间结果构造出一棵二叉树
for(int i=0;i<this.nodeCount;i++)
{
int left=map[i].left;
int right=map[i].right;
//用于获取二叉树中相关的节点
Node node=nodeMap.get(i);
//用于构造该二叉树
if(left!=-1)
node.left=nodeMap.get(left);
if(right!=-1)
node.right=nodeMap.get(right);
}
root=nodeMap.get(0);
return root;
}
}
/**
* 用于存储树中双亲孩子节点的信息
* @author 学徒
*
*/
class TreeNode
{
//存储相关数据
Object data;
/* //其双亲节点在数组中位置的下标
int parent;*/
//用于存储孩子节点的孩子节点链表
Queue<Integer> firstChild;
/**
* 用于增加孩子节点的方法
*/
public void addChild(int child)
{
firstChild.add(child);
}
}
/**
* 二叉树中的节点描述类
* @author 学徒
*/
class Node
{
Object data;
Node left;
Node right;
}
/**
* 用于测试使用的类,该代码采用的测试用例即为“树转换成二叉树的过程示意图”中的树
* @author 学徒
*
*/
class Test
{
public static void main(String[] args)
{
Forest forest=new Forest(8);
forest.setTreeData(0,"A");
forest.setTreeData(1,"B");
forest.setTreeData(2, "C");
forest.setTreeData(3,"D");
forest.setTreeData(4,"E");
forest.setTreeData(5,"F");
forest.setTreeData(6,"G");
forest.setTreeData(7, "H");
forest.addTreeChild(0, 1);
forest.addTreeChild(0, 2);
forest.addTreeChild(0, 3);
forest.addTreeChild(1, 4);
forest.addTreeChild(1, 5);
forest.addTreeChild(5, 7);
forest.addTreeChild(3, 6);
Node root=forest.toTranslateBinaryTree();
//得到二叉树中序遍历的结果
System.out.print("中序遍历的结果 :");
inRootTraver(root);
System.out.println();
//得到二叉树后序遍历的结果
System.out.print("后序遍历的结果 :");
postRootTraver(root);
}
/**
* 用于得到二叉树中序遍历的结果
*/
public static void inRootTraver(Node root)
{
if(root!=null)
{
inRootTraver(root.left);
System.out.print(root.data+" ");
inRootTraver(root.right);
}
} /**
* 用于得到二叉树后序遍历的结果(Node root)
*/
public static void postRootTraver(Node root)
{
if(root!=null)
{
postRootTraver(root.left);
postRootTraver(root.right);
System.out.print(root.data+" ");
}
}
} 其运行结果如下: 中序遍历的结果 :E H F B C G D A
后序遍历的结果 :H F E G D C B A

二叉树转换成树:

 二叉树转换成树是由树转换成二叉树的一个逆过程,也就是一个由二叉树还原成它原来所对应的树的过程,其步骤如下:

  1. 加线:若某节点是其双亲节点的左孩子,则将该节点沿着右分支向下的所有节点与该节点的双亲节点用线连接

  2. 删线:将书中所有本来双亲节点与右孩子节点的连线删除

  3. 旋转:对经过步骤1、2两步后所得的树以根节点为轴心,按逆时针方向旋转一定的角度并做适当调整,使得转化后所得的树看起来比较规整

下图给出了将二叉树转化为树的过程:

以下,相关的代码中采用孩子链表存储结构来存储相关的树。

其相关的代码如下:

相关代码:

package all_in_tree;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue; /**
* 用于实现将一棵二叉树转化为树的相关操作,其中树采用孩子链表节点的存储结构
* 同时,借助一个Map用来记录二叉树中节点和树存储结构中的数组的下标的编号的对应关系
* @author 学徒
*
*/
public class ReverseForest
{
//用于记录该树二叉树
private Node root;
//二叉树中节点的数目
private int nodeCount;
//用于记录树中各个存储节点的指针,便于树的构造过程
private Map<Integer,Node> nodeMap=new HashMap<Integer,Node>();
//用于记录树中各个指针对应的存储节点的编号,便于树的构造
private Map<Node,Integer> indexMap=new HashMap<Node,Integer>();
//用于描述树的孩子链表的存储结构
private TreeNode[] tree; public ReverseForest(Node root)
{
this.root=root;
//对二叉树采用任意一种遍历方式,用于计算出其总节点的数目同时初始化其映射Map,此处采用层次遍历的方式
Queue<Node> q=new LinkedList<Node>();
q.add(root);
nodeMap.put(this.nodeCount,root);
indexMap.put(root,this.nodeCount++ );
while(!q.isEmpty())
{
Node node = q.poll();
if(node.left!=null)
{
q.add(node.left);
nodeMap.put( this.nodeCount,node.left);
indexMap.put(node.left,this.nodeCount++);
}
if(node.right!=null)
{
q.add(node.right);
nodeMap.put(this.nodeCount,node.right);
indexMap.put(node.right, this.nodeCount++);
}
}
//初始化其孩子链表的存储结构
tree=new TreeNode[this.nodeCount];
//用于初始化各个树中的节点
for(Entry<Integer,Node> node:nodeMap.entrySet())
{
int index=node.getKey();
tree[index]=new TreeNode();
tree[index].data=node.getValue().data;
}
}
/**
* 用于增加树中某个节点的孩子节点
* @param index
* @param childIndex
*/
public void addTreeChild(int index,int childIndex)
{
if(tree[index].firstChild==null)
tree[index].firstChild=new LinkedList<Integer>();
tree[index].firstChild.add(childIndex);
}
/**
* 用于将二叉树转化为树
*/
public TreeNode[] toTranslateTree()
{
//用于遍历描述树的数组
for(int index=0;index<this.nodeCount;index++)
{
//得到该树节点
Node node=nodeMap.get(index);
//得到该树节点所对应的第一个孩子节点
node=node.left;
if(node!=null)
{
addTreeChild(index,indexMap.get(node));
while(node.right!=null)
{
addTreeChild(index, indexMap.get(node.right));
node=node.right;
}
}
}
return this.tree;
}
} class Tests
{
/**
* 测试代码,用于测试其运行结果
* @param args
*/
public static void main(String[] args)
{
TreeNode[] tree= new ReverseForest(Test.test()).toTranslateTree();
System.out.println();
for(int i=0;i<tree.length;i++)
{
System.out.print(i+(tree[i].data.toString())+": ");
Queue<Integer> q=tree[i].firstChild;
if(q!=null)
{
Iterator iterator =q.iterator(); while(iterator.hasNext())
{
System.out.print(iterator.next()+" ");
}
}
else
{
System.out.print("无孩子节点");
}
System.out.println();
}
}
}
/**
* 用于存储树中双亲孩子节点的信息
* @author 学徒
*
*/
class TreeNode
{
//存储相关数据
Object data;
//用于存储孩子节点的孩子节点链表
Queue<Integer> firstChild;
/**
* 用于增加孩子节点的方法
*/
public void addChild(int child)
{
firstChild.add(child);
}
}
/**
* 二叉树中的节点描述类
* @author 学徒
*/
class Node
{
Object data;
Node left;
Node right;
}
/**
* 用于测试使用的类,该代码采用的测试用例即为“树转换成二叉树的过程示意图”中的树
* @author 学徒
*
*/
class Test
{
public static Node test()
{
Forest forest=new Forest(8);
forest.setTreeData(0,"A");
forest.setTreeData(1,"B");
forest.setTreeData(2, "C");
forest.setTreeData(3,"D");
forest.setTreeData(4,"E");
forest.setTreeData(5,"F");
forest.setTreeData(6,"G");
forest.setTreeData(7, "H");
forest.addTreeChild(0, 1);
forest.addTreeChild(0, 2);
forest.addTreeChild(0, 3);
forest.addTreeChild(1, 4);
forest.addTreeChild(1, 5);
forest.addTreeChild(5, 7);
forest.addTreeChild(3, 6);
Node root=forest.toTranslateBinaryTree();
//得到二叉树中序遍历的结果
System.out.print("中序遍历的结果 :");
inRootTraver(root);
System.out.println();
//得到二叉树后序遍历的结果
System.out.print("后序遍历的结果 :");
postRootTraver(root);
return root;
}
/**
* 用于得到二叉树中序遍历的结果
*/
public static void inRootTraver(Node root)
{
if(root!=null)
{
inRootTraver(root.left);
System.out.print(root.data+" ");
inRootTraver(root.right);
}
} /**
* 用于得到二叉树后序遍历的结果(Node root)
*/
public static void postRootTraver(Node root)
{
if(root!=null)
{
postRootTraver(root.left);
postRootTraver(root.right);
System.out.print(root.data+" ");
}
}
} 运行结果:
中序遍历的结果 :E H F B C G D A
后序遍历的结果 :H F E G D C B A
0A: 1 3 5
1B: 2 4
2E: 无孩子节点
3C: 无孩子节点
4F: 6
5D: 7
6H: 无孩子节点
7G: 无孩子节点

森林转换成二叉树:

 森林是若干棵树的集合,而任何一棵和树对应的二叉树其根节点的右子树一定为空,因而可得到将森林转换成二叉树的方法如下:

  1. 将森林中的每棵树转换成相应的二叉树
  2. 按照森林中树的先后顺序,将一棵二叉树视为前一棵二叉树的右子树依次连接起来,从而构成一棵二叉树

下图给出了将森林转换为二叉树的过程:

从中可以看出,森林与二叉树之间存在着一一对应的关系。森林中第一棵树的根节点对应着二叉树的根节点;森林中第一棵树的根节点的子树所构成的森林对应着二叉树的左子树;森林中除第一棵树之外的其它树所构成的森林所对应着二叉树的右子树。

 除了将每棵树转化为相应的二叉树,之后按照森林中树的先后顺序,将一棵二叉树视为前一棵二叉树的右子树依次连接起来,构成一棵二叉树的方法之外,还存在着一种增加一个虚节点,之后将森林转化成一棵树,然后再按照将树转化成二叉树的方式转化成二叉树的方法

下图给出了增加一个虚节点将森林转换成二叉树的过程:

森林转化为二叉树的代码与树转化为二叉树的代码基本相同,此处不再重复

二叉树转换成森林:

 二叉树与森林之间存在着一一对应的关系,可以根据以下步骤将一棵二叉树转化为森林:

  1. 将二叉树的根节点的右孩子节点的各个连线之间依次断开,将一棵二叉树分解为多棵二叉树

  2. 依次将每一棵二叉树通过加线、删线、旋转的方式转换为一棵树

  3. 各个树构成的整体即为一个森林

下图给出了将一棵二叉树转化为森林的过程:

二叉树转化为森林的代码与二叉树转化为树的代码基本相同,此处不再重复

回到目录|·(工)·)

K:树、二叉树与森林之间的转换及其相关代码实现的更多相关文章

  1. 树形DP+(分组背包||二叉树,一般树,森林之间的转换)codevs 1378 选课

    codevs 1378 选课 时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond  题目描述 Description 学校实行学分制.每门的必修课都有固定的学分 ...

  2. 树&二叉树&哈夫曼树

    1.树 需要注意的两点:n(n>=0)表示结点的个数,m表示子树的个数 (1)n>0时,树的根节点是唯一的. (2)m>0时,子树的个数没有限制. 结点的度和树的度 (1)结点的度是 ...

  3. K:二叉树

    相关介绍:  二叉树是一种特殊的树,它的每个节点最多只有两棵子树,并且这两棵子树也是二叉树.由于二叉树中的两棵子树有左右之分,为此,二叉树是有序树. 二叉树的定义:  二叉树是由n(n>=0)个 ...

  4. 树&二叉树&&满二叉树&&完全二叉树&&完满二叉树

    目录 树 二叉树 完美二叉树(又名满二叉树)(Perfect Binary Tree) 完全二叉树(Complete Binary Tree) 完满二叉树(Full Binary Tree) 树 名称 ...

  5. iOS全角符与半角符之间的转换

    iOS全角符与半角符之间的转换 相关资料: 函数『CFString​Transform』中文 详情: 问题 1.17-03-15,「有人在群里边问怎么把『半角』符字符串转换成『全角』字符串?」,百度的 ...

  6. FFMPEG 实现 YUV,RGB各种图像原始数据之间的转换(swscale)

    FFMPEG中的swscale提供了视频原始数据(YUV420,YUV422,YUV444,RGB24...)之间的转换,分辨率变换等操作,使用起来十分方便,在这里记录一下它的用法. swscale主 ...

  7. Java中String与Date格式之间的转换

    转自:https://blog.csdn.net/angus_17/article/details/7656631 经常遇到string和date之间的转换,把相关的内容总结在这里吧: 1.strin ...

  8. 实体类与实体DTO类之间的转换

    实体类与实体DTO类之间的转换 实体类与实体DTO类之间的转换 1.通过使用第三方序列化反序列化工具Newtonsoft.Json 2.通过反射实现 3.通过表达式目录树加字典缓存实现 4. 通过表达 ...

  9. java中unicode utf-8以及汉字之间的转换工具类

    1.       汉字字符串与unicode之间的转换 1.1          stringToUnicode /** * 获取字符串的unicode编码 * 汉字"木"的Uni ...

随机推荐

  1. Nginx平台构架

    深入理解Nginx模块发开与架构解析读书笔记. nginx在启动后,在unix系统中会以daemon的方式(能够手动关闭 nginx.conf daemon off)在后台执行,后台进程包括一个mas ...

  2. Explicit Semantic Analysis (ESA)

    有别于LSA (Latent Semantic Analysis), 下列文章提出一种ESA (Explicit Semantic Analysis), 并介绍怎样使用ESA来进行语义相关性和文本分类 ...

  3. Woody的Python学习笔记1

    Python 是一种解释性语言:这意味着开发过程中省去了编译这个环节,类似于PHP\Perl. Python 是交互式语言:这意味着你能够在一个python提示符,直接互动运行写你的程序. Pytho ...

  4. java注解细节

    现在很多框架都使用注解了,典型的像Spring里面就可以看到大量的注解,比如@Service,@Controller这一类的都是注解.注解Annotation是java一项很重要的功能.下面就来整理一 ...

  5. linux启动失败

    如图 1.开机界面 按 e 键 2.选择第二个进入就好了 根据网上说的修改kernel 配置  加上 enforcing=0 无效 1.进入界面后再按 e 键 3.选择第二个按e键进入编辑 界面 每次 ...

  6. 安装Oracle之后在DOS命令下不能使用sqlplus命令启动Oracle?

    就像完成安装JDK后需要给系统配置环境变量JAVA_HOME和PATH,以便让系统认识在JDK里面的Javac命令.同样的,安装Oracle也需要这么一个过程.在正常情况下安装Oracle时系统安装程 ...

  7. Asp.Net Web API(三)

    Routing Tables路由表 在Asp.Net Web API中,一个控制器就是一个处理HTTP请求的类,控制器的public方法就被叫做action方法或简单的Action.当Web API接 ...

  8. 【java】method.invoke(方法底层所属对象/null,new Object[]{实际参数})

    反射调方法时无论是静态/非静态,固定/可变参数,都有Object对象数组对参数进行包装. package com.tn.clas; import java.lang.reflect.Method; i ...

  9. java.util.ConcurrentHashMap (JDK 1.8)

    1.1 java.util.ConcurrentHashMap继承结构 ConcurrentHashMap和HashMap的实现有很大的相似性,建议先看HashMap源码,再来理解Concurrent ...

  10. 零基础学习webpack打包管理

    这些天在项目之余的时间学习了webpack打包项目的东西,非常荣幸的找到一些大神的文章来学习,死劲嚼了几天,终于略知一二.在以后的工作上还需继续学习,下面我将分享我这几天学到的一点东西,希望能让我一个 ...