275.算法設計工具―STL

1.概述

1.1定義

     STL主要由container(容器)、algorithm(算法)和iterator(迭代器)三大部分構成,容器用於存放數據對象(元素),算法用於操做容器中的數據對象。html

 

 

1.2stl容器

   一個STL容器就是一種數據結構,如鏈表、棧和隊列等,這些數據結構在STL中都已經實現好了,在算法設計中能夠直接使用它們。ios

 

 

 爲此,使用STL時必須將下面的語句插入到源代碼文件開頭:程序員

    using namespace std;算法

這樣直接把程序代碼定位到std命名空間中。數據庫

 

1.3STL算法

    STL算法是用來操做容器中數據的模板函數,STL提供了大約100個實現算法的模版函數。例如,STL用sort()來對一個vector中的數據(通用數據)進行排序,用find()來搜索一個list中的對象。數組

    STL算法部分主要由頭文件<algorithm>、<numeric>和<functional>組成。數據結構

 

例如,如下程序使用STL算法sort()實現整型數組a的遞增排序:app

#include <algorithm>
using namespace std; 
void main()  
{  int a[]={2,5,4,1,3};
   sort(a,a+5);
   for (int i=0;i<5;i++)
    printf("%d ",a[i]);    //輸出: 1 2 3 4 5
   printf("\n");
}

 

1.4stl迭代器

指針的另外一個說法less

    STL迭代器用於訪問容器中的數據對象。函數

    每一個容器都有本身的迭代器,只有容器本身才知道如何訪問本身的元素。

    迭代器像C/C++中的指針,算法經過迭代器來定位和操做容器中的元素。

(整型指針只能指整型,向量指向量)

 

經常使用的迭代器有:

iterator:指向容器中存放元素的迭代器,用於正向遍歷容器中的元素。

const_iterator:指向容器中存放元素的常量迭代器,只能讀取容器中的元素。

reverse_iterator:指向容器中存放元素的反向迭代器,用於反向遍歷容器中的元素。

const_reverse_iterator:指向容器中存放元素的常量反向迭代器,只能讀取容器中的元素。

 

迭代器的經常使用運算以下:

++:正向移動迭代器。

--:反向移動迭代器。

*:返回迭代器所指的元素值。

 

vector<int> myv;  //即將定義一個整型變量 
myv.push_back(1); //push_back尾部插入
myv.push_back(2);
myv.push_back(3); //向量的定義和變量的插入
vector<int>::iterator it;    //定義正向迭代器it
for (it=myv.begin();it!=myv.end();++it)
                //從頭至尾遍歷全部元素  myv.end()最後一個元素的後面
    printf("%d ",*it);    //輸出:1 2 3
printf("\n");
vector<int>::reverse_iterator rit;
                //定義反向迭代器rit
for (rit=myv.rbegin();rit!=myv.rend();++rit)    
                //從尾到頭遍歷全部元素   myv.rend()第一個元素的前面
    printf("%d ",*rit);    //輸出:3 2 1
printf("\n");

 

 

2.經常使用的STL容器

順序容器

適配器容器

關聯容器

 

2.1順序容器

2.1.1 vector(向量容器)

1.定義

   它是一個向量類模板。向量容器至關於數組

   用於存儲具備相同數據類型的一組元素,能夠從末尾快速的插入與刪除元素,快速地隨機訪問元素,可是在序列中間插入、刪除元素較慢,由於須要移動插入或刪除處後面的全部元素。

 

定義vector容器的幾種方式以下:

vector<int> v1;        //定義元素爲int的向量v1   至關於動態數組
vector<int> v2(10);        //指定向量v2的初始大小爲10個int元素
vector<double> v3(101.23);    //指定v3的10個初始元素的初值爲1.23
vector<int> v4(a,a+5);    //用數組a[0..4]共5個元素初始化v4

 

2.成員函數

  • vector提供了一系列的成員函數,vector主要的成員函數以下:
  • empty():判斷當前向量容器是否爲空。
  • size():返回當前向量容器的中的實際元素個數。
  • []:返回指定下標的元素。
  • reserve(n):爲當前向量容器預分配n個元素的存儲空間。
  • capacity():返回當前向量容器在從新進行內存分配之前所能容納的元素個數。
  • resize(n) :調整當前向量容器的大小,使其能容納n個元素。
  • push_back():在當前向量容器尾部添加了一個元素。
  • insert(pos,elem):在pos位置插入元素elem,即將元素elem插入到迭代器pos指定元素以前。
  • front():獲取當前向量容器的第一個元素。
  • back():獲取當前向量容器的最後一個元素。
  • erase():刪除當前向量容器中某個迭代器或者迭代器區間指定的元素。
  • clear():刪除當前向量容器中全部元素。
  • 迭代器函數:
    • begin():返回指向容器第一個元素的迭代器
    • end():返回指向容器最後元素(後面)的迭代器  
    • rbegin():倒數
    • rend():倒數

調用方法:變量點函數  

 

