B树的原理与实现(C++)

B树的定义

一棵B树T是具有如下性质的有根树(根为root[T]):
1)每个结点x有如下域:
      a)n[x],当前存储在结点x中的关键字个数; 
      b)n[x]个关键字本身,以非降序存放,因此key 1 [x]≤key 2[x]≤…≤key n[x][x]
      c)leaf[x],是一个布尔值,如果x是叶子结点的话,则它为TRUE,如果x为一个内结点,则它为FALSE。
2)每个内结点x还包含n[x]+1个指向其子女的指针c 1[x],c 2[x],…,c n[x]+1[x]。叶结点没有子女,故它们的c i域无定义。
3)各关键字Keyi[x]对存储在各子树中的关键字范围加以分隔:如果ki为存储在以ci[x]为根的子树中的关键字,则        k 1≤key 1[x]≤k 2≤key 2[x]≤…≤key n[x][x]≤k n[x]+1
4)每个叶结点具有相同的深度,即树的高度h。
5)每一个结点能包含的关键字数有一个上界和下界。这些界可用一个称作B树的最小度数(即一个结点中可指向的孩子结点个数)的固定整数t≥2来表示。
       a)每个非根的结点必须至少有t-1个关键字,每个非根的内结点必须至少有t个子女。如果树是非空的,则根结点至少包含一个关键字。
       b)每个结点可包含至多2t-1个关键字。所以一个内结点至多可有2t个子女。我们说一个结点是满的,如果它恰好有2t-1个关键字。

对B树的基本操作

搜索B树

搜索B树与搜索二叉树很相似,只是在每个结点所做的不是二叉或者“两路”分支决定,二是根据该结点的子女数所做的多路分支决定。更准确的说,在每个内结点x处,要做n[x]+1路的分支决定。

向B树插入关键字

向B树中插入关键字,同二叉查找树中插入一个关键字类似,要查找插入新关键字的叶子结点位置。因为不能把关键字插入到一个已满的叶子结点中,故需要将一个已满的结点按其中间关键字分裂成两个结点,中间关键字被提升到该结点的父结点中。但是这种满结点的分裂动作会沿着树向上传播。为了解决这个问题,可以采取这样一种策略:当沿着树根往下查找新关键字所属位置时,就沿途分裂遇到的每个满结点。因此,每当要分裂一个满结点时,就能确保它的父结点不是满的。

从B树中删除关键字

B树上的删除操作与插入操作类似,只是稍微复杂点,因为一个关键字能够从任意一个结点中删除,而不只是叶结点。就要我们必须保证一个结点不会因为插入而变得太大一样,必须保证一个结点不会因为删除而变得太小。下面,大致描述一下删除关键字的各种情况:
1)如果关键字k在结点x中而且x是个叶结点,则从x中删除k。
2)如果关键字k在结点x中而且x是个内结点,则作如下操作:
      a)如果结点x中前于k的子结点y包含至少t个关键字,则找出k在以y为根的子树中的前驱k‘。递归的删除k’,并在x中用k‘取代k。
      b)对称地,如果结点x中位于k之后的子结点z包含至少t个关键字,则找出k在以z为根的子树中的后继k’。递归的删除k‘,并在x中使用k’取代k。
      c)否则,如果y和z都只有t-1个关键字,则将k和z中所有关键字合并进y,使得x失去k和指向z的指针,这使y包含2t-1个关键字。然后,释放z并将k从y中递归删除。
3)如果关键字k不在内结点x中,则确定包含k的正确的子树的根ci[x]。如果ci[x]只有t-1个关键字,执行步骤3a或3b以保证我们降至一个包含至少t个关键字的结点。然后,通过对x的某个合适的子结点递归而结束。
      a)如果ci[x]只包含t-1个关键字,但它的一个相邻兄弟结点包含至少t个关键字,则将x中的某一个关键字降至ci[x]中,将ci[x]的相邻左兄弟或右兄弟中的某一关键字升至x,将该兄弟中合适的子结点指针移到ci[x]中,这样使得ci[x]增加一个额外的关键字。
      b)如果ci[x]以及ci[x]的所有相邻兄弟结点都只包含t-1个关键字,则将ci[x]与任意一个兄弟合并,则将x的一个关键字移至新合并的结点,使之成为新结点的中间关键字。


