C++蓝桥杯 基础练习 Huffuman树 HERODING的蓝桥杯之路

资源限制
时间限制:1.0s 内存限制:512.0MB
问题描述
  Huffman树在编码中有着广泛的应用。在这里,我们只关心Huffman树的构造过程。
  给出一列数{pi}={p0, p1, …, pn-1},用这列数构造Huffman树的过程如下:
  1. 找到{pi}中最小的两个数,设为pa和pb,将pa和pb从{pi}中删除掉,然后将它们的和加入到{pi}中。这个过程的费用记为pa + pb。
  2. 重复步骤1,直到{pi}中只剩下一个数。
  在上面的操作过程中,把所有的费用相加,就得到了构造Huffman树的总费用。
  本题任务:对于给定的一个数列,现在请你求出用该数列构造Huffman树的总费用。

例如,对于数列{pi}={5, 3, 8, 2, 9},Huffman树的构造过程如下:
  1. 找到{5, 3, 8, 2, 9}中最小的两个数,分别是2和3,从{pi}中删除它们并将和5加入,得到{5, 8, 9, 5},费用为5。
  2. 找到{5, 8, 9, 5}中最小的两个数,分别是5和5,从{pi}中删除它们并将和10加入,得到{8, 9, 10},费用为10。
  3. 找到{8, 9, 10}中最小的两个数,分别是8和9,从{pi}中删除它们并将和17加入,得到{10, 17},费用为17。
  4. 找到{10, 17}中最小的两个数,分别是10和17,从{pi}中删除它们并将和27加入,得到{27},费用为27。
  5. 现在,数列中只剩下一个数27,构造过程结束,总费用为5+10+17+27=59。
输入格式
  输入的第一行包含一个正整数n(n<=100)。
  接下来是n个正整数,表示p0, p1, …, pn-1,每个数不超过1000。
输出格式
  输出用这些数构造Huffman树的总费用。
样例输入
5
5 3 8 2 9
样例输出
59

解题思路:
哈弗曼树是一个比较经典的算法,其应用场景在破解密码、加密等方面颇有建树。今天这道题其实思路很简单,用贪心的思想每次都获取最小的两个数相加之后,再放回树的数组中,如此反复,直到数组中只有一个元素,输出构建过程的代价。这里我用两个方法为大家解答。
第一个方法:最小堆,学过数据结构的朋友都知道,写最小堆的过程是如此艰辛,但是使用起来,不得不说,那是真香呀,先把所有输入的数放入最小堆中,每次从最小堆中获取堆顶元素,其一定是最小值,如此反复,直到堆中只有一个元素。
第二个方法:利用sort()函数排序,每次都拿最小的元素,注意要删除多余的元素哦。

以下是两种方法:

#include<bits/stdc++.h>

using namespace std;

template<class T>
class MinHeap {
private:
	T *heapArray;			// 存放堆数据的数组
	int CurrentSize;			// 当前堆中元素数目
	int MaxSize;			// 堆所能容纳的最大元素数目
public:
	MinHeap(const int n);			// 构造函数,n表示 堆的最大元素数目
	virtual ~MinHeap() { delete[]heapArray; }	// 析构函数
	void swap(int pos_x, int pos_y);	// 交换位置x和y的元素
	void BuildHeap();			// 建堆
	bool isEmpty();			// 如果堆空,则返回真
	bool isLeaf(int pos) const;	    		// 如果是叶结点,返回TRUE
	int leftchild(int pos) const;	   		// 返回左孩子位置
	int rightchild(int pos) const;		// 返回右孩子位置
	int parent(int pos) const;			// 返回父结点位置
	bool Remove(int pos, T& node); 		// 删除给定下标的元素
	bool Insert(const T& newNode);		// 向堆中插入新元素newNode
	T& RemoveMin();			// 从堆顶删除最小值
	void SiftUp(int position);		 // 从position向上开始调整,使序列成为堆
	void SiftDown(int left);         	// 向下筛选,参数left表示开始处理的数组下标
	void show();
	int getCurrentSize();
};

template<class T>
int MinHeap<T>::getCurrentSize() {
	return CurrentSize;
}


template<class T>
bool MinHeap<T>::isEmpty() {
	if (CurrentSize == 0) {
		return true;
	}
	else return false;
}

template<class T>
bool MinHeap<T>::isLeaf(int pos) const {
	if (pos % 2 == 1) {
		return true;
	}
	else return false;
}

template<class T>
void MinHeap<T>::swap(int pos_x, int pos_y) {
	T data = heapArray[pos_x];
	heapArray[pos_x] = heapArray[pos_y];
	heapArray[pos_y] = data;
}

template<class T>
void MinHeap<T>::BuildHeap() {
	for (int i = CurrentSize / 2 - 1; i >= 0; i--)// 反复调用筛选函数
		SiftDown(i);
}