#include <vector>
using namespace std;
void main()
{   vector<int> myv;        //定義vector容器myv
    vector<int>::iterator it;    //定義myv的正向迭代器it
    myv.push_back(1);        //在myv末尾添加元素1
    it=myv.begin();        //it迭代器指向開頭元素1
    myv.insert(it,2);        //在it指向的元素以前插入元素2
    myv.push_back(3);        //在myv末尾添加元素3
    myv.push_back(4);        //在myv末尾添加元素4
    it=myv.end();        //it迭代器指向尾元素4的後面
    it--;            //it迭代器指向尾元素4
    myv.erase(it);        //刪除元素4
    for (it=myv.begin();it!=myv.end();++it)
    printf("%d ",*it);
    printf("\n");
}
//結果213

 

 

 

2.1.2string(字符串容器)

1.定義

    string是一個保存字符序列的容器,全部元素爲字符類型,相似vector<char>。

    除了有字符串的一些經常使用操做之外,還有包含了全部的序列容器的操做。字符串的經常使用操做包括增長、刪除、修改、查找比較、鏈接、輸入、輸出等。

 

建立string容器的幾種方式以下:

char cstr[]="China! Greate Wall";    //C-字符串
string s1(cstr);            // s1:China! Greate Wall
string s2(s1);                // s2:China! Greate Wall
string s3(cstr,711);        // s3:Greate Wall
string s4(cstr,6);            // s4:China!
string s5(5'A');            // s5:AAAAA

 

 

2.經常使用的成員函數以下

  • empty():判斷當前字符串是否爲空串。
  • size():返回當前字符串的實際字符個數(返回結果爲size_type類型)。
  • length():返回當前字符串的實際字符個數。
  • [idx]:返回當前字符串位於idx位置的字符,idx從0開始。
  • at(idx):返回當前字符串位於idx位置的字符。
  • compare(const string& str):返回當前字符串與字符串str的比較結果。在比較時,若二者相等,返回0;前者小於後者,返回-1;不然返回1。
  • append(cstr):在當前字符串的末尾添加一個字符串str。
  • insert(size_type idx,const string& str) :在當前字符串的idx處插入一個字符串str。
  • empty():判斷當前字符串是否爲空串。
  • size():返回當前字符串的實際字符個數(返回結果爲size_type類型)。
  • length():返回當前字符串的實際字符個數。
  • [idx]:返回當前字符串位於idx位置的字符,idx從0開始。
  • at(idx):返回當前字符串位於idx位置的字符。
  • compare(const string& str):返回當前字符串與字符串str的比較結果。在比較時,若二者相等,返回0;前者小於後者,返回-1;不然返回1。
  • append(cstr):在當前字符串的末尾添加一個字符串str。
  • insert(size_type idx,const string& str) :在當前字符串的idx處插入一個字符串str。
  • 迭代器函數:begin()、end()、rbegin()最後一個元素後面、rend()第一個函數前面

 

#include <iostream>
#include <string> 
using namespace std;
void main() 
{   string s1="",s2,s3="Bye";
    s1.append("Good morning");    //s1=" Good morning"
    s2=s1;                //s1=" Good morning"
    int i=s2.find("morning");        //i=5 查找i位置
    s2.replace(i,s2.length()-i,s3);    //至關於s2.replace(5,7,s3)
    cout << "s1: " << s1 << endl;  //s1=" Good morning"
    cout << "s2: " << s2 << endl;  //s1=" Good Bye"
}

 

求解模板生成工具問題。成成最近在搭建一個網站,其中一些頁面的部份內容來自數據庫中不一樣的數據記錄,可是頁面的基本結構是相同的。例如,對於展現用戶信息的頁面,當用戶爲Tom時,網頁的源代碼以下:

而當用戶爲Jerry時,網頁的源代碼以下:

 

這樣的例子在包含動態內容的網站中還有不少。爲了簡化生成網頁的工做,成成以爲他須要引入一套模板生成系統。模板是包含特殊標記的文本。成成用到的模板只包含一種特殊標記,格式爲{{ VAR }},其中VAR是一個變量。該標記在模板生成時會被變量VAR的值所替代。例如,若是變量name = "Tom",則{{ name }}會生成Tom。具體的規則以下:

變量名由大小寫字母、數字和下劃線(_)構成,且第一個字符不是數字,長度不超過16個字符。
變量名是大小寫敏感的,Name和name是兩個不一樣的變量。
變量的值是字符串。
若是標記中的變量沒有定義,則生成空串,至關於把標記從模板中刪除。
模板不遞歸生成。也就是說,若是變量的值中包含形如{{ VAR }}的內容,再也不作進一步的替換。

 

