目录

一、问题描述

二、基本要求

三、逻辑结构设计

四、存储结构设计

五、主要操作设计

六、技术难点与解决方法

七、实现与展示

八、详细代码


一、问题描述

利用哈夫曼编码进行信息通讯可以大大提高信道利用率,缩短信息传输时间,降低传输成本。但是,这要求在发送端通过一个编码系统对待传数据预先编码;在接收端将传来的数据进行译码(复原)。对于双工信道 (即可以双向传输信息的信道),每端都需要一个完整的编/译码系统。试对于任意的一段文本(可能是直接输入的,也可能是保存在本地文件中或者网络上的),写一个哈夫曼码的编译码系统。

二、基本要求

一个完整的系统应具有以下功能:

(l)I:初始化 (Initialization)。从终端读入字符集大小 n,及 n 个字符和 m 个权值,建立哈夫曼树,并将它存于文件 hfmtree 中。

(2)C:编码 (Coding)。利用已建好的哈夫曼树(如不在内存,则从文件 hfmtree 中读入),对文件 tobetrans 中的正文进行编码,然后将结果存入文件 codefile 中。

(3)D:解码(Decoding)。利用已建好的哈夫曼树将文件 codefile 中的代码进行译码,结果存入文件 textfile 中。

(4)P:打印代码文件 (Print)。将文件 codefile 以紧凑格式显示在终端上,每行 50 个代码。同时将此字符形式的编码文件写入文件 codeprint 中。

(5)T:打印哈夫曼树 (Tree printing)。将已在内存中的哈夫曼树以直观的方式 (树或凹入表形式)显示在终端上,同时将此字符形式的哈夫曼树写入文件 treeprint 中。

三、逻辑结构设计

Huffman编码过程中会根据字符出现的概率高低来构造一棵树,存在一对多的关系,直观呈现“层次”特征,故Huffman编码用树形结构。

四、存储结构设计

Huffman树结构上有左右子结点的关系,故可以让一个存储结点包含其子树的邻接关系,也就是用链式存储结构。

图Huffman UML

五、主要操作设计

1.编码:当Huffman树tree初始化之后,tree调用coding()方法,获取单个字符与编码的关系,然后tree调用encoding()方法,与文件中的所有字符进行比对,完成输入内容的编码。

2. 解码:map中存放着字符与编码的关系,将传进来的字符串从头到尾一个一个地与map中的数据进行比对,发现相同,就将其输入到textfile中。

六、技术难点与解决方法

难点:实现过程中哈夫曼树的打印用JavaFX比较困难。

解决方法:在createPrintTree方法,传入相关参数,node是该哈夫曼树的root结点,row是X轴的偏移量,col是Y轴的偏移量,deep是树的深度。进入方法之后,根据坐标的计算,分别将对应的结点放在相应的位置。然后递归调用,先后处理父节点的左右孩子。

七、实现与展示

八、详细代码