template<class T>
MinHeap<T>::MinHeap(const int n) {
	if (n <= 0)     return;
	CurrentSize = 0;
	MaxSize = n;				// 初始化堆容量为n
	heapArray = new T[MaxSize];		// 创建堆空间
	BuildHeap();  // 此处进行堆元素的赋值工作
}

template<class T>
int MinHeap<T>::leftchild(int pos) const {
	return 2 * pos + 1;					 	// 返回左孩子位置
}

template<class T>
int MinHeap<T>::rightchild(int pos) const {
	return 2 * pos + 2;					   	// 返回右孩子位置
}
template<class T>
int MinHeap<T>::parent(int pos) const {
	return (pos - 1) / 2;				   		// 返回父结点位置
}

template <class T>
bool MinHeap<T>::Insert(const T& newNode) { // 向堆中插入新元素newNode
	if (CurrentSize == MaxSize)	return false; // 堆空间已经满
	heapArray[CurrentSize] = newNode;
	SiftUp(CurrentSize);					// 向上调整
	CurrentSize++;
	return true;
}

template<class T>
T& MinHeap<T>::RemoveMin() {	// 从堆顶删除最小值
	if (CurrentSize == 0) {
		cout << "Can't Delete";		// 调用RemoveMin之前,需要判断堆非空
		exit(1);
	}
	else {
		swap(0, --CurrentSize);	                 // 交换堆顶和最后一个元素
		if (CurrentSize > 1) {
			SiftDown(0);   // 从堆顶开始筛选
		}
		return heapArray[CurrentSize];
	}
}
template<class T>
bool MinHeap<T>::Remove(int pos, T& node) {   	// 删除给定下标的元素
	if ((pos < 0) || (pos >= CurrentSize))     return false;
	node = heapArray[pos];
	heapArray[pos] = heapArray[--CurrentSize];	// 用最后的元素值替代删除位置的元素
	if (heapArray[parent(pos)] > heapArray[pos])
		SiftUp(pos);			// 当前元素小于父结点,需要上升调整
	else SiftDown(pos);			// 当前元素大于父结点,向下筛
	return true;
}

template<class T>
void MinHeap<T>::SiftUp(int position) {
	// 从position向上开始调整
	int temppos = position;
	T temp = heapArray[temppos];
	while ((temppos > 0) && (heapArray[parent(temppos)] > temp)) {
		heapArray[temppos] = heapArray[parent(temppos)];
		temppos = parent(temppos);
	}
	heapArray[temppos] = temp;
}

template <class T>
void MinHeap<T>::SiftDown(int left) {
	int i = left;			// 标识父结点
	int j = leftchild(i); 			// 标识关键值较小的子结点
	T temp = heapArray[i];		// 保存父结点
	while (j < CurrentSize) {       		// 过筛
		if ((j < CurrentSize - 1) && (heapArray[j] > heapArray[j + 1]))
			//若有右子节点,且小于左子节点
			j++;							// j指向右子结点
		if (temp > heapArray[j]) {  //若父节点大于子节点的值则交换位置
			heapArray[i] = heapArray[j];
			i = j;
			j = leftchild(j);
		}
		else break;  //堆序满足,跳出
	}
	heapArray[i] = temp;
}

template<class T>
void MinHeap<T>::show() {
	for (int i = 0;i < CurrentSize; i++)
	{
		cout << heapArray[i] << " ";
	}
}
int main() {
	MinHeap<int> heap(100);
	int i, n;
	cin >> n; 
	for(i = 0; i < n; i++) {
		int data;
		cin >> data;
		heap.Insert(data);
	}
	int sum = 0;
	int num = 0;
	while(heap.getCurrentSize() > 1){
		num += heap.RemoveMin();
		num += heap.RemoveMin();
		sum += num;
		heap.Insert(num);
		num = 0;
	} 
	cout << sum;
	return 0;
}
#include<bits/stdc++.h>

using namespace std;

int main()
{
    int a[100];
    int i,n;
    cin >> n;
    for(i = 0; i < n; i++) {
    	cin >> a[i];
	}
    sort(a,a+n);//从小到大排序
    int sum = 0;
    while(n > 1)
    {
        i=0;
        a[i] = a[i] + a[i+1];
        sum += a[i];
        a[i + 1] = -1;//将这个数去除
        sort(a,a+n);
        for(i = 0;i < n-1; i++)//把-1值覆盖 
            a[i] = a[i+1];
    n--;
}
    cout << sum;
    return 0;
}

发布了7 篇原创文章 · 获赞 8 · 访问量 804

猜你喜欢

转载自blog.csdn.net/HERODING23/article/details/105673503
今日推荐