今天,看一段代碼的時候發現只一句話就做了個排序,是這樣的:
sort(rotateArray.begin(),rotateArray.end());
很震驚,後來查了一下sort的用法,
sort函數的用法
自己寫個冒泡之類的O(n^2)排序,不但程序容易超時,還很有可能寫錯。
STL裡面有個sort函數,可以直接對數組排序,復雜度為n*log2(n)。使用這個函數,需要包含頭文件。
這個函數可以傳兩個參數或三個參數。第一個參數是要排序的區間首地址,第二個參數是區間尾地址的下一地址。
也就是說,排序的區間是[a,b)。簡單來說,有一個數組int a[100],要對從a[0]到a[99]的元素進行排序,只要寫sort(a,a+100)就行了,默認的排序方式是升序。
拿我出的“AC的策略”這題來說,需要對數組t的第0到len-1的元素排序,就寫sort(t,t+len);
對向量v排序也差不多,sort(v.begin(),v.end());//這正是我之前看到的用法
排序的數據類型不局限於整數,只要是定義了小於運算的類型都可以,比如字符串類string。
如果是沒有定義小於運算的數據類型,或者想改變排序的順序,就要用到第三參數——比較函數。比較函數是一個自己定義的函數,返回值是bool型,它規定了什麼樣的關系才是“小於”。想把剛才的整數數組按降序排列,可以先定義一個比較函數cmp
bool cmp(int a,int b)
{
return a>b;
}
排序的時候就寫sort(a,a+100,cmp);
假設自己定義了一個結構體node
struct node{
int a;
int b;
double c;
}
有一個node類型的數組node arr[100],想對它進行排序:先按a值升序排列,如果a值相同,再按b值降序排列,如果b還相同,就按c降序排列。
就可以寫這樣一個比較函數:
以下是代碼片段:
bool cmp(node x,node y) { if(x.a!=y.a) return x.a if(x.b!=y.b) return x.b>y.b; return return x.c>y.c; }
排序時寫sort(arr,a+100,cmp);
qsort(s[0],n,sizeof(s[0]),cmp);
int cmp(const void *a,const void *b)
{
return *(int *)a-*(int *)b;
}
一、對int類型數組排序
int num[100];
Sample:
int cmp ( const void *a , const void *b )
{
return *(int *)a - *(int *)b;
}
qsort(num,100,sizeof(num[0]),cmp);
二、對char類型數組排序(同int類型)
char word[100];
Sample:
int cmp( const void *a , const void *b )
{
return *(char *)a - *(int *)b;
}
qsort(word,100,sizeof(word[0]),cmp);
三、對double類型數組排序(特別要注意)
double in[100];
int cmp( const void *a , const void *b )
{
return *(double *)a > *(double *)b ? 1 : -1;
}
qsort(in,100,sizeof(in[0]),cmp);
四、對結構體一級排序
struct In
{
double data;
int other;
}s[100]
//按照data的值從小到大將結構體排序,關於結構體內的排序關鍵數據data的類型可以很多種,參考上面的例子寫
int cmp( const void *a ,const void *b)
{
return ((In *)a)->data - ((In *)b)->data ;
}
qsort(s,100,sizeof(s[0]),cmp);
五、對結構體
struct In
{
int x;
int y;
}s[100];
//按照x從小到大排序,當x相等時按照y從大到小排序
int cmp( const void *a , const void *b )
{
struct In *c = (In *)a;
struct In *d = (In *)b;
if(c->x != d->x) return c->x - d->x;
else return d->y - c->y;
}
qsort(s,100,sizeof(s[0]),cmp);
六、對字符串進行排序
struct In
{
int data;
char str[100];
}s[100];
//按照結構體中字符串str的字典順序排序
int cmp ( const void *a , const void *b )
{
return strcmp( ((In *)a)->str , ((In *)b)->str );
}
qsort(s,100,sizeof(s[0]),cmp);
七、計算幾何中求凸包的cmp
int cmp(const void *a,const void *b) //重點cmp函數,把除了1點外的所有點,旋轉角度排序
{
struct point *c=(point *)a;
struct point *d=(point *)b;
if( calc(*c,*d,p[1]) < 0) return 1;
else if( !calc(*c,*d,p[1]) && dis(c->x,c->y,p[1].x,p[1].y) < dis(d->x,d->y,p[1].x,p[1].y)) //如果在一條直線上,則把遠的放在前面
return 1;
else return -1;
}
好了,說了這麼多sort的用法,有沒有人對什麼是STL還迷茫的呢?
下面說說什麼是STL(內容來源網絡)
一、一般介紹
STL(Standard Template Library),即標准模板庫,是一個具有工業強度的,高效的C++程序庫。它被容納於C++標准程序庫(C++ Standard Library)中,是ANSI/ISO C++標准中最新的也是極具革命性的一部分。該庫包含了諸多在計算機科學領域裡所常用的基本數據結構和基本算法。為廣大C++程序員們提供了一個可擴展的應用框架,高度體現了軟件的可復用性。
從邏輯層次來看,在STL中體現了泛型化程序設計的思想(generic programming),引入了諸多新的名詞,比如像需求(requirements),概念(concept),模型(model),容器(container),算法(algorithmn),迭代子(iterator)等。與OOP(object-oriented programming)中的多態(polymorphism)一樣,泛型也是一種軟件的復用技術;
從實現層次看,整個STL是以一種類型參數化(type parameterized)的方式實現的,這種方式基於一個在早先C++標准中沒有出現的語言特性--模板(template)。如果查閱任何一個版本的STL源代碼,你就會發現,模板作為構成整個STL的基石是一件千真萬確的事情。除此之外,還有許多C++的新特性為STL的實現提供了方便;
二、STL的六大組件
以下主要討論:容器,迭代器,算法,適配器。如欲詳加了解 參見C++ Primer
1.STL容器
1)序列式容器(Sequence containers),每個元素都有固定位置--取決於插入時機和地點,和元素值無關,vector、deque、list;
Vectors:將元素置於一個動態數組中加以管理,可以隨機存取元素(用索引直接存取),數組尾部添加或移除元素非常快速。但是在中部或頭部安插元素比較費時;
Deques:是“double-ended queue”的縮寫,可以隨機存取元素(用索引直接存取),數組頭部和尾部添加或移除元素都非常快速。但是在中部或頭部安插元素比較費時;
Lists:雙向鏈表,不提供隨機存取(按順序走到需存取的元素,O(n)),在任何位置上執行插入或刪除動作都非常迅速,內部只需調整一下指針;
2)關聯式容器(Associated containers),元素位置取決於特定的排序准則,和插入順序無關,set、multiset、map、multimap;
Sets/Multisets:內部的元素依據其值自動排序,Set內的相同數值的元素只能出現一次��Multisets內可包含多個數值相同的元素,內部由二叉樹實現(實際上基於紅黑樹(RB-tree)實現),便於查找;
Maps/Multimaps:Map的元素是成對的鍵值/實值,內部的元素依據其值自動排序,Map內的相同數值的元素只能出現一次,Multimaps內可包含多個數值相同的元素,內部由二叉樹實現(實際上基於紅黑樹(RB-tree)實現),便於查找;
另外有其他容器hash_map,hash_set,hash_multiset,hash_multimap。
容器的比較:
Vector Deque List Set MultiSet Map MultiMap 內部結構 dynamic array array of arrays double linked list binary tree binary tree binary tree binary tree 隨機存取 Yes Yes No No No Yes(key) No 搜索速度 慢 慢 很慢 快 快 快 快 快速插入移除 尾部 首尾 任何位置 -- -- -- --
2.STL迭代器
Iterator(迭代器)模式又稱Cursor(游標)模式,用於提供一種方法順序訪問一個聚合對象中各個元素,
而又不需暴露該對象的內部表示。或者這樣說可能更容易理解:Iterator模式是運用於聚合對象的一種模式,通過運用該模式,使得我們可以在不知道對象內部表示的情況下,按照一定順序(由iterator提供的方法)訪問聚合對象中的各個元素。
迭代器的作用:能夠讓迭代器與算法不干擾的相互發展,最後又能無間隙的粘合起來,重載了*,++,==,!=,=運算符。用以操作復雜的數據結構,容器提供迭代器,算法使用迭代器;
常見的一些迭代器類型:iterator、const_iterator、reverse_iterator和const_reverse_iterator
迭代器一般聲明使用示例
vector<T>::iterator it; list<T>::iterator it; deque<T>::iterator it;
input output
\ /
forward
|
bidirectional
|
random access
要注意,上面這圖表並不是表明它們之間的繼承關系:而只是描述了迭代器的種類和接口。處於圖表下層的迭代器都是相對於處於圖表上層迭代器的擴張集。例如:forward迭代器不但擁有input和output迭代器的所有功能,還擁有更多的功能。
各個迭代器的功能如下:
迭代器類別
說明
輸入
從容器中讀取元素。輸入迭代器只能一次讀入一個元素向前移動,輸入迭代器只支持一遍算法,同一個輸入迭代器不能兩遍遍歷一個序列
輸出
向容器中寫入元素。輸出迭代器只能一次一個元素向前移動。輸出迭代器只支持一遍算法,統一輸出迭代器不能兩次遍歷一個序列
正向
組合輸入迭代器和輸出迭代器的功能,並保留在容器中的位置
雙向
組合正向迭代器和逆向迭代器的功能,支持多遍算法
隨機訪問
組合雙向迭代器的功能與直接訪問容器中任何元素的功能,即可向前向後跳過任意個元素
迭代器的操作:
每種迭代器均可進行包括表中前一種迭代器可進行的操作。
迭代器操作
說明
所有迭代器
p++
後置自增迭代器
++p
前置自增迭代器
輸入迭代器
*p
復引用迭代器,作為右值
p=p1
將一個迭代器賦給另一個迭代器
p==p1
比較迭代器的相等性
p!=p1
比較迭代器的不等性
輸出迭代器
*p
復引用迭代器,作為左值
p=p1
將一個迭代器賦給另一個迭代器
正向迭代器
提供輸入輸出迭代器的所有功能
雙向迭代器
--p
前置自減迭代器
p--
後置自減迭代器
隨機迭代器
p+=i
將迭代器遞增i位
p-=i
將迭代器遞減i位
p+i
在p位加i位後的迭代器
p-i
在p位減i位後的迭代器
p[i]
返回p位元素偏離i位的元素引用
p<p1
如果迭代器p的位置在p1前,返回true,否則返回false
p<=p1
p的位置在p1的前面或同一位置時返回true,否則返回false
p>p1
如果迭代器p的位置在p1後,返回true,否則返回false
p>=p1
p的位置在p1的後面或同一位置時返回true,否則返回false
只有順序容器和關聯容器支持迭代器遍歷,各容器支持的迭代器的類別如下:
容器
支持的迭代器類別
說明
vector
隨機訪問
一種隨機訪問的數組類型,提供了對數組元素進行快速隨機訪問以及在序列尾部進行快速的插入和刪除操作的功能。可以再需要的時候修改其自身的大小
deque
隨機訪問
一種隨機訪問的數組類型,提供了序列兩端快速進行插入和刪除操作的功能。可以再需要的時候修改其自身的大小
list
雙向
一種不支持隨機訪問的數組類型,插入和刪除所花費的時間是固定的,與位置無關。
set
雙向
一種隨機存取的容器,其關鍵字和數據元素是同一個值。所有元素都必須具有惟一值。
multiset
雙向
一種隨機存取的容器,其關鍵字和數據元素是同一個值。可以包含重復的元素。
map
雙向
一種包含成對數值的容器,一個值是實際數據值,另一個是用來尋找數據的關鍵字。一個特定的關鍵字只能與一個元素關聯。
multimap
雙向
一種包含成對數值的容器,一個值是實際數據值,另一個是用來尋找數據的關鍵字。一個關鍵字可以與多個數據元素關聯。
stack
不支持
適配器容器類型,用vector,deque或list對象創建了一個先進後出容器
queue
不支持
適配器容器類型,用deque或list對象創建了一個先進先出容器
priority_queue
不支持
適配器容器類型,用vector或deque對象創建了一個排序隊列
3.STL算法
STL算法部分主要由頭文件<algorithm>,<numeric>,<functional>組成。要使用 STL中的算法函數必須包含頭文件<algorithm>,對於數值算法須包含<numeric>,<functional>中則定義了一些模板類,用來聲明函數對象。
STL中算法大致分為四類:
1)、非可變序列算法:指不直接修改其所操作的容器內容的算法。
2)、可變序列算法:指可以修改它們所操作的容器內容的算法。
3)、排序算法:包括對序列進行排序和合並的算法、搜索算法以及有序序列上的集合操作。
4)、數值算法:對容器內容進行數值計算。
以下對所有算法進行細致分類並標明功能:
<一>查找算法(13個):判斷容器中是否包含某個值
adjacent_find: 在iterator對標識元素范圍內,查找一對相鄰重復元素,找到則返回指向這對元素的第一個元素的ForwardIterator。否則返回last。重載版本使用輸入的二元操作符代替相等的判斷。
binary_search: 在有序序列中查找value,找到返回true。重載的版本實用指定的比較函數對象或函數指針來判斷相等。
count: 利用等於操作符,把標志范圍內的元素與輸入值比較,返回相等元素個數。
count_if: 利用輸入的操作符,對標志范圍內的元素進行操作,返回結果為true的個數。
equal_range: 功能類似equal,返回一對iterator,第一個表示lower_bound,第二個表示upper_bound。
find: 利用底層元素的等於操作符,對指定范圍內的元素與輸入值進行比較。當匹配時,結束搜索,返回該元素的一個InputIterator。
find_end: 在指定范圍內查找"由輸入的另外一對iterator標志的第二個序列"的最後一次出現。找到則返回最後一對的第一個ForwardIterator,否則返回輸入的"另外一對"的第一個ForwardIterator。重載版本使用用戶輸入的操作符代替等於操作。
find_first_of: 在指定范圍內查找"由輸入的另外一對iterator標志的第二個序列"中任意一個元素的第一次出現。重載版本中使用了用戶自定義操作符。
find_if: 使用輸入的函數代替等於操作符執行find。
lower_bound: 返回一個ForwardIterator,指向在有序序列范圍內的可以插入指定值而不破壞容器���序的第一個位置。重載函數使用自定義比較操作。
upper_bound: 返回一個ForwardIterator,指向在有序序列范圍內插入value而不破壞容器順序的最後一個位置,該位置標志一個大於value的值。重載函數使用自定義比較操作。
search: 給出兩個范圍,返回一個ForwardIterator,查找成功指向第一個范圍內第一次出現子序列(第二個范圍)的位置,查找失敗指向last1。重載版本使用自定義的比較操作。
search_n: 在指定范圍內查找val出現n次的子序列。重載版本使用自定義的比較操作。
<二>排序和通用算法(14個):提供元素排序策略
inplace_merge: 合並兩個有序序列,結果序列覆蓋兩端范圍。重載版本使用輸入的操作進行排序。
merge: 合並兩個有序序列,存放到另一個序列。重載版本使用自定義的比較。
nth_element: 將范圍內的序列重新排序,使所有小於第n個元素的元素都出現在它前面,而大於它的都出現在後面。重載版本使用自定義的比較操作。
partial_sort: 對序列做部分排序,被排序元素個數正好可以被放到范圍內。重載版本使用自定義的比較操作。
partial_sort_copy: 與partial_sort類似,不過將經過排序的序列復制到另一個容器。
partition: 對指定范圍內元素重新排序,使用輸入的函數,把結果為true的元素放在結果為false的元素之前。
random_shuffle: 對指定范圍內的元素隨機調整次序。重載版本輸入一個隨機數產生操作。
reverse: 將指定范圍內元素重新反序排序。
reverse_copy: 與reverse類似,不過將結果寫入另一個容器。
rotate: 將指定范圍內元素移到容器末尾,由middle指向的元素成為容器第一個元素。
rotate_copy: 與rotate類似,不過將結果寫入另一個容器。
sort: 以升序重新排列指定范圍內的元素。重載版本使用自定義的比較操作。
stable_sort: 與sort類似,不過保留相等元素之間的順序關系。
stable_partition: 與partition類似,不過不保證保留容器中的相對順序。
<三>刪除和替換算法(15個)
copy: 復制序列
copy_backward: 與copy相同,不過元素是以相反順序被拷貝。
iter_swap: 交換兩個ForwardIterator的值。
remove: 刪除指定范圍內所有等於指定元素的元素。注意,該函數不是真正刪除函數。內置函數不適合使用remove和remove_if函數。
remove_copy: 將所有不匹配元素復制到一個制定容器,返回OutputIterator指向被拷貝的末元素的下一個位置。
remove_if: 刪除指定范圍內輸入操作結果為true的所有元素。
remove_copy_if: 將所有不匹配元素拷貝到一個指定容器。
replace: 將指定范圍內所有等於vold的元素都用vnew代替。
replace_copy: 與replace類似,不過將結果寫入另一個容器。
replace_if: 將指定范圍內所有操作結果為true的元素用新值代替。
replace_copy_if: 與replace_if,不過將結果寫入另一個容器。
swap: 交換存儲在兩個對象中的值。
swap_range: 將指定范圍內的元素與另一個序列元素值進行交換。
unique: 清除序列中重復元素,和remove類似,它也不能真正刪除元素。重載版本使用自定義比較操作。
unique_copy: 與unique類似,不過把結果輸出到另一個容器。
<四>排列組合算法(2個):提供計算給定集合按一定順序的所有可能排列組合
next_permutation: 取出當前范圍內的排列,並重新排序為下一個排列。重載版本使用自定義的比較操作。
prev_permutation: 取出指定范圍內的序列並將它重新排序為上一個序列。如果不存在上一個序列則返回false。重載版本使用自定義的比較操作。
<五>算術算法(4個)
accumulate: iterator對標識的序列段元素之和,加到一個由val指定的初始值上。重載版本不再做加法,而是傳進來的二元操作符被應用到元素上。
partial_sum: 創建一個新序列,其中每個元素值代表指定范圍內該位置前所有元素之和。重載版本使用自定義操作代替加法。
inner_product: 對兩個序列做內積(對應元素相乘,再求和)並將內積加到一個輸入的初始值上。重載版本使用用戶定義的操作。
adjacent_difference: 創建一個新序列,新序列中每個新值代表當前元素與上一個元素的差。重載版本用指定二元操作計算相鄰元素的差。
<六>生成和異變算法(6個)
fill: 將輸入值賦給標志范圍內的所有元素。
fill_n: 將輸入值賦給first到first+n范圍內的所有元素。
for_each: 用指定函數依次對指定范圍內所有元素進行迭代訪問,返回所指定的函數類型。該函數不得修改序列中的元素。
generate: 連續調用輸入的函數來填充指定的范圍。
generate_n: 與generate函數類似,填充從指定iterator開始的n個元素。
transform: 將輸入的操作作用與指定范圍內的每個元素,並產生一個新的序列。重載版本將操作作用在一對元素上,另外一個元素來自輸入的另外一個序列。結果輸出到指定容器。
<七>關系算法(8個)
equal: 如果兩個序列在標志范圍內元素都相等,返回true。重載版本使用輸入的操作符代替默認的等於操作符。
includes: 判斷第一個指定范圍內的所有元素是否都被第二個范圍包含,使用底層元素的<操作符,成功返回true。重載版本使用用戶輸入的函數。
lexicographical_compare: 比較兩個序列。重載版本使用用戶自定義比較操作。
max: 返回兩個元素中較大一個。重載版本使用自定義比較操作。
max_element: 返回一個ForwardIterator,指出序列中最大的元素。重載版本使用自定義比較操作。
min: 返回兩個元素中較小一個。重載版本使用自定義比較操作。
min_element: 返回一個ForwardIterator,指出序列中最小的元素。重載版本使用自定義比較操作。
mismatch: 並行比較兩個序列,指出第一個不匹配的位置,返回一對iterator,標志第一個不匹配元素位置。如果都匹配,返回每個容器的last。重載版本使用自定義的比較操作。
<八>集合算法(4個)
set_union: 構造一個有序序列,包含兩個序列中所有的不重復元素。重載版本使用自定義的比較操作。
set_intersection: 構造一個有序序列,其中元素在兩個序列中都存在。重載版本使用自定義的比較操作。
set_difference: 構造一個有序序列,該序列僅保留第一個序列中存在的而第二個中不存在的元素。重載版本使用自定義的比較操作。
set_symmetric_difference: 構造一個有序序列,該序列取兩個序列的對稱差集(並集-交集)。
<九>堆算法(4個)
make_heap: 把指定范圍內的元素生成一個堆。重載版本使用自定義比較操作。
pop_heap: 並不真正把最大元素從堆中彈出,而是重新排序堆。它把first和last-1交換,然後重新生成一個堆。可使用容器的back來訪問被"彈出"的元素或者使用pop_back進行真正的刪除。重載版本使用自定義的比較操作。
push_heap: 假設first到last-1是一個有效堆,要被加入到堆的元素存放在位置last-1,重新生成堆。在指向該函數前,必須先把元素插入容器後。重載版本使用指定的比較操作。
sort_heap: 對指定范圍內的序列重新排序,它假設該序列是個有序堆。重載版本使用自定義比較操作。
4.適配器
STL提供了三個容器適配器:queue、priority_queue、stack。這些適配器都是包裝了vector、list、deque中某個順序容器的包裝器。注意:適配器沒有提供迭代器,也不能同時插入或刪除多個元素。下面對各個適配器進行概括總結。
(1)stack用法
#include <stack> template < typename T, typename Container=deque > class stack;
可以使用三個標准順序容器vecotr、deque(默認)、list中的任何一個作為stack的底層模型。
bool stack<T>::empty() //判斷堆棧是否為空 void stack<T>::pop() //彈出棧頂數據 stack<T>::push(T x) //壓入一個數據 stack<T>::size_type stack<T>::size() //返回堆棧長度 T stack<T>::top() //得到棧頂數據
代碼示例:
stack<int> intDequeStack; stack<int,vector<int>> intVectorStack; stack<int,list<int>> intListStack;
(2)queue用法
#include <queue> template<typename T, typename Container = deque<T> > class queue;
第一個參數指定要在queue中存儲的類型,第二個參數規定queue適配的底層容器,可供選擇的容器只有dequeue和list。對大多數用途使用默認的dequeue。
queue<T>::push(T x) void queue<T>::pop() T queue<T>::back() T queue<T>::front() queue<T>::size_type queue<T>::size() bool queue<T>::empty()
代碼示例:
queue<int> intDequeQueue; queue<int,list<int>> intListQueue;
(3)priority_queue用法
#include <queue> template <typename T, typename Container = vector<T>, typename Compare = less<T> > class priority_queue;
priority_queue也是一個隊列,其元素按有序順序排列。其不采用嚴格的FIFO順序,給定時刻位於隊頭的元素正是有最高優先級的元素。如果兩個元素有相同的優先級,那麼它們在隊列中的順序就遵循FIFO語義。默認適配的底層容器是vector,也可以使用deque,list不能用,因為priority_queue要求能對元素隨機訪問以便進行排序。
priority_queue<T>::push(T x) void priority_queue<T>::pop() T priority_queue<T>::top() priority_queue<T>::size_type priority_queue<T>::size() bool priority_queue<T>::empty()
代碼示例:
priority_queue< int, vector<int>, greater<int> > priority_queue< int, list<int>, greater<int> >
標准庫默認使用元素類型的<操作符來確定它們之間的優先級關系,用法有三:
優先隊列第一種用法,通過默認使用的<操作符可知在整數中元素大的優先級高。
priority_queue<int> qi;
示例中輸出結果為:9 6 5 3 2
優先隊列第二種用法,建立priority_queue時傳入一個比較函數,使用functional.h函數對象作為比較函數。
priority_queue<int, vector<int>, greater<int> >qi2;
示例2中輸出結果為:2 3 5 6 9
優先隊列第三種用法,是自定義優先級。
struct node { friend bool operator< (node n1, node n2) { return n1.priority < n2.priority; } int priority; int value; }; priority_queue<node> qn;
在示例3中輸出結果為:
優先級 值
9 5
8 2
6 1
2 3
1 4
在該結構中,value為值,priority為優先級。通過自定義operator<操作符來比較元素中的優先級。注意:必須是自定義<操作符才行,把上述的結構中的<操作符改成>編譯不通過。