日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

贪心算法(Greedy Algorithm)之霍夫曼编码

發布時間:2024/7/5 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 贪心算法(Greedy Algorithm)之霍夫曼编码 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 1. 貪心算法
    • 2. 應用
      • 2.1 找零錢
      • 2.2 區間覆蓋
      • 2.3 霍夫曼編碼
        • 霍夫曼編碼完整代碼

1. 貪心算法

  • 我們希望在一定的限制條件下,獲得一個最優解
  • 每次都在當前的標準下做出當下最優決策(整體不一定最優),做出的決策不可以后悔,即不回溯,最終可以得到較為滿意的解
  • 貪心算法不追求最優解,節省時間,避免窮盡所有可能

2. 應用

2.1 找零錢

給定金額的找零,用最少張(枚)錢給顧客。(總是優先給大額的)

/*** @description: 貪心應用--找零錢* @author: michael ming* @date: 2019/6/28 22:02* @modified by: */ #include <iostream> #include <memory.h> #include <math.h> #define N 10 using namespace std; void exchange(float money, float *rmb, int *amount) {if(money < 0.1)return;int i, k = 0;for(i = 0; i < N; ++i){money = round(money*10)/10.0;//四舍五入掉分k = money/rmb[i];amount[i] = k;money = money-k*rmb[i];} } int main() {float rmb[N] = {100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1};int amount[N];while(1){memset(amount,0,N*sizeof(int));float money;cout << "請輸入要找的錢的金額:";cin >> money;money = round(money*10)/10.0;//四舍五入掉分cout << "找零結果如下(分位四舍五入):" << endl;exchange(money,rmb,amount);int i = 0;while(i < N){if(amount[i] != 0)cout << amount[i] << "個" << rmb[i] << " ";i++;}cout << endl;cout << "----------------------" << endl;} }

2.2 區間覆蓋

運用場景:任務調度,教師排課,學生活動室安排等

在上面圖中再加入些區間數據[2,3];[-1,4],[5,12];[4,5],代碼實現如下:

/*** @description: 貪心算法--區間覆蓋應用* (給定每個人可以在一個房間內活動的時間,要求讓最多的人在這個房間活動,打印出這些人)* @author: michael ming* @date: 2019/6/29 23:34* @modified by: */ #include <iostream> #include <algorithm> using namespace std; struct Interval {int left, right;bool operator<(const Interval &sel_idx) const{if(left == sel_idx.left)return right < sel_idx.right;//左端點相等,取右端小的elsereturn left < sel_idx.left;//左端不等,取左端小的}bool belongto(int l, int r)//區間屬于[l,r]的子集{return left >= l && right <= r;}bool absnotbelong(int l, int r)//兩個區間完全無重疊{return left >= r || right <= l;} }; int main() {const int N = 10;//區間數量int left = 1, right = 10;//大區間左右端點(房間開放時間)int select[N];//存儲選出來的區間(人)idint i, j, k, sel_idx = 0;for(i = 0; i < N; ++i){select[i] = -1;}Interval qujian[N] = {{1,5},{2,4},{3,5},{5,9},{6,8},{8,10},{2,3},{-1,4},{5,12},{4,5}};//所有人的占用時間段sort(qujian,qujian+N);//對區間進行排序(先按開始時間早排序,一樣早,按占用時間少排前面)for(i = 0; i < N; ++i){if(qujian[i].left >= left && qujian[i].right <= right)//占用時間在開放時間內{while(sel_idx != 0 && !qujian[i].absnotbelong(qujian[select[sel_idx-1]].left,qujian[select[sel_idx-1]].right)){//如果有人占用了房間,一直找到第一個跟他時間不沖突的人++i;}for(k = i, j = k+1; j < N; ++j){//找到時間是不沖突那個人的最小子集的人if(qujian[j].belongto(qujian[k].left,qujian[k].right)){k = j;}}select[sel_idx++] = k;//占用最短的那個人選出來i = k;//從這個人開始再往后找}}cout << "total selected " << sel_idx << " people, their time as following:" << endl;for(i = 0; i < N && select[i] != -1; ++i){//打印被選出來的人的時間信息cout << i << ": [" << qujian[select[i]].left << ","<< qujian[select[i]].right << "]" << endl;}return 0; }