B树的c++实现代码

#pragma once
template<class T>
class CBTree
{
private:
	static const int M = 3;                  //B树的最小度数
	static const int KEY_MAX = 2*M-1;        //节点包含关键字的最大个数
	static const int KEY_MIN = M-1;          //非根节点包含关键字的最小个数
	static const int CHILD_MAX = KEY_MAX+1;  //孩子节点的最大个数
	static const int CHILD_MIN = KEY_MIN+1;  //孩子节点的最小个数
	struct Node
	{
		bool isLeaf;             //是否是叶子节点
		int keyNum;              //节点包含的关键字数量
		T keyValue[KEY_MAX];     //关键字的值数组
		Node *pChild[CHILD_MAX]; //子树指针数组

		Node(bool b=true, int n=0)
			:isLeaf(b), keyNum(n){}
	};
public:
	CBTree()
	{
		m_pRoot = NULL;  //创建一棵空的B树
	}

	~CBTree()
	{
		clear();
	}

	bool insert(const T &key)    //向B数中插入新结点key
	{
		if (contain(key))  //检查该关键字是否已经存在
		{
			return false;
		}
		else
		{
			if (m_pRoot==NULL)//检查是否为空树
			{
				m_pRoot = new Node();
			}
			if (m_pRoot->keyNum==KEY_MAX) //检查根节点是否已满
			{
				Node *pNode = new Node();  //创建新的根节点
				pNode->isLeaf = false;
				pNode->pChild[0] = m_pRoot;
				splitChild(pNode, 0, m_pRoot);
				m_pRoot = pNode;  //更新根节点指针
			}
			insertNonFull(m_pRoot, key);
			return true;
		}
	}

	bool remove(const T &key)    //从B中删除结点key
	{
		if (!search(m_pRoot, key))  //不存在
		{
			return false;
		}
		if (m_pRoot->keyNum==1)//特殊情况处理
		{
			if (m_pRoot->isLeaf)
			{
				clear();
				return true;
			}
			else
			{
				Node *pChild1 = m_pRoot->pChild[0];
				Node *pChild2 = m_pRoot->pChild[1];
				if (pChild1->keyNum==KEY_MIN&&pChild2->keyNum==KEY_MIN)
				{
					mergeChild(m_pRoot, 0);
					deleteNode(m_pRoot);
					m_pRoot = pChild1;
				}
			}
		}
		recursive_remove(m_pRoot, key);
		return true;
	}
	void display()const //打印树的关键字
	{
		displayInConcavo(m_pRoot,KEY_MAX*10);
	}
	bool contain(const T &key)const   //检查该key是否存在于B树中
	{
		return search(m_pRoot, key);
	}
	void clear()                      //清空B树
	{
		recursive_clear(m_pRoot);
		m_pRoot = NULL;
	}
private:
	//删除树
	void recursive_clear(Node *pNode)
	{
		if (pNode!=NULL)
		{
			if (!pNode->isLeaf)
			{
				for(int i=0; i<=pNode->keyNum; ++i)
					recursive_clear(pNode->pChild[i]);
			}
			deleteNode(pNode);
		}
	}

	//删除节点
	void deleteNode(Node *&pNode)
	{
		if (pNode!=NULL)
		{
			delete pNode;
			pNode = NULL;
		}
	}

	//查找关键字
	bool search(Node *pNode, const T &key)const 
	{
		if (pNode==NULL)  //检测节点指针是否为空,或该节点是否为叶子节点
		{
			return false;
		}
		else
		{
			int i;
			for (i=0; i<pNode->keyNum && key>*(pNode->keyValue+i); ++i)//找到使key<=pNode->keyValue[i]成立的最小下标i
			{
			}
			if (i<pNode->keyNum && key==pNode->keyValue[i])
			{
				return true;
			}
			else
			{
				if (pNode->isLeaf)   //检查该节点是否为叶子节点
				{
					return false;
				}
				else
				{
					return search(pNode->pChild[i], key);
				}
			}
		}
	}

