C++学习重点记录(Week3)

本文分享了如何按层打印二叉树、顺时针矩阵打印、二叉树和为n的路径、字符串全排列等经典问题的解决方法,以及哈夫曼树的构造算法和编码,包括带权路径长度、编码原理与应用。同时介绍了vector初始化的各种技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、刷题重点题目记录

①把二叉树按层打印成多行

二叉树的BFS是用一个队列来存储结点,每次将结点的左右子结点存入队列来达到按层遍历的效果。按层打印成多行,就需要在每次遍历前用一个变量size记录下队列中的元素数量,遍历size次,每次就可以得到一层的结点。

(在刚开始实现的时候没有想到用size来存储,是用了两个队列,一个用来遍历,另一个从来存储左右子结点)

②顺时针打印矩阵(细节优化)

方法很简单,使用up、down、left、right四个变量存储矩阵边界,按照右->下->左->上的顺序遍历,遍历过后调整边界值,但细节方面需要优化。

·循环条件:while(left <= right && up <= down)

·除了向右遍历时,每次遍历开始时要去除顶点元素,将顶点元素放到下一次遍历再记录。如果上一次遍历时不包括顶点元素,在处理单个元素时就会出现问题,因为单个元素时四个方向的共同顶点,就不会被记录,导致最后结果为空值。

③二叉树中和为n的路径

递归的终止条件很容易判断,就是cursum == expectNumber时,但需要一个数组记录当前路径中的值,这里使用数组path,不带引用,否则左右子树中的结果会互相影响。但最终还是能得到结果。如果遍历到子结点,要么输入,要么直接返回。

④字符串全排列

主要思路是每次固定一个位置的字符,改变其他位置的字符并存储ans,由于有可能有重复字符,因为先使用set(无重复元素的存储结果)存储,然后再返回到vector中。

⑤线索二叉树

正常的二叉树节点是只有left、right和val三个变量,分别用来存储当前节点的左子节点、右子节点和值。线索二叉树是有前驱和后继节点的,因为有的节点并没有左右子节点,其指针域是空的,这时候就将其指针域利用起来,指向前驱或者后继节点。至于前驱和后继具体是哪个节点,取决于遍历方式。线索二叉树是分为先序线索二叉树、中序线索二叉树和后序线索二叉树的,如果没有左子节点,那么左子节点就指向遍历结果中的前一个节点。同理,如果没有右子节点,那么右子节点就指向遍历结果中的后一个节点。

那么存在一个问题,在确定了每个节点的前驱后继节点后,如何区分其指针域中存的是原本的左子节点还是没有左子节点后的前驱节点呢?因此还需要因为两个tag变量,来标记是原节点还是前驱节点。Ltag和Rtag,Ltag为0时,代表其指向的是原本的左子节点,Ltag为1时,代表其指向的是前驱节点。因此线索二叉树的节点结构相对复杂一点,包括五块:Left、Ltag、val、Rtag、Right。

 

二、哈夫曼树构造算法及编码

①哈夫曼树

带权路径长度:在二叉树中,每个叶子结点都有给定的权值,从根节点到该叶子结点的路径长度与其权值的乘积,为带权路径长度。哈夫曼树是在相同条件下的带权路径长度最小的树,也被称为最优二叉树。

构造哈夫曼树:先选取两个权值最小的结点作为叶子结点,其权值相加作为父结点。再让这个得到的父结点与权值第三小的结点构成左右子节点,循环往上。

这样的形成方式决定了哈夫曼树只有度为0和2的结点,不存在度为1的结点。

②哈夫曼编码

首先为什么要进行哈夫曼编码,不能使用其他编码方式?

因为编码要保证的最重要的是没有任何两个字符的编码是前缀编码,如果有前缀编码就一定会造成解码时的歧义。(前缀编码:01和010,01就是010的前缀编码,这样解码的时候无法判断是01还是010)。哈夫曼树由于所有的元素都在叶子结点,因为路径一定是不同的,也就是不可能一个结点是另一个结点的父结点,这样就不会存在前缀编码,保证了唯一性,就可以进行编码传输。

在传输文件时,如果这个文件过大,为了节省传输开销,通过哈夫曼编码来减小传输过程中的文件大小。具体方法如下:

·首先统计文件中各个字符的出现数量,作为该字符的权值。创建一个数组来存储字符和其对应权值;

·构造哈夫曼树,使用上面的方法,构造出最有二叉树;

·进行哈夫曼编码,指向左子结点的路径记为0,指向右子结点的路径记为1,对每个字符的路径进行编码;

·对文件进行遍历,进行转换。

③解码

·通过接收到的频数文件构造哈夫曼树;

·0即向左走,1即向右走,一直读到叶子结点,得到当前字符。

·不断解码,得到文件。

三、vector数组的常见初始化

  1. 只是创建一个数组: vector<int> nums;
  2. 用另一个数组初始化数组: vector<int> nums1(nums2);vector<int> nums1=nums2;
  3. 直接写出数组的值:vector<int> nums=[1,2,3];
  4. 用另一个数组的部分初始化数组: vector<int> nums1(nums2.begin()+n,nums2.end()-n);
  5. 初始化时指定数组大小:vector<int> nums(2);
  6. 初始化时指定数组大小和元素: vector<int> nums(2,1).

 

直接放剑指Offer的题目和代码图片看不到,后面要避免直接放截图,一点用没有,很烦。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值