哈夫曼编码是一种编码方式,是可变字长编码(VLC)的一种。以哈夫曼树—即最优二叉树,带权路径长度最小的二叉树,经常应用于数据
压缩。 在计算机信息处理中,“哈夫曼编码”是一种一致性编码法(又称"熵编码法"),用于数据的无损耗压缩。这一术语是指使用一张特殊的
编码表将源字符(例如某文件中的一个符号)进行编码。这张编码表的特殊之处在于,它是根据每一个源字符出现的估算概率而建立起来的(出
现概率高的字符使用较短的编码,反之出现概率低的则使用较长的编码,这便使编码之后的字符串的平均期望长度降低,从而达到无损压缩数
据的目的)。这种方法是由David.A.Huffman发展起来的。 例如,在英文中,e的出现概率很高,而z的出现概率则最低。当利用哈夫曼编码
对一篇英文进行压缩时,e极有可能用一个位(bit)来表示,而z则可能花去25个位(不是26)。用普通的表示方法时,每个英文字母均占用一
个字节(byte),即8个位。二者相比,e使用了一般编码的1/8的长度,z则使用了3倍多。倘若我们能实现对于英文中各个字母出现概率的较
准确的估算,就可以大幅度提高无损压缩的比例。
   其中哈夫曼编码的构造过程为:
  (1)初始化,根据符号概率的大小按由大到小顺序对符号进行排序。
    (2)把概率最小的两个符号组成一个新符号(节点),即新符号的概率等于这两个符号概率之和。
    (3)重复第2步,直到形成一个符号为止(树),其概率最后等于1。
    (4)从编码树的根开始回溯到原始的符号,并将每一左分枝赋值为0,右分枝赋值为0。
 前三步为构造哈夫曼树过程,既而在第四步遍历构造出的哈夫曼树,得到各字符的最优前缀码.  
 为了快速实现取集合中的最小值及向集合中插入新的元素,可以使用二叉堆来实现,二叉堆的定义及实现请另行参考.(注:由于ASCII码及
char型数据范围的限制,编码过程中生成的哈夫曼树不会非常大,所以生成哈夫曼树所需要的时间在整个压缩过程中显得微不足道,可用其它方
式来生成哈夫曼树.)
以一棵生成的哈夫曼树为例(图片来自:http://blog.163.com/yuang_yu_ping/blog/static/4693287620098172049346/):
图片中的哈夫曼树对应的绿色即叶结点为待编码字符,对应的前缀码分别为:
A : 10
C : 01
D : 11
E : 000
M : 001
    在文本文件的压缩过程中,首先取ASCII字符对应的出现次数,然后对 0x00~0xff 这些字符根据出现频率生成对应的前缀码,然后读取
文件 ,为每一个字符进行编码.
    编码问题已经解决了,可是如果要实现文件压缩,压缩文件的编码如何进行存储呢? 首先明确一点,源文件压缩之后的编码对应的是二进制的
位段,而C语言中每个char 类型占用八位的空间,我们可以把编码后的二进制位以八位为一段,生成对应的字符来存储.对最后不足八位的二进制段,
补足八位,用一个字符表示,依次存储这些转化来的字符,即可生成压缩文件.
    另,为了解压的需要,我们要存储编码的信息至压缩文件中,可以采用源文件中各字符出现的频率来记录,解码时读取这些信息,再利用这些信息
重新生成一棵同样的哈夫曼树,另外压缩文件头中存储有最后一个字符实际占用的位数,用以在解压时对最后一个字符正确处理.
 至此,用哈夫曼编码来实现文本文件压缩的大致过程也算明白了,下面来谈一谈具体实现:
 首先是哈夫曼树的结点,
//哈夫曼树结点结构体实现
typedef struct talNode{
unsigned char c;    //叶结点时对应ASCII值
int weight;         //该结点权值
int lt,rt;          //左、右结点下标
talNode(){}
talNode(unsigned char _c,int _p):
c(_c),weight(_p),lt(-1),rt(-1)
{}
talNode(unsigned char _c,int _p,int l,int r)
:c(_c),weight(_p),lt(l),rt(r)
{}
bool operator < (talNode a)
{//重载运算符“<”用于二叉堆内的比较
return weight<a.weight;
}
}HuffNode; 
  生成哈夫曼树时要用到的堆
//实现二叉堆模板类,小顶堆
template <class HeapType>
class CHeap
{
HeapType *data,tmp;
int size;
void HeapUp(int ix)
{//自底向顶维护堆
int f;
for(f=F(ix);ix&&data[ix]<data[f];ix=f,f=F(f))
SWAP(data[ix],data[f],tmp);
}
void HeapDown(int ix)
{//自顶向底维护堆
int l,r,t;
HeapType min,tmp;
if(ix>=size) return ;
l=L(ix),r=R(ix);
min=data[ix],t=ix;
if(l<size&&data[l]<min)
t=l,min=data[l];
if(r<size&&data[r]<min)
t=r,min=data[l];
SWAP(data[ix],data[t],tmp);
if(ix!=t) HeapDown(t);
}
public:
CHeap()
{//这里特殊应用,堆内元素个数不超过256
size=0;
data=new HeapType[256];
}
~CHeap()
{//释放内存
delete data;
}
int getsize()
{//返回堆大小
return size;
}
void clear()
{//清空堆
size=0;
}
void insert(HeapType e)
{//向堆尾中插入元素,并向顶维护堆
data[size++]=e;
HeapUp(size-1);
}
HeapType top()
{//从堆顶取出元素,并向底维护堆
HeapType ret=data[0];
data[0]=data[--size];
HeapDown(0);return ret;
}
}; 
  进行压缩工作时的变量:
  HuffNode arr[512];      //哈夫曼树结点数组
int size;               //哈夫曼树结点个数
bool code[256][64];     //ASCII对应编码方案
int lenth[256];         //ASCII对应编码长度
//lastcodelenth,ps[256],用于存储压缩文件中作为文件头
int lastcodelenth;      //文件最后一个字符实用几位
int ps[256];            //ASCII对应出现频率
int soucelen,targetlen; //源及目标文件长度 
  CHuffMan类的声明:
class CHuffMan{
HuffNode arr[512];      //哈夫曼树结点数组
int size;               //哈夫曼树结点个数
bool code[256][64];     //ASCII对应编码方案
int lenth[256];         //ASCII对应编码长度
//lastcodelenth,ps[256],用于存储压缩文件中作为文件头
int lastcodelenth;      //文件最后一个字符实用几位
int ps[256];            //ASCII对应出现频率
int soucelen,targetlen; //源及目标文件长度
//私有成员函数,用于实现内部功能
void SetHuffTree(int []);           //根据字符频率生成哈夫曼树
void SetHuffCode(int ,bool [],int );//根据哈夫曼树生成编码方案
void CreateHuff(int []);            //创建哈夫曼树及编码方案
void EnCodePre(char []);            //压缩前预处理
void DeCodePre(FILE *);             //解压前预处理
void SaveHuffHead(FILE *);          //保存压缩文件头
void ReadHuffHead(FILE *);          //读取压缩文件头
public:
CHuffMan(){Clear();}                //构造函数
~CHuffMan(){}                       //析构函数
//公有成员函数,用于提供使用接口
void Clear();                       //清空当前对象内容
void EnCodeFile(char [],char []);   //编码,用于压缩文件,第一个参数为
//源文件名,第二个参数为目标文件名
void DeCodeFile(char [],char []);   //解码,用于解压文件,第一个参数为
//源文件名,第二个参数为目标文件名
void GetHuffCode();                 //输出ASCII对应编码方案
int GetSouceLen();                  //输出当前工作项的源文件长度
int GetTargetLen();                 //输出当前工作项的目标文件长度
}; 
  思考:从运行效果上来看,压缩的比例不是非常满意,压缩率达不到25%,应该可以采用动态哈夫曼树的编码来对大文件的压缩进行优化,
从而达到更优的压缩率。确定一个初始哈夫曼树,然后根据当前处理过的字符动态的调整哈夫曼树的结构,解压过程同样根据解压过的字符调整哈夫曼树
结构。   压缩与解压的时间效率有待提高,本程序在压缩及解压过程中对硬盘进行了大量的读写,使速度受到影响,可以通过缓冲带还实现提速,即构
造内存缓冲区,每次读写多个字符,既而进行处理。
 
 (注:根据哈夫曼树的构成原理,可以预见一种特殊的情况:哈夫曼树的结构为每一层只有一个叶结点,这种情况下的每个字符对应的前缀码
长度最高会达到256位,在此程序中未进行特殊考虑。)
   运行效果:

   源代码:

/***************************************************************** Title: 哈夫曼编码及文件压缩 Author: Dooler IDE: VC++6.0 Date: 2011-5-31 INFO: 实现了两个类, CHeap(二叉堆) CHuffMan(文件压缩) 可对文本文件进行压缩 QQ: 441809866 ******************************************************************/ #include<iostream> #define ASCIIL 256 #define F(x) ((x-1)>>1) #define L(x) ((x<<1)+1) #define R(x) ((x<<1)+2) #define SWAP(a,b,tmp) {tmp=a;a=b;b=tmp;} using namespace std; //实现二叉堆模板类,小顶堆 template <class HeapType> class CHeap { HeapType *data,tmp; int size; void HeapUp(int ix) {//自底向顶维护堆 int f; for(f=F(ix);ix&&data[ix]<data[f];ix=f,f=F(f)) SWAP(data[ix],data[f],tmp); } void HeapDown(int ix) {//自顶向底维护堆 int l,r,t; HeapType min,tmp; if(ix>=size) return ; l=L(ix),r=R(ix); min=data[ix],t=ix; if(l<size&&data[l]<min) t=l,min=data[l]; if(r<size&&data[r]<min) t=r,min=data[l]; SWAP(data[ix],data[t],tmp); if(ix!=t) HeapDown(t); } public: CHeap() {//这里特殊应用,堆内元素个数不超过256 size=0; data=new HeapType[256]; } ~CHeap() {//释放内存 delete [] data; } int getsize() {//返回堆大小 return size; } void clear() {//清空堆 size=0; } void insert(HeapType e) {//向堆尾中插入元素,并向顶维护堆 data[size++]=e; HeapUp(size-1); } HeapType top() {//从堆顶取出元素,并向底维护堆 HeapType ret=data[0]; data[0]=data[--size]; HeapDown(0);return ret; } }; //哈夫曼树结点结构体实现 typedef struct talNode{ unsigned char c; //叶结点时对应ASCII值 int weight; //该结点权值 int lt,rt; //左、右结点下标 talNode(){} talNode(unsigned char _c,int _p): c(_c),weight(_p),lt(-1),rt(-1) {} talNode(unsigned char _c,int _p,int l,int r) :c(_c),weight(_p),lt(l),rt(r) {} bool operator < (talNode a) {//重载运算符“<”用于二叉堆内的比较 return weight<a.weight; } }HuffNode; //哈夫曼文件压缩类声明 /************************************************ 类说明: 1、通过EnCodeFile(char filef[],char filet[]) 和DeCodeFile(char filef[],char filet[]) 来建立工作项,此时对象中保存压缩或解压过和中的 详细信息 2、通过GetHuffCode();GetSouceLen();GetTargetLen() 来获得当前工作项信息 3、通过Clear();清除当前工作项信息,为下一次压缩或 解压作准备 ************************************************/ class CHuffMan{ HuffNode arr[512]; //哈夫曼树结点数组 int size; //哈夫曼树结点个数 bool code[256][64]; //ASCII对应编码方案 int lenth[256]; //ASCII对应编码长度 //lastcodelenth,ps[256],用于存储压缩文件中作为文件头 int lastcodelenth; //文件最后一个字符实用几位 int ps[256]; //ASCII对应出现频率 int soucelen,targetlen; //源及目标文件长度 //私有成员函数,用于实现内部功能 void SetHuffTree(int []); //根据字符频率生成哈夫曼树 void SetHuffCode(int ,bool [],int );//根据哈夫曼树生成编码方案 void CreateHuff(int []); //创建哈夫曼树及编码方案 void EnCodePre(char []); //压缩前预处理 void DeCodePre(FILE *); //解压前预处理 void SaveHuffHead(FILE *); //保存压缩文件头 void ReadHuffHead(FILE *); //读取压缩文件头 public: CHuffMan(){Clear();} //构造函数 ~CHuffMan(){} //析构函数 //公有成员函数,用于提供使用接口 void Clear(); //清空当前对象内容 void EnCodeFile(char [],char []); //编码,用于压缩文件,第一个参数为 //源文件名,第二个参数为目标文件名 void DeCodeFile(char [],char []); //解码,用于解压文件,第一个参数为 //源文件名,第二个参数为目标文件名 void GetHuffCode(); //输出ASCII对应编码方案 int GetSouceLen(); //输出当前工作项的源文件长度 int GetTargetLen(); //输出当前工作项的目标文件长度 }; void CHuffMan::SetHuffTree(int ps[]) { //每次取出两权值最小的结点合并成新树, //加入堆,直至堆中只余有一个元素 CHeap<HuffNode> hp; //二叉堆对象 for(int i=0;i<ASCIIL;i++){ //如果字符i出现过,则插入二插堆 if(ps[i]) hp.insert(HuffNode(i,ps[i])); } size=0; //初始化哈夫曼树中结点个数 while(hp.getsize()>1){ arr[size++]=hp.top(); //取出权值最小的两个结点 arr[size++]=hp.top(); hp.insert(HuffNode(0, arr[size-1].weight+arr[size-2].weight, size-1,size-2)); //合并结点,并插入堆 } arr[size++]=hp.top(); //arr[size-1]为哈夫曼树根 } void CHuffMan::SetHuffCode(int ix,bool stk[],int top) { //递归深搜哈夫曼树,生成所有存在的ASCII //的前缀码 if(arr[ix].c){ //如果 if(top){ //此判断用于只含有一类字符的文件 memmove(code[arr[ix].c],stk,sizeof(bool)*top); lenth[arr[ix].c]=top; } else lenth[arr[ix].c]=1; return ; } stk[top]=0; //左子树的边设为0 SetHuffCode(arr[ix].lt,stk,top+1); //递归进入左子树 stk[top]=1; //右子树的边设为1 SetHuffCode(arr[ix].rt,stk,top+1); //递归进入右子树 } void CHuffMan::CreateHuff(int ps[]) { //构造哈夫曼树及前缀码 bool stk[64]; SetHuffTree(ps); //根据字符频率构造哈夫曼树 SetHuffCode(size-1,stk,0); //根据哈夫曼树生成前缀码 } void CHuffMan::EnCodePre(char sfilename[]) { //压缩文件预处理,读取字符出现频率 FILE *fp; //及构造哈夫曼树及前缀码 int c; fp=fopen(sfilename,"rb"); if(fp==NULL){ cout<<"读取文件错误"<<endl; exit(0); } memset(ps,0,sizeof(ps)); //读取字符出现频率 while(true){ c=fgetc(fp); if(feof(fp))break; ps[c]++; } fclose(fp); CreateHuff(ps); //构造哈夫曼树及前缀码 } void CHuffMan::DeCodePre(FILE *fp) { //解压文件预处理,读取压缩文件头 //根据读取头信息构千哈夫曼树及前缀码 ReadHuffHead(fp); CreateHuff(ps); } void CHuffMan::SaveHuffHead(FILE *fp) { //向压缩文件中写文件头 fwrite((void *)&lastcodelenth,4,257,fp);//从lastcodelenth的地址开始的连续 //4*257个字节,即lastcodelenth和 //ps[256]数组内容 targetlen+=4*257; } void CHuffMan::ReadHuffHead(FILE *fp) { //从缩文件中读文件头 fread((void *)&lastcodelenth,4,257,fp); //从lastcodelenth的地址开始的连续 //4*257个字节,即lastcodelenth和 soucelen+=4*257; //ps[256]数组内容 } void CHuffMan::Clear() { //清空前前工作项 size=0;soucelen=targetlen=0; lastcodelenth=0; memset(lenth,0,sizeof(lenth)); memset(ps,0,sizeof(ps)); } int CHuffMan::GetSouceLen() { //获取当前工作项的源文件长度 return soucelen; } int CHuffMan::GetTargetLen() { //获取当前工作项的目标文件长度 return targetlen; } void CHuffMan::GetHuffCode() { //输出当前工作项的编码前缀码方案 int i; for(i=0;i<ASCIIL;i++){ if(lenth[i]>0){ //如果前缀码不空 printf("%c : ",i); //输出ASCII码 for(int j=0;j<lenth[i];j++){ printf("%d",code[i][j]); //输出对应前缀码 } puts(""); } } } void CHuffMan::EnCodeFile(char sfilename[],char gfilename[]) { //将文件sfilename //压缩为文件gfilename[] FILE *fp,*fpt; int c,data,l,i; EnCodePre(sfilename); //压缩预处理,生成哈曼树及 //字符前缀码 fp=fopen(sfilename,"rb"); fpt=fopen(gfilename,"wb"); SaveHuffHead(fpt); //写入压缩文件的头信息 //!!!注意,此时lastcodelenth //为空,需压缩结束时重置 l=data=0; puts("Encoding ... "); //编码压缩过程,依次对源文件字符进行编码 while(true){ //存入一个字符中,用移位操作实现,每8位前 c=fgetc(fp); //缀码对应一个字符,将该字符存入目标文件, if(feof(fp)) break; //最终不足8位的记录最后一位占用的前缀码长度 soucelen++; //源文件长度增加 for(i=0;i<lenth[c];i++){ //对data进行左移,空出最低位 data<<=1; //对当前字符的前缀码当前们存储于data中 data+=code[c][i]; if(++l%8==0){ //满8位,则存储 fputc(data,fpt); targetlen++; //目标文件长度增加 } } } //对最后的一个字符进行处理 lastcodelenth=l%8; //记录实际占用位的长度 data<<=8-lastcodelenth; //空出剩余位 fputc(data,fpt); //输出至文件 targetlen++; //目标文件长度增加 fseek(fpt,0,SEEK_SET); //!!!回溯至文件头,更新lastcodelenth至 fwrite(&lastcodelenth,4,1,fpt); //真实值 fclose(fp); //关闭文件 fclose(fpt); } void CHuffMan::DeCodeFile(char sfilename[],char gfilename[]) { //解压文件sfile至gfile FILE *fp=fopen(sfilename,"rb"); FILE *fpt=fopen(gfilename,"wb"); int c,t,l,i; //l用于记录当前前缀码段的长度 HuffNode cur; bool tmp[64]; //tmp[]用于记录当前的前缀码段 DeCodePre(fp); l=0; puts("Decoding ... "); fscanf(fp,"%c",&c); //解码过程,压缩过程的逆过程,取出编码了的字符, //按位取出,存于tmp[]中,找出前缀码对应的字符 while(!feof(fp)){ soucelen++; fscanf(fp,"%c",&t); if(feof(fp))break; for(i=l+7;i>=l;i--){ //按位取出前缀码 tmp[i]=c&1;c>>=1; }l+=8; while(l>=32){ //如果当前前缀码段超出一定的长度,则取出前缀码 //进行解码 for(i=0,cur=arr[size-1];!cur.c;i++) cur=tmp[i]?arr[cur.rt]:arr[cur.lt];//找到前缀码段对应第一个字符 fprintf(fpt,"%c",cur.c); //输出至目标文件 l-=i;targetlen++; //前缀码段减去当前字符前缀码长度 memmove(tmp,tmp+i,sizeof(bool)*l); //数组顺移至开头,即从0开始记录当前的 //前缀码段 }c=t; } for(i=l+7;i>=l;i--){ //对最后一个字符做特殊处理 tmp[i]=c&1; //取出每一位 c>>=1; } l+=lastcodelenth; //只利用最后一个字符的前lastcodelenth位 while(l){ //输出剩余的前缀码段对应的字符 for(i=0,cur=arr[size-1];!cur.c;i++) cur=tmp[i]?arr[cur.rt]:arr[cur.lt]; fprintf(fpt,"%c",cur.c);l-=i;targetlen++; memmove(tmp,tmp+i,sizeof(bool)*l); } fclose(fp);fclose(fpt); //关闭文件 } bool Menu(int &op) { system("cls"); printf("|/t哈夫曼编码实现文件压缩/t|/n"); printf("功能:/n"); printf("_________________________________/n"); printf("|/t1、/t压缩文件/t|/n"); printf("|/t2、/t解压文件/t|/n"); printf("|/t3、/t输出编码方案/t|/n"); printf("|/t0、/t退出 /t|/n"); printf("---------------------------------/n"); do{ printf("请选择:"); scanf("%d",&op); }while(op<0||op>3); return op?true:false; } int main() { int op; char file1[32],file2[32]; CHuffMan work; char step[2]; while(Menu(op)){ switch(op){ case 1: printf("请输入待压缩文件名(.txt):"); scanf("%s",file1); printf("请输入压缩文件名(.huf):"); scanf("%s",file2); work.Clear(); work.EnCodeFile(file1,file2); printf("源文件长度:/t%d/n",work.GetSouceLen()); printf("目标文件长度:/t%d/n",work.GetTargetLen()); break; case 2: printf("请输入待压缩文件名(.huf):"); scanf("%s",file1); printf("请输入压缩文件名(.txt):"); scanf("%s",file2); work.Clear(); work.DeCodeFile(file1,file2); printf("源文件长度:/t%d/n",work.GetSouceLen()); printf("目标文件长度:/t%d/n",work.GetTargetLen()); break; case 3: work.GetHuffCode(); break; } puts("按任意键继续..."); gets(step); gets(step); } return 0; }

哈夫曼编码及文本文件的压缩解压(c++SourceCode)相关推荐

  1. Python+Qt 使用哈夫曼编码对文本文件进行压缩,解压缩

    from Huffman_Ui import * from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtGui i ...

  2. 使用哈夫曼编码实现数据的压缩和解压(java版)

    1.哈夫曼树 哈夫曼编码使用哈夫曼树的数据结构,哈夫曼树图解如下,即构造一个带权路径最小的数: 2.哈夫曼编码 使用哈夫曼树生成哈夫曼编码,已实现减少传输中数据的冗余:截取网络课程中的几张图来说明: ...

  3. 哈夫曼编码实现文件的压缩和解压

    哈夫曼编码的概念 哈夫曼编码是基于哈夫曼树实现的一种文件压缩方式. 哈夫曼树:一种带权路径最短的最优二叉树,每个叶子结点都有它的权值,离根节点越近,权值越小(根节点权值为0,往下随深度增加依次加一), ...

  4. 基于哈夫曼编码对文件进行压缩和解压缩(详细讲解)

    基于哈夫曼编码对文件进行压缩和解压缩(详细讲解) 本文对应c++代码实现链接 一.背景 利用特定的算法来压缩数据的工具,压缩后生成的文件称为压缩包.如果想使用其中的数据,就得用压缩软件对数据进行解压. ...

  5. 基于哈夫曼编码完成的文件压缩及解压

    这几天在较为认真的研究基于哈夫曼编码的文件压缩及解压,费了点时间,在这分享一下: 这里用链式结构,非顺序表结构: 文件压缩: 1.获取文件信息(这里采用TXT格式文本): 2.压缩文件: 3.写配置文 ...

  6. Delphi Base64编码/解码及ZLib压缩/解压

    最近在写的程序与SOAP相关,所以用到了一些Base64编码/解码及数据压缩/解压方面的知识. 在这里来作一些总结: 一.Base64编码/解码 一般用到的是Delphi自带的单元EncdDecd,当 ...

  7. 利用huffman编码对文本文件进行压缩与解压(java实现)

    利用huffman编码对文本文件进行压缩与解压 输入:一个文本文件 输出:压缩后的文件 算法过程: (1)统计文本文件中每个字符的使用频度 (2)构造huffman编码 (3)以二进制流形式压缩文件 ...

  8. 哈夫曼字符串编码c语言实现,基于哈夫曼(haffuman)算法的文件压缩的实现(C语言)(原创)...

    本文首先简要阐述哈夫曼算法的基本思想,然后介绍了使用哈夫曼算法进行文件压缩和解压缩的 处理步骤,最后给出了C语言实现的文件压缩和解压缩的源代码. 哈夫曼算法的主要思想是: ①首先遍历要处理的字符串,得 ...

  9. C语言霍夫曼编码压缩,数据结构大作业——哈夫曼编码压缩BMP格式文件

    数据结构大作业--哈夫曼编码压缩BMP格式文件 首先需要了解BMP图像格式 BMP图像格式详解 其次需要了解哈夫曼编码如何对BMP文件进行压缩 哈夫曼压缩与解压缩 编程部分 使用的头文件 虽然这里用了 ...

最新文章

  1. python保存内容到文件(text、json、csv)
  2. 【错误记录】NDK 动态库报错 ( dlopen failed: file offset for the library /lib/arm64/libwebp.so“ >= file size:0)
  3. [转]阿拉伯数字转一二三和壹贰叁
  4. Leetcode 剑指 Offer 11. 旋转数组的最小数字 (每日一题 20210916)
  5. 数据库三大范式(1NF,2NF,3NF)及ER图
  6. 现代软件工程 学生自我评价表
  7. linux 导出软件,如何将perf(Linux工具)的输出保存到文件中?
  8. codevs3143 二叉树的序遍历
  9. 深入理解l内核v4l2框架之video for linux 2(转载)
  10. adx-desc-adtype统计
  11. 第一部分 线程APIs(Thread APIs)线程和运行(Threads and Runnables)
  12. 2021年的10种突破性技术
  13. EndNote X9导入文献的5种方式
  14. 万用表使用_如何使用万用表
  15. 上海互盾信息科技有限公司:心之所向,行必能至
  16. GOOGLE:单一模式背后
  17. Ubuntu16.04定时执行功能
  18. windows API 实现精确的打点计时器
  19. ubuntu终端英文乱码问题
  20. Python批量处理lrmx格式文档内指定内容

热门文章

  1. try catch用法理解
  2. 高能手办团个别服务器维护,《高能手办团》11月20日例行维护公告
  3. org.junit.runners.model.InvalidTestClassError异常解决
  4. 跑分cpu_【新机】天玑800+跑分性能出炉:CPU干翻骁龙765G,比肩麒麟985 | 骁龙768G性能曝光...
  5. SpringBoot RabbitMQ 注解版 基本概念与基本案例
  6. nativeexcel使用,Delphi 开发解决方案
  7. Successive Approximation Register ADC逐次逼近寄存器型模数转换器
  8. 事务和事务的隔离级别及脏读、幻读
  9. Docker与微服务培训【PPT截图】
  10. “与”,“或”的快速记忆