	//分裂子节点
	void splitChild(Node *pParent, int nChildIndex, Node *pChild)  
	{
		//将pChild分裂成pLeftNode和pChild两个节点
		Node *pRightNode = new Node();//分裂后的右节点
		pRightNode->isLeaf = pChild->isLeaf;
		pRightNode->keyNum = KEY_MIN;
		int i;
		for (i=0; i<KEY_MIN; ++i)//拷贝关键字的值
		{
			pRightNode->keyValue[i] = pChild->keyValue[i+CHILD_MIN];
		}
		if (!pChild->isLeaf)  //如果不是叶子节点,拷贝孩子节点指针
		{
			for (i=0; i<CHILD_MIN; ++i)
			{
				pRightNode->pChild[i] = pChild->pChild[i+CHILD_MIN];
			}
		}
		
		pChild->keyNum = KEY_MIN;  //更新左子树的关键字个数

		for (i=pParent->keyNum; i>nChildIndex; --i)//将父节点中的nChildIndex后的所有关键字的值和子树指针向后移一位
		{
			pParent->pChild[i+1] = pParent->pChild[i];
			pParent->keyValue[i] = pParent->keyValue[i-1];
		}
		++pParent->keyNum;  //更新父节点的关键字个数
		pParent->pChild[nChildIndex+1] = pRightNode;  //存储右子树指针
		pParent->keyValue[nChildIndex] = pChild->keyValue[KEY_MIN];//把节点的中间值提到父节点
	}

	//在非满节点中插入关键字
	void insertNonFull(Node *pNode, const T &key)
	{
		int i = pNode->keyNum;  //获取节点内关键字个数
		if (pNode->isLeaf)      //pNode是叶子节点
		{
			while (i>0&&key<pNode->keyValue[i-1])   //从后往前,查找关键字的插入位置
			{
				pNode->keyValue[i] = pNode->keyValue[i-1];  //向后移位
				--i;
			}
			pNode->keyValue[i] = key;  //插入关键字的值
			++pNode->keyNum; //更新节点关键字的个数
		}
		else//pNode是内节点
		{
			while(i>0&&key<pNode->keyValue[i-1])   //从后往前,查找关键字的插入的子树
				--i;
			Node *pChild = pNode->pChild[i];  //目标子树结点指针 
			if (pChild->keyNum==KEY_MAX)  //子树节点已满
			{
				splitChild(pNode, i, pChild);//分裂子树节点
				if(key>pNode->keyValue[i])   //确定目标子树
					pChild = pNode->pChild[i+1];
			}
			insertNonFull(pChild, key);  //插入关键字到目标子树节点
		}
	}

	//用括号打印树
	void displayInConcavo(Node *pNode, int count)const
	{
		if (pNode!=NULL)
		{
			int i, j;
			for (i=0; i<pNode->keyNum; ++i)
			{
				if (!pNode->isLeaf)
				{
					displayInConcavo(pNode->pChild[i], count-2);
				}
				for (j=count; j>=0; --j)
				{
					cout<<"-";
				}
				cout<<pNode->keyValue[i]<<endl;
			}
			if (!pNode->isLeaf)
			{
				displayInConcavo(pNode->pChild[i], count-2);
			}
		}
	}

	//合并两个子节点
	void mergeChild(Node *pParent, int index)
	{
		Node *pChild1 = pParent->pChild[index];
		Node *pChild2 = pParent->pChild[index+1];
		//将pChild2数据合并到pChild1
		pChild1->keyNum = KEY_MAX;
		pChild1->keyValue[KEY_MIN] = pParent->keyValue[index];//将父节点index的值下移
		int i;
		for (i=0; i<KEY_MIN; ++i)
		{
			pChild1->keyValue[i+KEY_MIN+1] = pChild2->keyValue[i];
		}
		if (!pChild1->isLeaf)
		{
			for (i=0; i<CHILD_MIN; ++i)
			{
				pChild1->pChild[i+CHILD_MIN] = pChild2->pChild[i];
			}
		}

		//父节点删除index的key,index后的往前移一位
		--pParent->keyNum;
		for(i=index; i<pParent->keyNum; ++i)
		{
			pParent->keyValue[i] = pParent->keyValue[i+1];
			pParent->pChild[i+1] = pParent->pChild[i+2];
		}
		deleteNode(pChild2);  //删除pChild2
	}