2.3 霍夫曼編碼

  • 假設有一個包含1000個字符的文件,每個字符占1個byte(1byte=8bits),存儲這1000個字符一共需要8000bits,有沒有更加節省空間的存儲方式呢?
  • 假設通過統計發現,這1000個字符中只包含6種不同字符,假設它們分別是a、b、c、d、e、f。而3個二進制位(bit)就可以表示8個不同的字符,a(000)、b(001)、c(010)、d(011)、e(100)、f(101),所以,為了盡量減少存儲空間,每個字符我們用3個二進制位來表示。那存儲這1000個字符只需要3000bits就可以了,比原來的存儲方式節省了很多空間。
  • 還有沒有更加節省空間的存儲方式呢?
  • 霍夫曼編碼,考慮字符的出現頻率,頻率小的,用長編碼,大的,用短編碼,使得總體編碼長度變短(且由于其編碼方式,沒有一個字符的編碼是另一個的編碼的前綴,避免了解碼過程中的歧義)


霍夫曼編碼完整代碼

/*** @description: 貪心應用--霍夫曼編碼* @author: michael ming* @date: 2019/6/30 23:53* @modified by: */ #include <string> #include <vector> #include <queue> #include <iostream> #include <algorithm> #include <memory.h>#define N 6 //字符集字符種數 using namespace std; struct htNode//霍夫曼樹節點 {char data;//數據類型char code;//數據存放的節點編碼unsigned int weight;//數據權值htNode *parent, *lchild, *rchild;//連接節點指針htNode():data('/'),code('\0'),weight(0){parent = lchild = rchild = NULL;} }; class comp//優先隊列比較函數 { public:bool operator()(htNode* &a, htNode* &b)const{if(a->weight == b->weight)return a->data > b->data;return a->weight > b->weight;} };class HuffmanTree { public:htNode *root;//根節點指針htNode* node[2*N-1];//N個字符,霍夫曼樹節點個數2*N-1priority_queue<htNode*,vector<htNode*>,comp> pri_queue;//優先隊列中存放類指針時,第三個參數應該另寫一個comp類,類內寫operator()void creatTree_outputCode(int *w){char ch = 'a';htNode *left, *right;for(int i = 0; i < N; ++i,++ch){//生成前N個字符節點,輸入權重,和字符信息//并放入優先隊列(權值小的優先)node[i] = new htNode();node[i]->weight = w[i];node[i]->data = ch;pri_queue.push(node[i]);}for(int i = N; i < 2*N-1; ++i){//后面新生成的N-1個節點node[i] = new htNode();if(pri_queue.top()->data != '/'){//隊首的節點不是新生成的,隊首放右邊right = pri_queue.top();right->code = '1';//右邊節點編碼1pri_queue.pop();left = pri_queue.top();left->code = '0';//左邊節點編碼0pri_queue.pop();//左右節點出隊}else{//隊首是新生成的節點,放左邊//(以上if-else保證新生成的節點總在左邊)left = pri_queue.top();left->code = '0';pri_queue.pop();right = pri_queue.top();right->code = '1';pri_queue.pop();//左右節點出隊}//新節點權值、上下連接指針對接node[i]->weight = left->weight+right->weight;node[i]->lchild = left;node[i]->rchild = right;left->parent = node[i];right->parent = node[i];pri_queue.push(node[i]);//新生成的節點入隊}root = pri_queue.top();//最后還剩一個節點,是根節點creatHuffCode();for(int i = 0; i < 2*N-1; ++i)//釋放資源{delete node[i];}}void creatHuffCode(){htNode *parent;string huffcode;//霍夫曼編碼int codelen = 0;//輸入的字符串編碼后的總長度bitsfor(int i = 0; i < N; ++i)//遍歷前N個字符節點,求其編碼{huffcode = "";parent = node[i];//從自己(葉子節點)開始向上找父節點,直到rootcout << i+1 << " " << node[i]->data << " 的霍夫曼編碼是: ";while(parent != root)//{huffcode.push_back(parent->code);//將路徑中的編碼匯成字符串parent = parent->parent;}reverse(huffcode.begin(),huffcode.end());//將最終的編碼反轉一下cout << huffcode << endl;codelen += huffcode.size()*node[i]->weight;//單字符code長*出現次數}cout << "該字符串的huffman編碼長度為: " << codelen << " bits.";} };int main() {HuffmanTree huff;cout << "請輸入某字符串中" << N << "個字母abc...的權值(頻率):" << endl;int w[N];//權重for(int i = 0; i < N; ++i){cout << i+1 << " ";cin >> w[i];//輸入權值}huff.creatTree_outputCode(w);//將權值傳入并生成Huffman樹;生成霍夫曼編碼,打印出來return 0; }

總結

以上是生活随笔為你收集整理的贪心算法(Greedy Algorithm)之霍夫曼编码的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。