import java.io.*;
import java.util.*;public class Coding {private Map<String,Object> codes;
//当Huffman树tree初始化之后,tree调用coding()方法,获取单个字符与编码的关系,然后tree调用encoding()方法,与文件中的所有字符进行比对,完成输入内容的编码public Coding(){codes=new HashMap<>();}public void buildHuffmanCodes() throws Exception{HuffmanTreeNode Htree=readTree();String buildCode="";code(Htree,buildCode,codes);}private void code(HuffmanTreeNode Htree,String buildCode,Map<String ,Object> codes){if (Htree!=null){if (Htree.getLeftChild()==null && Htree.getRightChild()==null){codes.put(buildCode,Htree.getData());}code(Htree.getLeftChild(),buildCode+"0",codes);code(Htree.getRightChild(),buildCode+"1",codes);}}private HuffmanTreeNode readTree() throws Exception{ObjectInputStream ois=new ObjectInputStream(new FileInputStream("hfmtree.txt"));try {return (HuffmanTreeNode) ois.readObject();} catch (IOException e){System.out.println("读取文件失败!");return null;}}public void save() throws Exception{DataOutputStream os=new DataOutputStream(new FileOutputStream("codefile.txt"));List<Object> text=readText();Set<String > keys=codes.keySet();try {for (int i=0;i<text.size();i++){for (String key: keys){if (text.get(i).equals(codes.get(key))) {os.writeUTF(key);break;}}}os.close();System.out.println("编码成功!已保存至文件(codefile.txt)!");} catch (IOException e){System.out.println("保存失败!");}}private List<Object> readText() throws Exception{DataInputStream is=new DataInputStream(new FileInputStream("tobetrans.txt"));List<Object> text=new ArrayList<>();try{while (true){text.add(is.readChar());}} catch (EOFException e){}return text;}public Map<String, Object> getCodes() {return codes;}public void setCodes(Map<String, Object> codes) {this.codes = codes;}
}
package experimentFinal2;import java.io.*;
import java.util.*;public class Decoding {private Map<String,Object> codes;//map中存放着字符与编码的关系,将传进来的字符串从头到尾一个一个地与map中的数据进行比对,发现相同,就将其输入到textfile中。public Decoding() throws Exception{codes=createMap();}public Map<String,Object> createMap() throws Exception{codes=new HashMap<>();List<String> code=readCode();for (int i=0;i<code.size();i++) {codes.put(code.get(i),null);}return codes;}public void DecodingHuffmanTree() throws Exception{HuffmanTreeNode Htree=readTree();String buildCode="";decode(Htree,buildCode,codes);}private void decode(HuffmanTreeNode Htree,String buildCode,Map<String ,Object> codes){if (Htree!=null){if (Htree.getLeftChild()==null && Htree.getRightChild()==null){codes.put(buildCode,Htree.getData());}decode(Htree.getLeftChild(),buildCode+"0",codes);decode(Htree.getRightChild(),buildCode+"1",codes);}}private HuffmanTreeNode readTree() throws Exception{ObjectInputStream ois=new ObjectInputStream(new FileInputStream("hfmtree.txt"));try {return (HuffmanTreeNode) ois.readObject();} catch (IOException e){System.out.println("读取文件失败!");return null;}}public List<String> readCode() throws Exception{DataInputStream is=new DataInputStream(new FileInputStream("codefile.txt"));List<String> codes=new ArrayList<>();try {while (true){codes.add(is.readUTF());}} catch (EOFException e){}return codes;}public void save() throws Exception{DataOutputStream oos=new DataOutputStream(new FileOutputStream("textfile.txt"));List<String> code=readCode();Set<String> keys=codes.keySet();try {for (int i=0;i<code.size();i++){for (String key: keys){if (code.get(i).equals(key)) {oos.writeChar((char)codes.get(key));break;}}}oos.close();System.out.println("解码成功!已保存至文件(textfile.txt)!");} catch (IOException e){System.out.println("保存失败!");}}public Map<String, Object> getCodes() {return codes;}public void setCodes(Map<String, Object> codes) {this.codes = codes;}
}
package experimentFinal2;import java.io.*;
import java.util.ArrayList;
import java.util.List;public class HuffmanTree {private HuffmanTreeNode Htree;private int leafNode=0;private int depth=0;public HuffmanTree(){Htree=new HuffmanTreeNode();}public void createHuffmanTree() throws Exception{List<HuffmanTreeNode> leafNodes=readFromlFile();this.leafNode=leafNodes.size();if (leafNode==0){return;}while (leafNodes.size()>1){sort(leafNodes);//排序HuffmanTreeNode LChild=leafNodes.get(0);//左孩子为0HuffmanTreeNode RChild=leafNodes.get(1);//右孩子为1HuffmanTreeNode parent=new HuffmanTreeNode(null,LChild.getWeight()+RChild.getWeight());//双亲的权值合为左孩子、右孩子权值的合parent.setLeftChild(LChild);parent.setRightChild(RChild);leafNodes.remove(0);leafNodes.remove(0);leafNodes.add(parent);}Htree=leafNodes.get(0);}public void creatNew(String s) throws Exception {DataOutputStream os=new DataOutputStream(new FileOutputStream("tobetrans.txt"));for (int i=0;i<s.length();i++){os.writeChar(s.charAt(i));}os.close();}private List<HuffmanTreeNode> readFromlFile() throws Exception{DataInputStream is=new DataInputStream(new FileInputStream("tobetrans.txt"));List<HuffmanTreeNode> leafNodes=new ArrayList<>();HuffmanTreeNode p;try {while (true) {//统计字符在文档中出现的概率 概率的大小作为权值p=new HuffmanTreeNode();char data=is.readChar();int index=weighting(leafNodes,data);if (index!=-1){leafNodes.get(index).setWeight(leafNodes.get(index).getWeight()+1);} else {p.setData(data);p.setWeight(1);leafNodes.add(p);}}} catch (EOFException e){}is.close();return leafNodes;}public void print() throws Exception{List<HuffmanTreeNode> leafNodes=readFromlFile();System.out.println("文件字符集:");for (HuffmanTreeNode node: leafNodes){System.out.println("字符:"+node.getData()+"   权值:"+node.getWeight());}System.out.println();}private int weighting( List<HuffmanTreeNode> leafNodes,char c){//找到相应的叶子结点并返回下标for (int i=0;i<leafNodes.size();i++){if (leafNodes.get(i).getData().equals(c)){return i;}}return -1;}private void sort(List<HuffmanTreeNode> leafNodes){//按权值排序for (int i=0;i<leafNodes.size();i++){for (int j=0;j<leafNodes.size()-1-i;j++){if (leafNodes.get(j).getWeight()>leafNodes.get(j+1).getWeight()){HuffmanTreeNode p=leafNodes.get(j);leafNodes.set(j,leafNodes.get(j+1));leafNodes.set(j+1,p);}}}}public void save() throws Exception{ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("hfmtree.txt"));try{oos.writeObject(Htree);oos.close();System.out.println("初始化成功!哈夫曼树已创建!");} catch (IOException e){System.out.println(e.getMessage());}}public void deepCount(HuffmanTreeNode Htree,int count){//求深度if (Htree!=null){count++;if (Htree.getLeftChild()==null && Htree.getRightChild()==null){//如果没有孩子if (count>this.depth){this.depth=count;}}deepCount(Htree.getLeftChild(),count);deepCount(Htree.getRightChild(),count);}}public HuffmanTreeNode getHtree() {return Htree;}public void setHtree(HuffmanTreeNode htree) {Htree = htree;}public int getLeafNode() {return leafNode;}public void setLeafNode(int leafNode) {this.leafNode = leafNode;}public int getDepth() {int count=0;deepCount(this.Htree,count);return depth;}public void setDepth(int depth) {this.depth = depth;}
}
package experimentFinal2;import java.io.Serializable;public class HuffmanTreeNode implements Serializable {private Object data;private int weight=0;private HuffmanTreeNode leftChild=null;private HuffmanTreeNode rightChild=null;private int parent=-1;public HuffmanTreeNode(){}public HuffmanTreeNode(Object data,int weight){this(data,weight,null,null,-1);}public HuffmanTreeNode(Object data,int weight,HuffmanTreeNode leftChild,HuffmanTreeNode rightChild, int parent){this.data=data;this.weight=weight;//权值this.leftChild=leftChild;//左孩子this.rightChild=rightChild;//右孩子this.parent=parent;//双亲}public Object getData() {return data;}public void setData(Object data) {this.data = data;}public int getWeight() {return weight;}public void setWeight(int weight) {this.weight = weight;}public HuffmanTreeNode getLeftChild() {return leftChild;}public void setLeftChild(HuffmanTreeNode leftChild) {this.leftChild = leftChild;}public HuffmanTreeNode getRightChild() {return rightChild;}public void setRightChild(HuffmanTreeNode rightChild) {this.rightChild = rightChild;}public int getParent() {return parent;}public void setParent(int parent) {this.parent = parent;}
}
package experimentFinal2;import java.io.*;public class Print {private StringBuilder code;public Print() throws Exception{code=readCode();}public void printCodefile(){System.out.println("codefile.txt:");int begin=0;int end=50;for (;begin<code.length();begin=begin+50,end=end+50){if (end>code.length()){end=code.length();}System.out.println(code.substring(begin,end));}}private StringBuilder readCode() throws Exception{DataInputStream is=new DataInputStream(new FileInputStream("codefile.txt"));StringBuilder codes=new StringBuilder();try {while (true){codes.append(is.readUTF());}} catch (EOFException e){}return codes;}public void save() throws Exception{DataOutputStream os=new DataOutputStream(new FileOutputStream("codeprint.txt"));try {os.writeUTF(code.toString());os.close();System.out.println("代码已写入文件(codeprint.txt)!");} catch (IOException e){System.out.println("保存失败!");}}
}
package experimentFinal2;import java.util.Scanner;public class Test {public static void main(String[] args) throws  Exception{System.out.println("数据压缩与解压缩:");int choice;boolean running=true;while (running){choice=menu();switch (choice){case 1: initialization();break;case 2: coding();break;case 3: decoding();break;case 4: print();break;case 5: treePrint();break;case 6: running=false;}}System.out.println("谢谢使用");}public static int menu(){while (true){System.out.println("-----------------------------------");System.out.println("请选择功能:                        ");System.out.println("   1. I:初始化(Initialization)    ");System.out.println("   2. C:编码(Coding)              ");System.out.println("   3. D:解码(Decoding)            ");System.out.println("   4. P:打印代码文件(Print)        ");System.out.println("   5. T:打印哈夫曼树(Tree printing)");System.out.println("   6. 退出                          ");System.out.println("-----------------------------------");System.out.print("请输入您的选择:");Scanner input =new Scanner(System.in);int choice=input.nextInt();if (choice>0 && choice<7){return choice;} else {System.out.println("该选项不存在,请重新输入!\n");}}}public static void initialization() throws Exception{Scanner input=new Scanner(System.in);HuffmanTree Htree=new HuffmanTree();System.out.println("--------------------------");System.out.println("1.新建字符集   2.读取文件");System.out.println("--------------------------");System.out.print("请输入您的选择:");int choice;while (true){choice=input.nextInt();if (choice==1 || choice==2){break;}System.out.println("选择错误,重新选择!");}if (choice==1){System.out.print("新字符集:");String s=input.next();Htree.creatNew(s);}Htree.print();Htree.createHuffmanTree();Htree.save();}//当Huffman树tree初始化之后,tree调用coding()方法,获取单个字符与编码的关系public static void coding() throws Exception{Coding coding=new Coding();coding.buildHuffmanCodes();coding.save();}public static void decoding() throws Exception{Decoding decoding=new Decoding();decoding.DecodingHuffmanTree();decoding.save();}public static void print() throws Exception{Print print=new Print();print.printCodefile();print.save();}public static void  treePrint() throws Exception{TreePrinting treePrinting=new TreePrinting();TreePrinting.main();treePrinting.save();}
}
package experimentFinal2;import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import java.io.DataOutputStream;
import java.io.FileOutputStream;public class TreePrinting extends Application {private String codes="";public static void main(){Application.launch();}public BorderPane pane() throws Exception{HuffmanTree Htree=new HuffmanTree();Pane pane=new Pane();GridPane gridPane=new GridPane();gridPane.setHgap(20);//水平间距gridPane.setVgap(20);//垂直间距BorderPane borderPane=new BorderPane();Text text=new Text("Enter a text");TextField textField=new TextField();textField.setMinWidth(800);Button button=new Button("Show Huffman Tree");button.setOnAction(e->{try {pane.getChildren().clear();Htree.creatNew(textField.getText());Htree.createHuffmanTree();Htree.save();Coding coding=new Coding();coding.buildHuffmanCodes();coding.save();Decoding decoding=new Decoding();String s=textField.getText()+" is encoded to ";for (String str: decoding.readCode()){this.codes+=str;}s+=codes;if (textField.getText() .equals("")){s="please enter a text !";}createTree(Htree.getHtree(),Htree.getDepth()*50,pane,50,400);Alert alert = new Alert(Alert.AlertType.INFORMATION);alert.titleProperty().set("Encode Text to Bits");alert.headerTextProperty().set(s);alert.showAndWait();} catch (Exception ex) {ex.printStackTrace();}});FlowPane flowPane=new FlowPane();flowPane.getChildren().addAll(text,textField,button);borderPane.setTop(flowPane);Text text1=new Text("Enter a bit string");TextField textField1=new TextField();textField1.setMinWidth(800);Button button1=new Button("Decode Text");button1.setMinWidth(100);button1.setOnAction(e->{try {Htree.createHuffmanTree();String codes=textField1.getText();StringBuilder s=new StringBuilder(textField1.getText()+" is encoded to ");decode(codes,s,Htree.getHtree());if (textField1.getText().equals("")){s.replace(0,s.length(),"please enter a string !");}Alert alert = new Alert(Alert.AlertType.INFORMATION);alert.titleProperty().set("Decode Bits to Text");alert.headerTextProperty().set(s.toString());alert.showAndWait();} catch (Exception ex) {ex.printStackTrace();}});FlowPane flowPane1=new FlowPane();flowPane1.getChildren().addAll(text1,textField1,button1);borderPane.setBottom(flowPane1);borderPane.setCenter(pane);return borderPane;}public void start(Stage primaryStage) throws Exception{Scene scene=new Scene(pane(),1000,800);primaryStage.setTitle("HuffmanTree");primaryStage.setScene(scene);primaryStage.show();}public void decode(String codes,StringBuilder s,HuffmanTreeNode Htree){for (int i=0;i<codes.length();i++) {HuffmanTreeNode p=Htree;for (;i<codes.length();i++) {if (codes.charAt(i) == '1') {p=p.getRightChild();} else {p=p.getLeftChild();}if(p.getLeftChild()==null && p.getRightChild()==null){s.append(p.getData());break;}}}}public void createTree(HuffmanTreeNode Htree,int depth,Pane pane,int row,int col){if (Htree!=null) {Circle circle = new Circle(20);circle.setFill(null);circle.setStroke(Color.BLACK);Label label = new Label(Htree.getWeight() + "", circle);label.setContentDisplay(ContentDisplay.CENTER);label.setLayoutX(col);label.setLayoutY(row);pane.getChildren().add(label);row+=50;col-=depth;int row1=row,col1=2*depth+col;depth/=2;if(Htree.getLeftChild()!=null){Line line=new Line(label.getLayoutX(),label.getLayoutY()+30,col+35,row+5);Text text1=new Text("0");text1.setX((line.getStartX()+line.getEndX())/2-5);text1.setY((line.getStartY()+line.getEndY())/2-5);pane.getChildren().addAll(line,text1);}if (Htree.getRightChild()!=null){Line line2=new Line(label.getLayoutX()+40,label.getLayoutY()+30,col1+5,row1+5);Text text2=new Text("1");text2.setX((line2.getStartX()+line2.getEndX())/2-5);text2.setY((line2.getStartY()+line2.getEndY())/2-5);pane.getChildren().addAll(line2,text2);}if(Htree.getLeftChild()==null && Htree.getRightChild()==null){Text text=new Text(Htree.getData()+"");text.setX(label.getLayoutX()+20);text.setY(label.getLayoutY()+60);pane.getChildren().add(text);}createTree(Htree.getLeftChild(),depth, pane,row,col);createTree(Htree.getRightChild(),depth, pane,row1,col1);}}public void save() throws Exception{DataOutputStream os=new DataOutputStream(new FileOutputStream("TreePrint.txt"));os.writeUTF(this.codes);os.close();}
}

【数据结构课设】数据压缩与解压缩(java实现 javafx实现GUI)相关推荐

  1. 【数据结构课设】扫雷 (java实现)

    目录 一.问题描述 二.逻辑结构设计 三.存储结构设计 三.主要操作设计 四.技术难点与解决方法 五.实现与展示 六.详细代码 七.游戏内图片 一.问题描述 设计实现经典扫雷游戏,要求如下: (1) ...

  2. 华南农业大学课设——数据结构课设、Java课设、操作系统课设

    文章目录 缘起 大二上-数据结构课设(高校教学管理系统)-C++.Qt 视频演示 感想 大二下-Java课设(流程图绘制程序)-JavaFX 视频演示 感想 大三上-操作系统课设(模拟磁盘文件系统实现 ...

  3. 那些年,我的数据结构课设,现在满满的回忆!(现如今身处内卷之中,已经很难出现当初那份乐趣了)

    一.看到这个图标很有感觉 距离当初完成数据结构课设已经过去很久很久了,当初由于U盘失踪,也丢失了很多宝贵的东西,我也伤心的好久`(>﹏<)′!不过最近找到了失散多年的亲兄弟,打开U盘,感概 ...

  4. 南京航空航天大学2020数据结构课设

    南京航空航天大学2020数据结构课设 目录 1.系统进程设计 2.迷宫问题 3.家谱管理系统 4.Huffman编码与解码 5.地铁修建 6.公交线路提示 7.B-树应用 8.排序算法比较 9.数字排 ...

  5. 学生搭配问题数据结构报告c语言,数据结构课设学生搭配问题

    数据结构课设学生搭配问题 数 据 结 构 课程设计报告书 班级 学号 专业 姓名 课题描述: 一. 需求分析: 1. 设计内容 一班有m个女生,有n个男生(m不等于n),现要开一个舞会. 男女生分别编 ...

  6. 数据结构课设_网页形式的景区导游

    一.前言 欢迎大家来到这里~~ 1.这次数据结构课设,笔者的题目是网页形式的导航系统. 2.数据结构方面应用了链表存储商品信息.图来存储景点和路径信息.在图的广度优先中还用到了队列: 3.具体的算法有 ...

  7. 神秘国度的爱情故事 数据结构课设-广州大学

    神秘国度的爱情故事 数据结构课设-广州大学 ps:本次课设程序不仅需要解决问题,更需要注重代码和算法的优化和数据测试分析      直接广度优先实现的方法时间复杂度为O(QN),优化后的方法是lca+ ...

  8. 模拟浏览器操作程序(数据结构课设)

    文章目录 前言 一.题目 二.系统设计 2.1 功能模块图 2.2 主要功能函数 三.问题分析 四.实验结果及分析 五.源码 总结 前言 20级cqut的别抄! 一.题目  模拟浏览器操作程序:标准的 ...

  9. 背包问题求解(数据结构课设)

    文章目录 前言 一.题目 二.功能模块图 三.问题分析 四.实验结果及分析 五.源码 总结 前言 20级cqut的别抄! 一.题目  背包问题的求解:假设有一个能装入总体积为T的背包和n件体积分别为w ...

  10. 数据结构课设——汉诺塔游戏演示

    源代码下载地址:数据结构课设--汉诺塔游戏演示 一. 问题描述 汉诺塔游戏问题中的数据元素具有如下形式: lchild:左孩子结点 rchild:右孩子结点 num:该移动步骤需要移动的盘子的编号 s ...

最新文章

  1. Docker入门(一) - 仓库、容器、镜像、数据卷
  2. boost::callable_traits的qualified_class_of_t的测试程序
  3. 阿里云技术白皮书_对阿里重磅发布的云原生架构白皮书的初步解读
  4. iOS coredata 多表查询
  5. gradle是否可以编译c语言,build.gradle按条件编译与cmake配置
  6. 重磅!杰青、优青填报界面取消“论文收录与被引统计”
  7. python setup.py install 出错_python setup.py install 失败
  8. java 计算器api_用JAVA编写一个简单的计算器~要使用接口的~急啊~
  9. Introduction to Computer Networking学习笔记(二十九):DHCP
  10. 基于51单片机的多点8路温度采集系统 proteus仿真程序原理图设计
  11. linux flash文件系统,需要了解Linux flash文件系统
  12. 深圳:由“独角兽”们构建起的硬核科技之城
  13. 二叉树寻找节点x的所有祖先
  14. Android RSA加密解密
  15. string转map报错
  16. 如何使用 CSS 为 SVG 制作动画:示例教程
  17. base64解码报错Illegal base64 character
  18. 【管理知多少】通过现象看本质,小王和小张谁更胜任?
  19. Redis设计与实现 -读书笔记
  20. 华硕笔记本拆机清尘,修理

热门文章

  1. 控制科学与工程(自动化)保研经验【3】——山大、东南篇
  2. 工具-6:如何解决U盘无法弹出的问题?
  3. 大话设计模式之爱你一万年:第四章 创建型模式:建造者模式:爱不仅要说还要做:1.建造者模式概念
  4. android App内监听截图加二维码
  5. buuctf - web - [HCTF 2018]WarmUp
  6. 《内网安全攻防:渗透测试实战指南》读书笔记(七):跨域攻击分析及防御
  7. DXF文件格式——CLASSES 段
  8. 大学期末考java编程题_(完整版)大学期末考试Java题库
  9. vage mysql_MySql下视图的创建
  10. 设备指纹(Device Fingerprinting)是什么?