	//递归的删除关键字
    void recursive_remove(Node *pNode, const T &key)
	{
		int i=0;
		while(i<pNode->keyNum&&key>pNode->keyValue[i])
			++i;
		if (i<pNode->keyNum&&key==pNode->keyValue[i])//关键字key在节点pNode中
		{
			if (pNode->isLeaf)//pNode是个叶节点
			{
				//从pNode中删除k
				--pNode->keyNum;
				for (; i<pNode->keyNum; ++i)
				{
					pNode->keyValue[i] = pNode->keyValue[i+1];
				}
				return;
			}
			else//pNode是个内节点
			{
				Node *pChildPrev = pNode->pChild[i];//节点pNode中前于key的子节点
				Node *pChildNext = pNode->pChild[i+1];//节点pNode中后于key的子节点
				if (pChildPrev->keyNum>=CHILD_MIN)//节点pChildPrev中至少包含CHILD_MIN个关键字
				{
					T prevKey = getPredecessor(pChildPrev); //获取key的前驱关键字
				    recursive_remove(pChildPrev, prevKey);
					pNode->keyValue[i] = prevKey;     //替换成key的前驱关键字
					return;
				}
				else if (pChildNext->keyNum>=CHILD_MIN)//节点pChildNext中至少包含CHILD_MIN个关键字
				{
					T nextKey = getSuccessor(pChildNext); //获取key的后继关键字
					recursive_remove(pChildNext, nextKey);
					pNode->keyValue[i] = nextKey;     //替换成key的后继关键字
					return;
				}
				else//节点pChildPrev和pChildNext中都只包含CHILD_MIN-1个关键字
				{
					mergeChild(pNode, i);
					recursive_remove(pChildPrev, key);
				}
			}
		}
		else//关键字key不在节点pNode中
		{
			Node *pChildNode = pNode->pChild[i];//包含key的子树根节点
			if (pChildNode->keyNum==KEY_MIN)//只有t-1个关键字
			{
				Node *pLeft = i>0 ? pNode->pChild[i-1] : NULL;  //左兄弟节点
				Node *pRight = i<pNode->keyNum ? pNode->pChild[i+1] : NULL;//右兄弟节点
				int j;
				if (pLeft&&pLeft->keyNum>=CHILD_MIN)//左兄弟节点至少有CHILD_MIN个关键字
				{
					//父节点中i-1的关键字下移至pChildNode中
					for (j=pChildNode->keyNum; j>0; --j)  
					{
						pChildNode->keyValue[j] = pChildNode->keyValue[j-1];
					}
					pChildNode->keyValue[0] = pNode->keyValue[i-1];
					
					if (!pLeft->isLeaf)  
					{
						for (j=pChildNode->keyNum+1; j>0; --j) //pLeft节点中合适的子女指针移植到pChildNode中
						{
							pChildNode->pChild[j] = pChildNode->pChild[j-1];
						}
						pChildNode->pChild[0] = pLeft->pChild[pLeft->keyNum];
					}
					++pChildNode->keyNum;
					pNode->keyValue[i] = pLeft->keyValue[pLeft->keyNum-1];//pLeft节点中的最大关键字上升到pNode中
					--pLeft->keyNum;
				}
				else if (pRight&&pRight->keyNum>=CHILD_MIN)//右兄弟节点至少有CHILD_MIN个关键字
				{
					//父节点中i的关键字下移至pChildNode中
					pChildNode->keyValue[pChildNode->keyNum] = pNode->keyValue[i];
					++pChildNode->keyNum;
					pNode->keyValue[i] = pRight->keyValue[0];//pRight节点中的最小关键字上升到pNode中
					--pRight->keyNum;
					for (j=0; j<pRight->keyNum; ++j)
					{
						pRight->keyValue[j] = pRight->keyValue[j+1];
					}
					if (!pRight->isLeaf)  
					{
						pChildNode->pChild[pChildNode->keyNum] = pRight->pChild[0];//pRight节点中合适的子女指针移植到pChildNode中
					    for (j=0; j<=pRight->keyNum; ++j)
					    {
							pRight->pChild[j] = pRight->pChild[j+1];
					    }
					}
				}
				//左右兄弟节点都只包含CHILD_MIN-1个节点
				else if (pLeft)//与左兄弟合并
				{
					mergeChild(pNode, i-1);
					pChildNode = pLeft;
				}
				else if (pRight)//与右兄弟合并
				{
					mergeChild(pNode, i);

				}
			}
			recursive_remove(pChildNode, key);
		}
	}

