最后
关于面试刷题也是有方法可言的,建议最好是按照专题来进行,然后由基础到高级,由浅入深来,效果会更好。当然,这些内容我也全部整理在一份pdf文档内,分成了以下几大专题:
- Java基础部分
- 算法与编程
- 数据库部分
- 流行的框架与新技术(Spring+SpringCloud+SpringCloudAlibaba)
这份面试文档当然不止这些内容,实际上像JVM、设计模式、ZK、MQ、数据结构等其他部分的面试内容均有涉及,因为文章篇幅,就不全部在这里阐述了。
作为一名程序员,阶段性的学习是必不可少的,而且需要保持一定的持续性,这次在这个阶段内,我对一些重点的知识点进行了系统的复习,一方面巩固了自己的基础,另一方面也提升了自己的知识广度和深度。
2.1Huffman树
2.1.1huffman树的概念
二叉树的根结点到二叉树中所有叶结点的路径长度与相应权值的乘积之和为该二叉树的带权路径长度WPL。
上述四棵树的带权路径长度分别为:
WPLa = 1 * 2 + 3 * 2 + 5 * 2 + 7 * 2 = 32
WPLb = 1 * 2 + 3 * 3 + 5 * 3 + 7 * 1 = 33
WPLc = 7 * 3 + 5 * 3 + 3 * 2 + 1 * 1 = 43
WPLd = 1 * 3 + 3 * 3 + 5 * 2 + 7 * 1 = 29
其中·将带权路径最小的二叉树称为Huffman树。
2.1.2Huffman树的构造
- 由给定的n个权值{ w1, w2, w3, … , wn}构造n棵只有根节点的二叉树森林F={T1, T2 , T3, … ,Tn},每棵二叉树Ti只有一个带权值wi的根节点,左右孩子均为空。
- 重复以下步骤,直到F中只剩下一棵树为止
在F中选取两棵根节点权值最小的二叉树,作为左右子树构造一棵新的二叉树,新二叉树根节点的权
值为其左右子树根节点的权值之和
在F中删除这两棵二叉树
把新的二叉树加入到F中
按照上述图解
代码部分:
void CreateHuffmanTree(const W arry[], size_t size,const W& invaild)
{
//使用优先级队列保存二叉树森林的根节点
//建小堆--优先级队列的默认情况是大堆,要修改其比较规则
std::priority_queue<Node\*, vector<Node\*>, com<W>> q;
//注意这一块的greaer拿根节点的地址进行比较了 ,按照地址建小堆了;
//1.先使用所给的权值创建只有根节点的二叉树森林
for (size_t i = 0; i < size; i++)
{
if (arry[i] != invaild)
{
//创建一个只有根的二叉树,将arry中的树依次放到优先级队列中
q.push(new Node(arry[i]));
}
}
//循环进行一下步骤,直到二叉树森林中只剩下一颗二叉树的位置
while (q.size() > 1)
{
//从二叉树中先 去掉 权值最小的两颗二叉树
Node\* left = q.top();
q.pop();
Node\* right = q.top();
q.pop();
//将left和right作为某个新节点的的左右树中,构造成一个新的二叉树
//将左右子树的权值相加作为他们的根节点
Node\* parent = new Node(left->quanzhi + right->quanzhi);
parent->left = left;
parent->right = right;
left->parent = parent;
right->parent = parent;
//将新的二叉树插入到二叉树森林中
q.push(parent);
}
root = q.top();//循环结束,剩余的树就是我们所需要的二叉树
}
//获取根节点
Node\* GetRoot()
{
return root;
}
2.2压缩流程
2.2.1统计源文件中出现字符的次数
我们使用一个256的数组来统计字符出现的次数,每个元素的ASCII值刚好与数组的下标是一一对应的。因此:在统计时就可以直接以字节的ASCII值作为数组的下标进行统计。组数中的值就是字符出现的次数。
2.2.2 根据统计的结果构建Huffman树
将统计字符的次数根据上述Huffman树的原理进行构建
2.2.3通过Huffman树获取字符编码
对Huffman树进行遍历,当走到叶子节点的时候,往回退如果当前节点是左子树则往相应字符储存编码的的位置加0,如果是右子树则加1。等到当前节点的父节点为0时,结束,编码的位置是由叶子节点开始放的需要逆置,则该字符的对应编码就得到了,然后递归左子树,递归右子树,直到全部的字符编码都得到。
图解:
2.2.4编写解压缩的文件信息
2.2.5根据源文件的字符将对应的编码写入压缩文件中
将源文件中的每个字节替换成对应字节的编码
注意:在后面读取pf文件时,需要将pf文件指针放到文件起始的位置。
因为刚开始在统计字节出现的次数的时候,已经读取过i一遍文件了,pf已经在文件的末尾的位置了.
bool FileCompress::CompressFile(const string & filePath)
{
//1.统计源文件中每个字节出现的次数--->保存每个字符的信息
//"r"是以文本的形式进行读的,遇到-1会结束,所以需要以二进制"rb"
FILE\* pf = fopen(filePath.c\_str(), "rb");
if (nullptr == pf)
{
cout << "打开压缩文件失败!!" << endl;
return false;
}
//文件大小不知道---需要循环采用的方式来获取源文件的内容
uch readbuff[1024];//一次性读取的内容大小
while (true)
{
//rdsize表示实际读取的字节数
size_t rdsize = fread(readbuff, 1, 1024, pf);
if (0 == rdsize)
{
//现在已经读取到文件的末尾了
break;
}
//对读取的字节进行统计
for (size_t i = 0; i < rdsize; i++)
{
//利用:直接定制法--以字符的ASCII值作为数组的下标来进行快速的统计
fileByte[readbuff[i]].appearCount++;
}
}
//2.根据统计的结果创建huffman树
//在创建Huffman树时,需要将出现次数为0的字节去除掉
HuffmanTree<Byte>ht;
Byte invaild;
ht.CreateHuffmanTree(fileByte, 256,invaild);
///
//3.借助Huffman树获取每个字节的编码
GetHuffmanCode(ht.GetRoot());
//
//4.写解压缩时需要用到的信息
FILE\* fOut = fopen("压缩.hzp", "wb");
WriteHead(fOut, filePath);
///
//5.使用字节的编码对源文件重新进行改写
//注意:在后面读取pf文件时,需要将pf文件指针放到文件起始的位置。
//因为刚开始在统计字节出现的次数的时候,已经读取过i一遍文件了,pf已经在文件的末尾的位置了
//fseek(pf, 0, SEEK\_SET);
rewind(pf);//这两种方法都可以将pf放到起始的位置
uch ch = 0;
uch bitCount = 0;
while (true)
{
size_t rdsize = fread(readbuff, 1, 1024, pf);
if (0 == rdsize)
{
break;
}
//用编码改写字节--改写的结果需要放置到压缩结果文件当中
for (size_t i = 0; i < rdsize; i++)
{
//readbuff[i]--->'A'-->'100'
string& Code = fileByte[readbuff[i]].Code;
//将字符串格式的二进制编码往字节中存放
for (size_t j = 0; j < Code.size(); j++)
{
ch <<= 1;//高位丢弃,低位补0;
if (Code[j] == '1')
{
ch |= 1;
}
//当ch中的8个比特位填满之后,需要将该字节写入到压缩文件中
bitCount++;
if (8 == bitCount)
{
fputc(ch, fOut);//将该字节写入到压缩文件
bitCount = 0;
}
}
}
}
//检测:ch不够8个比特位,实际是没有写进去的
if (bitCount > 0 && bitCount < 8)
{
//解压缩是要从高位开始,所以不够8位时,要将剩余的放到高位
ch <<= (8 - bitCount);
fputc(ch, fOut);
}
fclose(pf);
fclose(fOut);
return true;
}
2.3解压缩流程
2.3.1读取解压缩信息
源代码:
//读取源文件的后缀
string postFix;
GetLine(fIn, postFix);
//频次信息的总行数、
string strContent;
GetLine(fIn, strContent);
size_t lineCount = atoi(strContent.c\_str());
//循环读取lineCount行,获取字节的频次信息
strContent = "";
for (size_t i = 0; i < lineCount; i++)
{
GetLine(fIn, strContent);//将读取的一整行放到strContent中
if ("" == strContent)
{
//换行需要特殊处理
//说明刚刚读取到的是一个换行
strContent += "\n";
GetLine(fIn, strContent);//需要在读一次,将换行读进去
}
//fileByte[strContent[0]].ch = strContent[0];
fileByte[(uch)strContent[0]].appearCount = atoi(strContent.c\_str() + 2);//A:1
strContent = "";
}
2.3.2构建Huffman树
//2.Huffman树
HuffmanTree<Byte> ht;
Byte invaild;
ht.CreateHuffmanTree(fileByte, 256, invaild);
2.3.3解压缩
根据压缩数据对应的二进制比特流来遍历Huffman树,
从压缩文件中逐个字节比特位来进行解压缩,依次获取每个比特位
如果该比特位为0,让cur往其左子树中移动
如果该比特位为1,往其右子树移动
只要cur走到叶子节点的位置,则成功解压缩一个字节,
将解压的字符写到文件中,回到根节点的位置,继续遍历。
代码部分:
string filename("解压缩");//解压缩文件的名字
filename += postFix;
FILE\* fOut = fopen(filename.c\_str(), "wb");//打开这个文件中去写
uch readbuff[1024];
uch bitCount = 0;
HuffmanTreeNode<Byte>\* cur = ht.GetRoot();
const int fileSize = cur->quanzhi.appearCount;//根节点的权值为源文件的大小
int compressSize = 0;//解压缩的字节数
while (true)
{
size_t rdsize = fread(readbuff, 1, 1024, fIn);
if (0 == rdsize)
{
break;
}
for (size_t i = 0; i < rdsize; i++)
{
//逐个字节比特位来进行解压缩
uch ch = readbuff[i];
# 文末
我将这三次阿里面试的题目全部分专题整理出来,并附带上详细的答案解析,生成了一份**PDF文档**
* **第一个要分享给大家的就是算法和数据结构**

* **第二个就是数据库的高频知识点与性能优化**

* **第三个则是并发编程(72个知识点学习)**

* **最后一个是各大JAVA架构专题的面试点+解析+我的一些学习的书籍资料**

还有更多的Redis、MySQL、JVM、Kafka、微服务、Spring全家桶等学习笔记这里就不一一列举出来
> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.youkuaiyun.com/forums/4f45ff00ff254613a03fab5e56a57acb)收录**
**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.youkuaiyun.com/forums/4f45ff00ff254613a03fab5e56a57acb)**
91ukggY-1715353022160)]
* **最后一个是各大JAVA架构专题的面试点+解析+我的一些学习的书籍资料**
[外链图片转存中...(img-Gx6K6ff2-1715353022160)]
还有更多的Redis、MySQL、JVM、Kafka、微服务、Spring全家桶等学习笔记这里就不一一列举出来
> **本文已被[CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】](https://bbs.youkuaiyun.com/forums/4f45ff00ff254613a03fab5e56a57acb)收录**
**[需要这份系统化的资料的朋友,可以点击这里获取](https://bbs.youkuaiyun.com/forums/4f45ff00ff254613a03fab5e56a57acb)**