輸入格式:輸入的第一行包含兩個整數m和n,分別表示模板的行數和模板生成時給出的變量個數。接下來m行,每行是一個字符串,表示模板。接下來n行,每行表示一個變量和它的值,中間用一個空格分隔。值是字符串,用雙引號(")括起來,內容可包含除雙引號之外的任意可打印 ASCII 字符(ASCII碼範圍32, 33, 35~126)。
輸出格式:輸出包含若干行,表示模板生成的結果。

 

樣例輸入:

11 2
<!DOCTYPE html>
<html>
<head>
<title>User {{ name }}</title>
</head>
<body>
<h1>{{ name }}</h1>
<p>Email: <a
href="mailto:{{ email }}">{{ email }}</a></p>
<p>Address: {{ address }}</p>
</body>
</html>
name "David Beckham"
email "david@beckham.com"

 

 

樣例輸出:

<!DOCTYPE html>
<html>
<head>
<title>User David Beckham</title>
</head>
<body>
<h1>David Beckham</h1>
<p>Email: <a
href="mailto:david@beckham.com">david@beckham.com</a
></p>
<p>Address: </p>
</body>
</html>

 

評測用例規模與約定:

0≤m≤100,0≤n≤100
輸入的模板每行長度不超過80個字符(不包含換行符)。
輸入保證模板中全部以 {{ 開始的子串都是合法的標記,開始是兩個左大括號和一個空格,而後是變量名,結尾是一個空格和兩個右大括號。
輸入中全部變量的值字符串長度不超過 100 個字符(不包括雙引號)。
保證輸入的全部變量的名字各不相同。

解:採用vector<string>向量content存放網頁,每一行做爲一個元素。用map<string,string>容器存放轉換字符串。例如,對於題目中的樣例,content向量中下標爲0到10的元素以下:

<!DOCTYPE html>
<html>
<head>
<title>User {{ name }}</title>
</head>
<body>
<h1>{{ name }}</h1>
<p>Email: <a
href="mailto:{{ email }}">{{ email }}</a></p>
<p>Address: {{ address }}</p>
</body>

 

mymap映射容器中包含以下兩個結點(注意雙引號是值的一部分):

mymap[email]= "david@beckham.com"
mymap[name]= "David Beckham"

而後掃描content的每一個元素,查找形如「{{ var }}」的字符串,將{{ var }}用mymap[var]替換(注意替換部分不包含雙引號),在一個元素中能夠有多個須要替換的內容。例如,將:

href="mailto:{{ email }}">{{ email }}</a></p>
替換爲:
href="mailto:david@beckham.com">david@beckham.com</a></p>

字符串查找、替換均採用string的成員函數完成。對應的程序以下:

#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;
vector<string> content;        //存放網頁
map<stringstring> mymap;        //存放轉換字符串
int m,n;
void trans()            //網頁轉換
{   for(int i=0;i<m;i++)        //轉換content向量中的全部行
    {    int pos=0,pos1,pos2;
    do
    {   pos1=content[i].find("{{",pos);  
                      //從pos位置開始查找第一個{{
        pos2=content[i].find("}}",pos1);
                 //從pos1位置開始查找第一個}}
        if(pos1>=0 && pos2>=0)        //找到{{ }}
        {    string var=content[i].substr(pos1+3,pos2-pos1-4);
        if(mymap.count(var))        //提取形如{{var}}
        {   string result=mymap[var].substr(2,
                    mymap[var].length()-3);
            content[i].replace(pos1,var.length()+6,result); //替換
        }
        else
             content[i].replace(pos1,var.length()+6"");
        pos=pos1+var.length();
        }
        else            //沒有找到{{ }},pos指向當前字符串末尾
        pos = content[i].length();
    } while(pos<content[i].length());
     }
}
int main()
{   int i; string line;
    cin >> m >> n;
    cin.ignore();        //屏蔽回車鍵
    for(i=0;i<m;i++)        //輸入m行存放在content向量中
    {    getline(cin,line);
    content.push_back(line);
    }
    for(i=0;i<n;i++)        //輸入n行按空格分爲兩個部分
    {    getline(cin,line);
    int pos = line.find(" ");    
    mymap.insert(map<stringstring>::value_type(
        line.substr(0,pos),line.substr(pos)));
    }
    trans();
    for (i=0;i<m;i++)        //輸出網頁轉換結果
    cout << content[i] << endl;
    return 0;
}

 

 

 

2.1.3deque(雙端隊列容器)

1.定義

它是一個雙端隊列類模板。雙端隊列容器由若干個塊構成,每一個塊中元素地址是連續的,塊之間的地址是不連續的,有一個特定的機制將這些塊構成一個總體。能夠從前面或後面快速插入與刪除元素,並能夠快速地隨機訪問元素,但刪除元素較慢。

 

 

 

定義deque雙端隊列容器的幾種方式以下:

deque<int> dq1;    //定義元素爲int的雙端隊列dq1
deque<int> dq2(10);    //指定dq2的初始大小爲10個int元素
deque<double> dq3(101.23);
            //指定dq3的10個初始元素的初值爲1.23
deque<int> dq4(dq2.begin(),dq2.end());    
            //用dq2的全部元素初始化dq4

 

 

2.deque主要的成員函數以下

  • empty():判斷雙端隊列容器是否爲空隊。
  • size():返回雙端隊列容器中元素個數。
  • push_front(elem):在隊頭插入元素elem。
  • push_back(elem):在隊尾插入元素elem。
  • pop_front():刪除隊頭一個元素。
  • pop_back():刪除隊尾一個元素。
  • erase():從雙端隊列容器中刪除一個或幾個元素。
  • clear():刪除雙端隊列容器中全部元素。
  • 迭代器函數:begin()、end()、rbegin()、rend()。

 

#include <deque>
using namespace std;
void disp(deque<int> &dq)        //輸出dq的全部元素
{  deque<int>::iterator iter;        //定義迭代器iter
   for (iter=dq.begin();iter!=dq.end();iter++)
    printf("%d ",*iter);
   printf("\n");
}
void main()
{  deque<int> dq;            //創建一個雙端隊列dq
   dq.push_front(1);            //隊頭插入1
   dq.push_back(2);            //隊尾插入2
   dq.push_front(3);            //隊頭插入3
   dq.push_back(4);            //隊尾插入4
   printf("dq: "); disp(dq);
   dq.pop_front();            //刪除隊頭元素
   dq.pop_back();            //刪除隊尾元素
   printf("dq: "); disp(dq);
}

//3124
//12

 

 

2.1.4list(鏈表容器)

1.定義

它是一個雙鏈表類模板。能夠從任何地方快速插入與刪除。它的每一個結點之間經過指針連接,不能隨機訪問元素。

 

 

 

定義list容器的幾種方式以下:

list<int> l1;            //定義元素爲int的鏈表l1
list<int> l2 (10);        //指定鏈表l2的初始大小爲10個int元素
list<double> l3 (101.23);    //指定l3的10個初始元素的初值爲1.23
list<int> l4(a,a+5);        //用數組a[0..4]共5個元素初始化l4

 

 

2.list的主要成員函數以下

  • empty():判斷鏈表容器是否爲空。
  • size():返回鏈表容器中實際元素個數。
  • push_back():在鏈表尾部插入元素。
  • pop_back():刪除鏈表容器的最後一個元素。
  • remove ():刪除鏈表容器中全部指定值的元素。
  • remove_if(cmp):刪除鏈表容器中知足條件的元素。
  • erase():從鏈表容器中刪除一個或幾個元素。
  • unique():刪除鏈表容器中相鄰的重複元素。
  • clear():刪除鏈表容器中全部的元素。
  • insert(pos,elem):在pos位置插入元素elem,即將元素elem插入到迭代器pos指定元素以前。
  • insert(pos,n,elem):在pos位置插入n個元素elem。
  • insert(pos,pos1,pos2):在迭代器pos處插入[pos1,pos2)的元素。
  • reverse():反轉鏈表。
  • sort():對鏈表容器中的元素排序。
  • 迭代器函數:begin()、end()、rbegin()、rend()。

 

說明:STL提供的sort()排序算法主要用於支持隨機訪問的容器,而list容器不支持隨機訪問,爲此,list容器提供了sort()採用函數用於元素排序。相似的還有unique()、reverse()、merge()等STL算法。

 

#include <list>
using namespace std;
void disp(list<int> &lst)        //輸出lst的全部元素
{  list<int>::iterator it;
   for (it=lst.begin();it!=lst.end();it++)
    printf("%d ",*it);
   printf("\n");
}
void main()
{  list<int> lst;            //定義list容器lst
   list<int>::iterator it,start,end;
   lst.push_back(5);            //添加5個整數5,2,4,1,3
   lst.push_back(2);  lst.push_back(4);
   lst.push_back(1);  lst.push_back(3);
   printf("初始lst: "); disp(lst);
   it=lst.begin();            //it指向首元素5
   start=++lst.begin();        //start指向第2個元素2
   end=--lst.end();            //end指向尾元素3
   lst.insert(it,start,end);
   printf("執行lst.insert(it,start,end)\n");
   printf("插入後lst: "); disp(lst);
}

 

 

 

 

2.2關聯容器

2.2.1set(集合容器)/ multiset(多重集容器)

1.定義

set和multiset都是集合類模板,其元素值稱爲關鍵字。set中元素的關鍵字是惟一的,multiset中元素的關鍵字能夠不惟一,並且默認狀況下會對元素按關鍵字自動進行升序排列。
查找速度比較快,同時支持集合的交、差和並等一些集合上的運算,若是須要集合中的元素容許重複那麼可使用multiset。

 

2.set/multiset的成員函數以下

  • empty():判斷容器是否爲空。
  • size():返回容器中實際元素個數。
  • insert():插入元素。
  • erase():從容器刪除一個或幾個元素。
  • clear():刪除全部元素。
  • count(k):返回容器中關鍵字k出現的次數。
  • find(k):若是容器中存在關鍵字爲k的元素,返回該元素的迭代器,不然返回end()值。
  • upper_bound():返回一個迭代器,指向關鍵字大於k的第一個元素。
  • lower_bound():返回一個迭代器,指向關鍵字不小於k的第一個元素。
  • 迭代器函數:begin()、end()、rbegin()、rend()。

 

#include <set>
using namespace std;
void main()
{  set<int> s;            //定義set容器s
   set<int>::iterator it;    //定義set容器迭代器it
   s.insert(1);
   s.insert(3);
   s.insert(2);
   s.insert(4);
   s.insert(2);
   printf(" s: ");
   for (it=s.begin();it!=s.end();it++)
    printf("%d ",*it);
   printf("\n");
//s:1 2 3 4
   multiset<int> ms;    //定義multiset容器ms
   multiset<int>::iterator mit;
            //定義multiset容器迭代器mit
   ms.insert(1);
   ms.insert(3);
   ms.insert(2);
   ms.insert(4);
   ms.insert(2);
   printf("ms: ");
   for (mit=ms.begin();mit!=ms.end();mit++)
    printf("%d ",*mit);
   printf("\n");
}
//ms:1 2 2 3 4

 

 

2.2.2map(映射容器)/ multimap(多重映射容器)

1.定義

相似於數組,關鍵字當成下標

map和multimap都是映射類模板。映射是實現關鍵字與值關係的存儲結構,可使用一個關鍵字key來訪問相應的數據值value。
在set/multiset中的key和value都是key類型,而key和value是一個pair類結構。
pair類結構的聲明形如:

struct pair
{ 
    T first;
    T second;
}

map/multimap利用pair的<運算符將全部元素即key-value對按key的升序排列,以紅黑樹的形式存儲,能夠根據key快速地找到與之對應的value(查找時間爲O(log2n))。
map中不容許關鍵字重複出現,支持[]運算符;而multimap中容許關鍵字重複出現,但不支持[]運算符。

 

2.map/multimap的主要成員函數以下

empty():判斷容器是否爲空。
size():返回容器中實際元素個數。
map[key]:返回關鍵字爲key的元素的引用,若是不存在這樣的關鍵字,則以key做爲關鍵字插入一個元素(不適合multimap)。
insert(elem):插入一個元素elem並返回該元素的位置。
clear():刪除全部元素。
find():在容器中查找元素。
count():容器中指定關鍵字的元素個數(map中只有1或者0)。
迭代器函數:begin()、end()、rbegin()、rend()。

 

在map中修改元素很是簡單,這是由於map容器已經對[]運算符進行了重載(對其含義進行從新定義)。例如:

map<char,int> mymap;    //定義map容器mymap,其元素類型爲pair<char,int>
mymap['a'] = 1;                //或者mymap.insert(pair<char,int>('a',1) );
//鍵字符型‘單引號’ 值int

得到map中一個值的最簡單方法以下:

int ans = mymap['a'];

只有當map中有這個關鍵字('a')時纔會成功,不然會自動插入一個元素,值爲初始化值。可使用find() 方法來發現一個關鍵字是否存在。

#include <map>
using namespace std;
void main()
{   map<char,int> mymap;    //定義map容器mymap
    mymap.insert(pair<char,int>('a',1));
                //插入方式1
    mymap.insert(map<char,int>::value_type('b',2));
                //插入方式2
    mymap['c']=3;                                
//插入方式3 map<char,int>::iterator it; for(it=mymap.begin();it!=mymap.end();it++) printf("[%c,%d] ",it->first,it->second); printf("\n"); } //[a,1] [b,2] [c,3]

 

 

2.3適配器容器

2.3.1stack(棧容器)

它是一個棧類模板,和數據結構中的棧同樣,具備後進先出的特色。棧容器默認的底層容器是deque。也能夠指定其餘底層容器。
例如,如下語句指定myst棧的底層容器爲vector:

stack<string,vector<string> > myst;    
                //第2個參數指定底層容器爲vector

 

 

stack容器主要的成員函數以下:

  • empty():判斷棧容器是否爲空。
  • size():返回棧容器中實際元素個數。
  • push(elem):元素elem進棧。
  • top():返回棧頂元素。
  • pop():元素出棧。

注意:stack容器沒有begin()/end()和rbegin()/rend()這樣的用於迭代器的成員函數。

 

#include <stack>
using namespace std;
void main()
{  stack<int> st;
   st.push(1); st.push(2); st.push(3);
   printf("棧頂元素: %d\n",st.top());
   printf("出棧順序: ");
   while (!st.empty())     //棧不空時出棧全部元素
   {    printf("%d ",st.top());
    st.pop() ;
   }
   printf("\n");
}

 

 

2.3.2queue(隊列容器)

它是一個隊列類模板,和數據結構中的隊列同樣,具備先進先出的特色。不容許順序遍歷,沒有begin()/end()和rbegin()/rend()這樣的用於迭代器的成員函數。

 

主要的成員函數以下:

  • empty():判斷隊列容器是否爲空。
  • size():返回隊列容器中實際元素個數。
  • front():返回隊頭元素。
  • back():返回隊尾元素。
  • push(elem):元素elem進隊。
  • pop():元素出隊。

 

#include <queue>
using namespace std;
void main()
{  queue<int> qu;
   qu.push(1); qu.push(2); qu.push(3);
   printf("隊頭元素: %d\n",qu.front());
   printf("隊尾元素: %d\n",qu.back());
   printf("出隊順序: ");
   while (!qu.empty())        //出隊全部元素
   {    printf("%d ",qu.front());
    qu.pop();
   }
   printf("\n");
}

 

 

2.3.3priority_queue(優先隊列容器)

它是一個優先隊列類模板。優先隊列是一種具備受限訪問操做的存儲結構,元素能夠以任意順序進入優先隊列。
一旦元素在優先隊列容器中,出隊操做將出隊列最高優先級元素。

 

主要的成員函數以下:

  • empty():判斷優先隊列容器是否爲空。
  • size():返回優先隊列容器中實際元素個數。
  • push(elem):元素elem進隊。
  • top():獲取隊頭元素。
  • pop():元素出隊。

 

#include <queue>
using namespace std;
void main()
{   priority_queue<int> qu;
    qu.push(3); qu.push(1); qu.push(2);
    printf("隊頭元素: %d\n",qu.top());
    printf("出隊順序: ");
    while (!qu.empty())        //出隊全部元素
    {    printf("%d ",qu.top());
    qu.pop();
    }
    printf("\n");
}

 

 

3.STL在算法設計中的應用

3.1存放主數據

算法設計重要步驟是設計數據的存儲結構,除非特別指定,程序員能夠採用STL中的容器存放主數據,選擇何種容器不只要考慮數據的類型,還有考慮數據的處理過程。
例如,字符串能夠採用string或者vector<char>來存儲,鏈表能夠採用list來存儲。

 

【例1.11】有一段英文由若干單詞組成,單詞之間用一個空格分隔。編寫程序提取其中的全部單詞。
解:這裏的主數據是一段英文,採用string字符串str存儲它,最後提取的單詞采用vector<string>容器words存儲。

#include <iostream>
#include <string>
#include <vector>
using namespace std;
void solve(string str,vector<string> &words) //產生全部單詞words
{  string w;
   int i=0;
   int j=str.find(" ");    //查找第一個空格
   while (j!=-1)        //找到單詞後循環
   {    w=str.substr(i,j-i);    //提取一個單詞,從i到j-i在str裏面取
    words.push_back(w);    //單詞添加到words中
    i=j+1;
    j=str.find(" ",i);    //查找下一個空格
   }
   if (i<str.length()-1)    //處理最後一個單詞
   {    
     w=str.substr(i); //提取最後一個單詞    words.push_back(w); //最後單詞添加到words中 } }

 

void main() 
{  string str="The following code computes the 
        intersection of two arrays";
   vector<string> words;
   solve(str,words);
   cout << "全部的單詞:" << endl;    //輸出結果
   vector<string>::iterator it;
   for (it=words.begin();it!=words.end();++it)
    cout << "  " << *it << endl;
}
全部的單詞:
   The
   following
   code
   computes
   the
   intersection
   of
   two
   arrays

 

 

3.2存放臨時數據

在算法設計中,有時須要存放一些臨時數據。一般的狀況是,若是後存入的元素先處理,可使用stack棧容器;
若是先存入的元素先處理,可使用queue隊列容器;若是元素處理順序按某個優先級進行,可使用priority_queue優先隊列容器。

 

【例1.12】設計一個算法,判斷一個含有()、[]、{}三種類型括號的表達式中全部括號是否匹配。
解:這裏的主數據是一個字符串表達式,採用string字符串str存儲它。在判斷括號是否匹配時須要用到一個棧(由於每一個右括號都是和前面最近的左括號匹配),採用stack<char>容器做爲棧。

#include <iostream>
#include <stack>
#include <string>
using namespace std;
bool solve(string str)        //判斷str中括號是否匹配
{  stack<char> st;
   int i=0;
   while (i<str.length())        //掃描str的全部字符
   {    if (str[i]=='(' || str[i]=='[' || str[i]=='{')
       st.push(str[i]);        //全部左括號進棧
    else if (str[i]==')')        //當前字符爲')'
    {   if (st.top()!='(')    //若棧頂不是匹配的'(',返回假
        return false;
        else            //若棧頂是匹配的'(',退棧
        st.pop();
    }
    else if (str[i]==']')        //當前字符爲']'
    {  if (st.top()!='[')        //若棧頂不是匹配的'[',返回假
        return false;
       else                //若棧頂是匹配的'[',退棧
        st.pop();
    }
    else if (str[i]=='}')        //當前字符爲'}'
    {   if (st.top()!='{')    //若棧頂不是匹配的'{',返回假
        return false;
        else            //若棧頂是匹配的'{',退棧
        st.pop();
    }
    i++;
    }
    if (st.empty())            //str處理完畢而且棧空返回真
    return true;
    else
    return false;            //不然返回假
}
void main() 
{  cout << "求解結果:" << endl;
   string str="(a+[b-c]+d)";
   cout << "  " << str << 
        (solve(str)?"中括號匹配":"中括號不匹配") << endl;
   str="(a+[b-c}+d)";
   cout << "  " << str << 
        (solve(str)?"中括號匹配":"中括號不匹配") << endl;
}
/*
(a+[b-c]+d) 中括號匹配
(a+[b-c}+d) 中括號不匹配
*/

 

 

3.3檢測數據元素的惟一性

可使用map容器或者哈希表容器檢測數據元素是否惟一或者存放累計個數。

 

【例1.13】設計一個算法判斷字符串str中每一個字符是否惟一。如,"abc"的每一個字符是惟一的,算法返回true,而"accb"的中字符'c'不是惟一的,算法返回false。


解:設計map<char,int>容器mymap,第一個份量key的類型爲char,第二個份量value的類型爲int,表示對應關鍵字出現的次數。
將字符串str中每一個字符做爲關鍵字插入到map容器中,插入後對應出現次數增1。若是某個字符的出現次數大於1,表示不惟一,返回false;若是全部字符惟一,返回true。

bool isUnique(string &str)    //檢測str中的全部字符是否惟一的
{  map<char,int> mymap;
   for (int i=0;i<str.length();i++)
   {    
     mymap[str[i]]
++;   if (mymap[str[i]]>1)   return false; } return true; }

 

 

求多少對相反數。有N個非零且各不相同的整數。請你編一個程序求出它們中有多少對相反數(a和-a爲一對相反數)。時間限制爲1.0s,內存限制:256.0MB。
輸入格式:第一行包含一個正整數 N(1≤N≤500)。第二行爲N個用單個空格隔開的非零整數,每一個數的絕對值不超過1000,保證這些整數各不相同。
輸出格式:只輸出一個整數,即這N個數中包含多少對相反數。
樣例輸入:
5
1 2 3 -1 -2
樣例輸出:
2

 

解:能夠直接採用暴力思路求解,但可能超時。
這裏使用STL的map容器mymap(其實用哈希表效率更高),對於輸入的負整數x,將(-x,1)插入。掃描全部輸入的正整數y,當mymap[y]存在時說明對應一個相反數對,ans增1。

#include <stdio.h>
#include <map>
using namespace std;
#define MAX 505
int main()
{   int ans=0;        //累計相反數對的個數
    int n,x,i;
    int a[MAX];
    map<intint> mymap;
    scanf("%d",&n);
    for (i=0;i<n;i++)
    {    scanf("%d",&x);
    a[i]=x;
    if (x<0)    //將負整數插入mymap
       mymap.insert(pair<intint>(-x,1));
    }
    for (i=0;i<n;i++)
    if (a[i]>0 && mymap[a[i]])
        ans++;
    printf("%d\n",ans);
}

 

 

3.4數據排序

對於list容器的元素排序可使用其成員函數sort(),對於數組或者vector等具備隨機訪問特性的容器,可使用STL算法sort()。
下面以STL算法sort()爲例討論。

 

 

1)內置數據類型的排序
對於內置數據類型的數據,sort()默認是以less<T>(小於關係函數)做爲關係函數實現遞增排序。
爲了實現遞減排序,須要調用<functional>頭文件中定義的greater類模板。
例如,如下程序使用greater<int>()實現vector<int>容器元素的遞減排序(其中sort(myv.begin(),myv.end(),less<int>())語句等同於sort(myv.begin(),myv.end()),實現默認的遞增排序):

#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>            //包含less、greater等
using namespace std;
void Disp(vector<int> &myv)        //輸出vector的元素
{  vector<int>::iterator it;
   for(it = myv.begin();it!=myv.end();it++)
    cout << *it << " ";
   cout << endl;
}
void main()
{  int a[]={2,1,5,4,3};
   int n=sizeof(a)/sizeof(a[0]);
   vector<int> myv(a,a+n);
   cout << "初始myv:  "; Disp(myv);    //輸出:2 1 5 4 3
   sort(myv.begin(),myv.end(),less<int>());
   cout << "遞增排序: "; Disp(myv);    //輸出:1 2 3 4 5
   sort(myv.begin(),myv.end(),greater<int>());
   cout << "遞減排序: "; Disp(myv);    //輸出:5 4 3 2 1
}

 

 

2)自定義數據類型的排序
對於自定義數據類型如結構體數據,一樣默認是less<T>(即小於關係函數)做爲關係函數,但須要重載該函數。另外還能夠本身定義關係函數()。在這些重載函數或者關係函數中指定數據的排序順序(按哪些結構體成員排序,是遞增仍是遞減)。
概括起來,實現排序時主要有兩種方式:

方式1:在聲明結構體類型中重載<運算符,以實現按指定成員的遞增或者遞減排序。如sort(myv.begin(),myv.end())調用默認<運算符對myv容器的全部元素實現排序。
方式2:本身定義關係函數(),以實現按指定成員的遞增或者遞減排序。如sort(myv.begin(),myv.end(),Cmp())調用Cmp的()運算符對myv容器的全部元素實現排序。

#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
struct Stud
{  int no;
   string name;
   Stud(int no1,string name1)    //構造函數
   {    no=no1;
    name=name1;
   }
   bool operator<(const Stud &s) const    //方式1:重載<運算符
   {
    return s.no<no;   //用於按no遞減排序,將<改成>則按no遞增排序
   }
};
struct Cmp            //方式2:定義關係函數()
{  bool operator()(const Stud &s,const Stud &t) const
   {
    return s.name<t.name; 
        //用於按name遞增排序,將<改成>則按name遞減排序
   }
};
void Disp(vector<Stud> &myv)    //輸出vector的元素
{   vector<Stud>::iterator it;
    for(it = myv.begin();it!=myv.end();it++)
    cout << it->no << "," << it->name << "\t";
    cout << endl;
}
void main()
{  Stud a[]={Stud(2,"Mary"),Stud(1,"John"),Stud(5,"Smith")};
   int n=sizeof(a)/sizeof(a[0]);
   vector<Stud> myv(a,a+n);
   cout << "初始myv:    "; Disp(myv);  
            //輸出:2,Mary   1,John  5,Smith
   sort(myv.begin(),myv.end());      //默認使用<運算符排序
   cout << "按no遞減排序:   "; Disp(myv);    
            //輸出:5,Smith  2,Mary  1,John
   sort(myv.begin(),myv.end(),Cmp());  //使用Cmp中的()運算符進行排序
   cout << "按name遞增排序: "; Disp(myv);
            //輸出:1,John   2,Mary  5,Smith
}

 

 

3.5優先隊列做爲堆

在有些算法設計中用到堆,堆採用STL的優先隊列來實現,優先級的高低由隊列中數據元素的關係函數(比較運算符)肯定,不少狀況下須要重載關係函數。

1)元素爲內置數據類型的堆
對於C/C++內置數據類型,默認是less<T>(小於關係函數)做爲關係函數,值越大優先級的越高(即大根堆),能夠改成以greater<T>做爲關係函數,這樣值越大優先級的越低(即小根堆)。
例如,如下程序中pq1爲大根堆(默認),pq2爲小根堆(經過greater<int>實現):

#include <iostream>
#include <queue>
using namespace std;
void main()
{  int a[]={3,6,1,5,4,2};
   int n=sizeof(a)/sizeof(a[0]);
   //(1)優先級隊列pq1默認是使用vector做容器
   priority_queue<int> pq1(a,a+n);
   cout << "pq1: ";
   while (!pq1.empty())
   {    cout << pq1.top() << " ";    //while循環輸出:6 5 4 3 2 1
    pq1.pop();
   }
   cout << endl;
   //(2)優先級隊列pq2使用vector做容器,int元素的關係函數改成greater
   priority_queue<int,vector<int>,greater<int> > pq2(a,a+n);
   cout << "pq2: ";
   while (!pq2.empty())
   {    cout << pq2.top() << " ";    //while循環輸出:1 2 3 4 5 6
    pq2.pop();
   }
   cout << endl;
}

 

 

2)元素爲自定義類型的堆
對於自定義數據類型如結構體數據,一樣默認是less<T>(即小於關係函數)做爲關係函數,但須要重載該函數。
另外還能夠本身定義關係函數()。在這些重載函數或者關係函數中指定數據的優先級(優先級取決於哪些結構體,是越大越優先仍是越小越優先)。

#include <iostream>
#include <queue>
#include <string>
using namespace std;
struct Stud                    //聲明結構體Stud
{  int no;
   string name;
   Stud(int n,string na)            //構造函數
   {    no=n;
    name=na;
   }
   bool operator<(const Stud &s) const    //重載<關係函數
   {    return no<s.no;  }
   bool operator>(const Stud &s) const    //重載>關係函數
   {    return no>s.no;  }
};
//結構體的關係函數,改寫operator()
struct StudCmp
{   bool operator()(const Stud &s,const Stud &t) const
    {
    return s.name<t.name;        //name越大越優先
    }
};
void main()
{  Stud a[]={Stud(2,"Mary"),Stud(1,"John"),Stud(5,"Smith")};
   int n=sizeof(a)/sizeof(a[0]);
   //(1)使用Stud結構體的<關係函數定義pq1
   priority_queue<Stud> pq1(a,a+n);
   cout << "pq1出隊順序: ";
   while (!pq1.empty())        //按no遞減輸出
   {    cout << "[" << pq1.top().no << "," << 
            pq1.top().name << "]\t";
    pq1.pop();
   }
   cout << endl;
//pq1出隊順序: [5,Smith]   [2,Mary]    [1,John]
    //(2)使用Stud結構體的>關係函數定義pq2
    priority_queue<Stud,deque<Stud>,greater<Stud> > pq2(a,a+n);
    cout << "pq2出隊順序: ";
    while (!pq2.empty())            //按no遞增輸出
    {    cout << "[" << pq2.top().no << "," << 
            pq2.top().name << "]\t";
    pq2.pop();
    }
    cout << endl;
//pq2出隊順序: [1,John]    [2,Mary]    [5,Smith]

    //(3)使用結構體StudCmp的關係函數定義pq3
   priority_queue<Stud,deque<Stud>,StudCmp > pq3(a,a+n);
   cout << "pq3出隊順序: ";
   while (!pq3.empty())        //按name遞減輸出
   {    cout << "[" << pq3.top().no << "," 
            << pq3.top().name << "]\t";
    pq3.pop();
   }
   cout << endl;
}
//pq3出隊順序: [5,Smith]   [2,Mary]    [1,John]

 

 

 

相關文章
相關標籤/搜索