	T getPredecessor(Node *pNode)//找到前驱关键字
	{
		while (!pNode->isLeaf)
		{
			pNode = pNode->pChild[pNode->keyNum];
		}
		return pNode->keyValue[pNode->keyNum-1];
	}

	T getSuccessor(Node *pNode)//找到后继关键字
	{
		while (!pNode->isLeaf)
		{
			pNode = pNode->pChild[0];
		}
		return pNode->keyValue[0];
	}
	
private:
	Node * m_pRoot;  //B树的根节点
};


  • 3
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
### 回答1: 以下是哈夫曼树的c++实现,包括创建哈夫曼树和哈夫曼编码的实现: ```cpp #include <iostream> #include <queue> #include <vector> #include <unordered_map> using namespace std; // 定义哈夫曼树的节点结构体 struct Node { char ch; // 字符 int freq; // 字符出现的频率 Node *left, *right; // 左右子节点 Node(char c, int f) : ch(c), freq(f), left(nullptr), right(nullptr) {} }; // 定义大根堆的比较函数,用于priority_queue struct cmp { bool operator()(Node* a, Node* b) { return a->freq < b->freq; } }; // 创建哈夫曼树 Node* huffman_tree(const string& s) { unordered_map<char, int> freq; // 统计每个字符出现的频率 for (char c : s) { freq[c]++; } priority_queue<Node*, vector<Node*>, cmp> pq; // 定义大根堆,用于构建哈夫曼树 for (auto& p : freq) { pq.push(new Node(p.first, p.second)); } while (pq.size() > 1) { Node* left = pq.top(); pq.pop(); Node* right = pq.top(); pq.pop(); Node* parent = new Node('\0', left->freq + right->freq); // 创建新的父节点 parent->left = left; parent->right = right; pq.push(parent); } return pq.top(); } // 递归获取哈夫曼编码 void get_code(Node* root, string code, unordered_map<char, string>& huffman_code) { if (!root) return; if (root->ch != '\0') { huffman_code[root->ch] = code; } get_code(root->left, code + "0", huffman_code); get_code(root->right, code + "1", huffman_code); } // 哈夫曼编码 unordered_map<char, string> huffman_encode(const string& s) { Node* root = huffman_tree(s); unordered_map<char, string> huffman_code; get_code(root, "", huffman_code); return huffman_code; } int main() { string s = "hello world"; unordered_map<char, string> huffman_code = huffman_encode(s); cout << "哈夫曼编码表:" << endl; for (auto& p : huffman_code) { cout << p.first << ": " << p.second << endl; } return 0; } ``` 上述代码中,首先使用 `unordered_map` 统计字符串中每个字符出现的频率,然后将每个字符和其频率封装成一个节点,加入大根堆中。接着依次弹出两个权值最小的节点,将它们作为左右子节点,创建一个新的父节点,将父节点再次加入大根堆中。重复上述步骤,直到堆中只剩下一个节点,这个节点即为哈夫曼树的根节点。 创建哈夫曼树后,使用递归的方式获取每个字符的哈夫曼编码。如果是左子节点,则在编码的末尾添加 0;如果是右子节点,则在编码的末尾添加 1。如果到达了叶子节点,则将该字符和它的哈夫曼编码加入哈夫曼编码表中。 最终输出哈夫曼编码表,其中键为字符,值为对应的哈夫曼编码。 ### 回答2: 哈夫曼树是一种用于数据压缩和编码的树形数据结构,其特点是根据数据出现的频率构建出最优的编码方式。下面是哈夫曼树的一个简单实现示例。 首先,我们需要定义一个结构体来表示哈夫曼树的节点,包含节点的权重和指向左右子节点的指针。 ``` struct HuffmanNode { int weight; struct HuffmanNode *left, *right; }; ``` 接下来,我们需要定义几个辅助函数来构建哈夫曼树。 首先是一个比较函数,用于对节点按照权重进行排序。 ``` int compare(const void *a, const void *b) { struct HuffmanNode **pa = (struct HuffmanNode **)a; struct HuffmanNode **pb = (struct HuffmanNode **)b; return (*pa)->weight - (*pb)->weight; } ``` 然后是一个用于构建哈夫曼树的函数,传入待编码的字符及其对应的权重数组。 ``` struct HuffmanNode *buildHuffmanTree(char *data, int *weights, int n) { // 创建节点数组 struct HuffmanNode **nodes = (struct HuffmanNode **)malloc(sizeof(struct HuffmanNode *) * n); for (int i = 0; i < n; i++) { nodes[i] = (struct HuffmanNode *)malloc(sizeof(struct HuffmanNode)); nodes[i]->weight = weights[i]; nodes[i]->left = nodes[i]->right = NULL; } // 按照权重排序节点 qsort(nodes, n, sizeof(struct HuffmanNode *), compare); // 通过循环构建哈夫曼树 while (n > 1) { // 取出权重最小的两个节点作为新节点的左右子节点 struct HuffmanNode *left = nodes[0]; struct HuffmanNode *right = nodes[1]; // 创建新节点 struct HuffmanNode *newNode = (struct HuffmanNode *)malloc(sizeof(struct HuffmanNode)); newNode->weight = left->weight + right->weight; newNode->left = left; newNode->right = right; // 更新节点数组 nodes[0] = newNode; memmove(nodes + 1, nodes + 2, sizeof(struct HuffmanNode *) * (n - 2)); // 数组长度减少1 n--; // 重新排序 qsort(nodes, n, sizeof(struct HuffmanNode *), compare); } // 返回树的根节点 return nodes[0]; } ``` 最后,我们可以编写一个函数来测试上述代码,输入一组字符及其权重,输出构建的哈夫曼树的结果。 ``` void test() { char data[] = {'a', 'b', 'c', 'd', 'e'}; int weights[] = {5, 3, 2, 1, 1}; int n = sizeof(data) / sizeof(char); struct HuffmanNode *root = buildHuffmanTree(data, weights, n); // 输出树的结果 printTree(root); } ``` 这是一个简单的哈夫曼树的实现示例,实际应用中可能需要考虑更多的边界情况和优化,但以上代码可以用作初步理解哈夫曼树的实现原理。 ### 回答3: 哈夫曼树是一种经典的数据结构,可以用来构建最优的哈夫曼编码。在C语言中,我们可以通过构造二叉树的方式来实现哈夫曼树。 首先,我们需要定义一个结构体来表示二叉树的节点,包含节点的权值和指向左右子节点的指针。 ```c typedef struct Node { int weight; // 节点的权值 struct Node *left; // 左子节点指针 struct Node *right; // 右子节点指针 } Node; ``` 接下来,我们可以定义一些用于操作哈夫曼树的函数。 1. 创建单个节点的函数 `createNode`: ```c Node * createNode(int weight) { Node *node = (Node *)malloc(sizeof(Node)); node->weight = weight; node->left = NULL; node->right = NULL; return node; } ``` 2. 构造哈夫曼树的函数 `buildHuffmanTree`: ```c Node * buildHuffmanTree(int weights[], int n) { Node *nodes[2*n-1]; // 初始化一个足够大的节点数组 for (int i = 0; i < n; i++) { nodes[i] = createNode(weights[i]); // 将权值数组中的每个值创建为独立节点 } for (int i = n; i < 2*n-1; i++) { // 每次选择两个最小权值的节点生成新的根节点,直到只剩下一个节点 int minIndex1 = -1, minIndex2 = -1; // 记录最小权值的两个节点下标 for (int j = 0; j < i; j++) { if (nodes[j] != NULL && (minIndex1 == -1 || nodes[j]->weight < nodes[minIndex1]->weight)) { minIndex2 = minIndex1; minIndex1 = j; } else if (nodes[j] != NULL && (minIndex2 == -1 || nodes[j]->weight < nodes[minIndex2]->weight)) { minIndex2 = j; } } // 生成新节点,以两个最小权值的节点为其左右子节点 nodes[i] = createNode(nodes[minIndex1]->weight + nodes[minIndex2]->weight); nodes[i]->left = nodes[minIndex1]; nodes[i]->right = nodes[minIndex2]; // 将已选用的两个权值最小的节点置为NULL,表示已合并 nodes[minIndex1] = NULL; nodes[minIndex2] = NULL; } return nodes[2*n-2]; // 返回根节点 } ``` 通过这两个函数,我们就能够实现哈夫曼树的构建。构建好的哈夫曼树可以用于编码和解码等操作,它能够根据字符出现的频率构造最优编码,使得编码后的数据长度更短,节省存储空间和传输带宽。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值