主要参考:《数据结构与算法/leetcode/lintcode题解》、C++参考手册、《疯狂Java》
参考C++参考手册,C++有一个容器库,其中包含了一些常见的数据结构。一共有三种容器:顺序容器、关联容器和无序关联容器。
顺序容器 | 关联容器 | 无序关联容器 | 容器适配器 |
---|---|---|---|
按顺序访问 | 快速查找 | 快速查找且无序 | 提供顺序容器的不同接口 |
array (c++11) | set | unordered_set(c++11起) | stack(LIFO)栈 |
vector | map | unordered_map(c++11起) | queue(FIFO)队列 |
deque | multiset | unordered_multiset(c++11起) | priority_queue(优先级) |
forward_list (c++11起) | multimap | unordered_multimap(c++11起) | |
list |
适配器是标准库中的一个通用概念,它是一中机制,使某种事物的行为看起来像另一种事物。一个容器适配器接受一种已有的容器类型,使其行为看起来像一种不同的类型。
适配器 | 要求 | 满足容器 |
---|---|---|
stack | 必须提供函数:back()、push_back()、pop_back() | vector、deque(默认)和list |
queue | back()、front()、push_back()、pop_front() | deque和list |
priority_queue | 迭代器必须满足遗留随机访问迭代器的要求,front()、push_back()、pop_back() | vector和deque |
顺序容器在C++参考手册上都有较详细的介绍,使用难度都不算大,所以不用特意做什么说明。
下面是所有的顺序容器的常用操作的示例
#include
#include
#include
#include
#include
#include
#include
#include
#include
class p {
private:
std::string name;
int id;
public:
p(std::string n, int i) :name(n), id(i){}
std::string getName() { return name; }
int getId() { return id; }
std::string toString() { std::string str = name;str.append(",id:"); str.append(std::to_string(id)); return str; }
};
int main() {
//----array----
std::cout << "array" << std::endl;
std::array<int, 3>a1{ {1,2,3} };
std::array<int, 3>a2 = { 1,2,3 };
std::array<std::string, 2>a3 = { "a","b" };
std::sort(a1.begin(), a1.end());
std::reverse_copy(a2.begin(), a2.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << "\n";
std::cout << a1.size() << std::endl;
std::array<int, 10>test;
test.fill(1);
a3.fill("t");
for (const auto& s : a3)
std::cout << s << " ";
//---vector---
std::cout << "\nvector" << std::endl;
//构造与复制
std::vector<int> v1{ {1,2,3} };
std::vector<int> v2(v1);
std::vector<char>v3 = { 'a','b','c' };
std::vector<int>v4(10, 0);//10个0
std::vector<int>v5(v4.begin(), v4.end());
std::vector<int>v6 = v5;
std::cout << v1.front() << "---" << v1.back() << std::endl;//访问头部和尾部元素
std::cout << v1[1] << std::endl;
std::vector<p>ps;
//添加元素与删除元素
ps.emplace_back("Tom", 1);//将参数传递给元素类型的构造函数
ps.push_back(p("Jerry", 2));
ps.emplace(ps.begin(), "Jackey", 3);//第一个参数为容器的迭代器
for (auto & c : ps)
std::cout << c.toString() << std::endl;
for (const auto& c : v2)
std::cout << c << " ";
std::cout << std::endl;
v2.pop_back();//删除尾部元素
for (const auto& c : v2)
std::cout << c << " ";
std::cout << std::endl;
v2.insert(v2.begin(), 3);
v2.insert(v2.begin(), 2,1);
v2.insert(v2.begin(), v1.begin(), v1.end());//插入元素
for (const auto& c : v2)
std::cout << c << " ";
std::cout << std::endl;
v2.erase(v2.begin()); //删除元素
v2.erase(v2.begin(), v2.begin() + 3);
for (const auto& c : v2)
std::cout << c << " ";
std::cout << std::endl;
v2.swap(v1); //交换容器
for (const auto& c : v1)
std::cout << c << " ";
std::cout << std::endl;
for (const auto& c : v2)
std::cout << c << " ";
std::cout << std::endl;
std::cout << v1.size() << "--" << v1.max_size() << "--" << v1.capacity() << std::endl;
//---deque---双端列表
std::cout << "deque" << std::endl;
//构造与复制
std::deque<int>d1{ 1,2,3 };
std::deque<int>d2 = { 1,2,3,4 };
std::deque<int>d3{ {1,2,3,4,5} };
std::deque<int>d4(d1);
std::deque<int>d5(10, 2);
std::deque<int>d6(d1.begin(), d1.begin());
std::deque<int>d7;
//添加与删除元素
d7.assign(10, 1);
for (int i : d7)
std::cout << i << " ";
std::cout << "/n" << d1.front() << " --" << d1.back() << std::endl;
std::cout << d7.size() << "---" << d7.max_size() << std::endl;
std::deque<p>d8;
d8.emplace(d8.begin(), std::string("Flandre"), 1);
d8.emplace_back("Remilia", 2);
d8.emplace_front("Sakuya", 3);
d8.push_back(p("Patchouli",4));//尾部添加
d8.push_front(p("Meirin", 5));//头部
for (auto c : d8)
std::cout << c.toString() << std::endl;
d2.insert(d2.begin(), 11);
d2.insert(d2.begin(), d1.begin(), d1.end());
d2.insert(d2.begin(), 3, 9);
for (int i : d2)
std::cout << i << " ";
d2.erase(d2.begin());
d2.erase(d2.begin(), d2.begin() + 4);
d2.pop_back();
d2.pop_front();
for (int i : d2)
std::cout << i << " ";
d2.swap(d1);
//forward_list 单链表
std::cout << "\nforward_list" << std::endl;
//构造与复制
std::forward_list<int>f1{ 1,2,3 };
std::forward_list<int>f2{ {1,2,3,4} };
std::forward_list<int>f3 = { 1,2,3,4,5,6 };
std::forward_list<int>f4(f1);
std::forward_list<int>f6(f1.begin(), f1.end());
std::forward_list<int>f7;
f7.assign(5, 1);
for (int i : f7)
std::cout << i << " ";
std::cout << std::endl;
//添加与删除元素
std::forward_list<p>f8;
f8.emplace_front("Marisa", 2);
f8.emplace_after(f8.begin(), "Reimu", 1);//如果容器为空时使用emplace_after会出错
f8.push_front(p("Alice", 3));
for (auto c : f8)
std::cout << c.toString() << std::endl;
f2.insert_after(f2.begin(), 11);
f2.insert_after(f2.begin(), f1.begin(), f1.end());
f2.insert_after(f2.begin(), 4, 9);
std::cout << std::endl;
for (auto c : f2)
std::cout << c << " ";
f2.pop_front();
//auto it = f2.begin();
//f2.erase_after(f2.begin(),++it );
f2.erase_after(f2.begin());
std::cout << std::endl;
for (auto c : f2)
std::cout << c << " ";
//----list-----双链表
std::cout << "\nlist" << std::endl;
//构造与复制
std::list<int>l1{ {1,2,3,4} };
std::list<int>l2{ 1,2,3,4,5 };
std::list < int > l3(l1);
std::list<int>l4 = { 1,2,3,4,5,6,7 };
std::list<int>l6(l1);
std::list<int>l7(l6.begin(), l6.end());
std::list<int>l8;
l8.assign(6, 0);
//添加与删除元素
std::list<p>l9;
l9.emplace(l9.begin(), "Kaguya", 1);
l9.emplace_back("Fujiwara no Mokou", 2);
l9.emplace_front("Kamishirasawa Keine", 3);
l9.push_back(p("Yagokoro eirin", 4));
l9.push_front(p("Reisen", 5));
for (auto c : l9)
std::cout << c.toString() << std::endl;
l1.insert(l1.begin(), 8);
l1.insert(l1.begin(), l2.begin(), l2.end());
l1.sort();//排序
l1.unique();//删除连续的重复元素
l1.reverse();//反序
l1.remove(1);//删除等于1的元素
l1.remove_if([](int n) {return n > 5; });//删除大于5的元素
for (auto c : l1)
std::cout << c << " ";
std::cout << std::endl;
l1.pop_back();
l1.pop_front();
l1.erase(l1.begin());
l1.erase(l1.begin(), --l1.end());
for (auto c : l1)
std::cout << c << " ";
}
输出
array
3 2 1
3
t t
vector
1---3
2
Jackey,id:3
Tom,id:1
Jerry,id:2
1 2 3
1 2
1 2 3 1 1 3 1 2
1 3 1 2
1 3 1 2
1 2 3
4--1073741823--8
deque
1 1 1 1 1 1 1 1 1 1 /n1 --3
10---1073741823
Meirin,id:5
Sakuya,id:3
Flandre,id:1
Remilia,id:2
Patchouli,id:4
9 9 9 1 2 3 11 1 2 3 4 11 1 2 3
forward_list
1 1 1 1 1
Alice,id:3
Marisa,id:2
Reimu,id:1
1 9 9 9 9 1 2 3 11 2 3 4
9 9 9 1 2 3 11 2 3 4
list
Reisen,id:5
Kamishirasawa Keine,id:3
Kaguya,id:1
Fujiwara no Mokou,id:2
Yagokoro eirin,id:4
5 4 3 2
3
C:\Users\xhh\Source\Repos\c++study\queue_S\Debug\queue_S.exe (进程 47068)已退出,代码为 0。
按任意键关闭此窗口. . .
set:含有key类型的已排序集,用比较函数进行排序。搜素、移除和插入拥有对数复杂度。set通常以红黑树来实现。
set按照键排序,所以不会出现相等的元素。
map:有序键值对容器,元素的键是唯一的,用比较函数排序键。map通常也用红黑树来实现。
mutliset
和multimap
是set和map对应的键值不唯一的,相等的键的排列顺序按照插入顺序,且不会改变。
#include
#include
#include
#include
#include
#include
class p {
private:
std::string name;
int id;
public:
p(std::string n, int i) :name(n), id(i) {}
std::string getName() { return name; }
int getId() { return id; }
std::string const toString() const { std::string str = name; str.append(",id:"); str.append(std::to_string(id)); return str; }
constexpr bool operator<(const p& r)const { return id < r.id ; };//注意,非常重要的一步:提供内建运算符operator<重载,
//这样提供了这个类的比较大小的途径,只有这样这种类的关联容器才能正常使用
};
int main()
{
//---set---
std::cout << "---set----" << std::endl;
//构造和复制
std::set<int>s1{ 1,2,3 };
std::set<int>s2{ {1,2,3,4,5,6,7} };
std::set<int>s3 = { 1,2,3 };
//std::sets4(s1);
//std::sets5(s1.begin(), s1.end());
std::set<int>s6;
//没有assign函数
//插入和删除元素
std::set<p>s7;
s7.emplace("Shameimaru Aya", 1);
s7.emplace_hint(s7.begin(), "Cirno", 2);
s7.insert(s7.end(), p("Youmu", 3));
for (auto c : s7)
std::cout << c.toString() << std::endl;
s1.insert(s1.begin(), 9);
s1.insert(s2.begin(), s2.end());
for (auto c : s1)
std::cout << c << " ";
std::cout << std::endl;
s1.erase(s1.begin(),++s1.begin());
s1.erase(3);
s1.erase(s1.begin());
for (auto c : s1)
std::cout << c << " ";
std::cout << std::endl;
std::cout << s2.count(2) << std::endl;//返回匹配特定键的元素数量
std::cout << *s2.find(2) << std::endl;//寻找
std::cout << *s2.equal_range(3).second << std::endl;//返回匹配特定键的元素范围,
std::cout << *s2.lower_bound(4) << std::endl;//返回指向首个不小于给定键的元素的迭代器
std::cout << *s2.upper_bound(5) << std::endl;//返回指向首个大于给定键的元素的迭代器
//---map-----
std::cout << "\nmap" << std::endl;
//构造和复制
std::map<int, int>m1{{1,2},{3,4}};
std::map<char, int>m2{ {std::make_pair('c',1),std::make_pair('d',2)} };
std::map<int, int>m3(m1);
std::map<int, int>m4 = { {1,2},{3,4},{5,6},{7,8} };
std::map<p, int>m5;
m5.emplace(std::make_pair(p("Yuyuko",1),2));
m5.emplace(std::make_pair(p("Yakumo Yukari", 2), 3));
m5.emplace_hint(m5.begin(), std::make_pair(p("Suika", 4),5));
m5.try_emplace(p("Tenshi", 5), 1);
m5[p("Ran", 6)] = 4;
for (auto c : m5)
std::cout << c.first.toString() << "-->value-->" << c.second << std::endl;
std::string str = "Explicit is better than implicit.";
//可以将map用于统计一段字符串中的字符
std::map <char, int>m6;
for(auto c:str)
{
if (m6.find(c) != m6.end())//寻找指定键值的元素,没有则返回容器的尾部迭代器end()
m6[c]++;
else
m6[c] = 1;
}
for (auto& c : m6)
std::cout << c.first << ":" << c.second << " ";
std::cout << std::endl;
m6.erase(m6.begin());//删除迭代器指向的元素
m6.erase(m6.begin(), ++m6.begin());//删除范围内的元素
m6.erase('i');//删除指定键值的元素
for (auto& c : m6)
std::cout << c.first << ":" << c.second << " ";
std::cout << std::endl;
return 0;
}
输出
---set----
Shameimaru Aya,id:1
Cirno,id:2
Youmu,id:3
1 2 3 4 5 6 7 9
4 5 6 7 9
1
2
4
4
6
map
Yuyuko,id:1-->value-->2
Yakumo Yukari,id:2-->value-->3
Suika,id:4-->value-->5
Tenshi,id:5-->value-->1
Ran,id:6-->value-->4
:4 .:1 E:1 a:1 b:1 c:2 e:2 h:1 i:6 l:2 m:1 n:1 p:2 r:1 s:1 t:5 x:1
E:1 a:1 b:1 c:2 e:2 h:1 l:2 m:1 n:1 p:2 r:1 s:1 t:5 x:1
C:\Users\xhh\Source\Repos\c++study\queue_S\Debug\queue_S.exe (进程 61628)已退出,代码为 0。
按任意键关闭此窗口. . .
unordered_set
:键值唯一,搜索、插入和移除具有平均常数时间复杂度。在内部元素不以特别的顺序排列,而是组织进桶中。元素被放进那个同依赖于其值的哈希。这允许对单独元素的快速访问。但不可修改容器元素。注意无序容器比较特殊,如果要用这一容器来装自定义的某个类,要么自定义一个这个类的散列函数然后再容器声明时使用,或者使用注入的std::hash特化。
#include
#include
#include
#include
#include
#include
class p {
private:
std::string name;
int id;
public:
p(std::string n, int i) :name(n), id(i) {}
const std::string getName() const{ return name; }
const int getId() const { return id; }
std::string const toString() const { std::string str = name; str.append(",id:"); str.append(std::to_string(id)); return str; }
constexpr bool operator<(const p& r)const { return id < r.id ; };//注意,非常重要的一步:提供内建运算符operator<重载,
//这样提供了这个类的比较大小的途径,只有这样这种类的关联容器才能正常使用
constexpr bool operator==(const p& r)const { return id == r.id && name==r.name; }
};
// std::hash 的自定义特化能注入 namespace std
//要使用无序关联容器来装这个类的话,这一步是必要的
namespace std
{
template<> struct hash<p>
{
typedef p argument_type;
typedef std::size_t result_type;
result_type operator()(argument_type const& s) const
{
result_type const h1(std::hash<std::string>{}(s.getName()));
result_type const h2(std::hash<int>{}(s.getId()));
return h1 ^ (h2 << 1); // 或使用 boost::hash_combine (见讨论)
}
};
}
int main()
{
//unordered_set
std::cout << "unordered_set" << std::endl;
std::unordered_set<int>us1{{1,2,3,4,5}};
std::unordered_set<int>us2(us1);
std::unordered_set<int>us3 = { 1,2,3,4,5,6 };
//插入和删除
std::unordered_set<p>us4;
us4.emplace("Rumia", 1);
us4.emplace_hint(us4.begin(), p("Daiyousei", 2));
us4.insert(us4.begin(), p("Lily White", 3));
for (auto c : us4)
std::cout << c.toString() << std::endl;
us3.insert(us3.begin(), 7);
us3.insert(us1.begin(), us1.end());
for (auto c : us3)
std::cout << c << " ";
std::cout << std::endl;
us3.erase(us3.begin());
us3.erase(3);
us3.erase(us3.begin(), ++us3.begin());
for (auto c : us3)
std::cout << c << " ";
std::cout << std::endl;
std::cout << *us3.find(5) << std::endl;
//桶接口
std::cout << us3.bucket_count() << std::endl;//返回桶的个数
std::cout << us3.bucket(6) << std::endl;//返回装着特定的元素的桶
for (int i = 0; i < us3.bucket_count(); ++i)
std::cout << "第" << i << "个桶有" << us3.bucket_size(i) << "个元素" << std::endl;//返回指定桶的元素数量
std::cout << us3.hash_function()(111) << std::endl;//返回计算键值哈希值的哈希函数
//unordered_map
std::cout << "unordered_map" << std::endl;
//构造与复制
std::unordered_map<int, int>um1{ {{1,2},{2,3},{3,4}} };
std::unordered_map<int, int>um2 = { {1,2},{2,4} };
std::unordered_map<int, int>um3(um1);
std::unordered_map<char, int>um4 = { std::make_pair('a',1),std::make_pair('b',2) };
//添加与删除
std::unordered_map<p, int>um5;
um5.emplace(p("Suika",1),1);
um5.emplace_hint(um5.begin(), p("Yuka", 2), 2);
um5.insert({ p("Shikieiki Yamaxanadu",3),3 });
for (auto c : um5)
std::cout << c.first.toString() << ":" << c.second << std::endl;
um1.insert(um2.begin(),um2.end());
um1.insert({ 5,6 });
um1.insert({ 7,2 });
um1.insert({ 6,6 });
for (auto c : um1)
std::cout << c.first << ":" << c.second << std::endl;;
um1.erase(1);
um1.erase(um1.begin(), ++um1.begin());
um1.erase(um1.begin());
for (auto c : um1)
std::cout << c.first << ":" << c.second << std::endl;;
std::cout << um1.bucket_count() << std::endl;//返回桶的个数
std::cout << um1.bucket(5) << std::endl;//返回装着特定的元素的桶
for (int i = 0; i < um1.bucket_count(); ++i)
std::cout << "第" << i << "个桶有" << um1.bucket_size(i) << "个元素" << std::endl;//返回指定桶的元素数量
std::cout << um1.hash_function()(111) << std::endl;//返回计算键值哈希值的哈希函数
return 0;
}
输出
unordered_set
Rumia,id:1
Daiyousei,id:2
Lily White,id:3
1 2 3 4 5 6 7
4 5 6 7
5
8
3
第0个桶有1个元素
第1个桶有1个元素
第2个桶有1个元素
第3个桶有1个元素
第4个桶有0个元素
第5个桶有0个元素
第6个桶有0个元素
第7个桶有0个元素
3605745226
unordered_map
Suika,id:1:1
Yuka,id:2:2
Shikieiki Yamaxanadu,id:3:3
1:2
2:3
3:4
5:6
7:2
6:6
5:6
7:2
6:6
8
0
第0个桶有1个元素
第1个桶有0个元素
第2个桶有1个元素
第3个桶有1个元素
第4个桶有0个元素
第5个桶有0个元素
第6个桶有0个元素
第7个桶有0个元素
3605745226
C:\Users\xhh\Source\Repos\c++study\queue_S\Debug\queue_S.exe (进程 61952)已退出,代码为 0。
按任意键关闭此窗口. . .
stack
:栈,先进后出。
queue
:队列,先进先出
priority_queue
:优先队列,提供常数时间的最大元素查找,对数代价的插入与释出。可用用户提供的compare
更改顺序。
#include
#include
#include
#include
#include
#include
int main()
{
std::vector<int>v1{ {1,2,3,4} };
std::deque<int>d1{ {1,2,3,4,5,6} };
std::list<int>l1{ {1,2,3,4,5,6,7} };
std::stack<int, std::vector<int>>s1(v1);
std::stack<int, std::deque<int>>s2(d1);
std::stack<int, std::list<int>>s3(l1);
std::stack<int>s4(d1);
std::cout << s1.top() << std::endl;
std::cout << s1.size() << std::endl;
s1.push(1);//向栈顶插入元素
for (int i=0,j=s1.size();i<j;++i)
{
std::cout << s1.top() << " ";//访问栈顶元素
s1.pop();//删除栈顶元素
}
//
std::queue<int, std::deque<int>>q1(d1);
std::queue<int, std::list<int>>q2(l1);
std::queue<int>q3(d1);
q1.push(10);//向队尾插入元素
q1.emplace(11);
std::cout << q1.front() << std::endl;//访问第一个元素
std::cout << q1.back() << std::endl;//访问最后一个元素
for(int i=0,j=q1.size();i<j;++i)
{
std::cout << q1.front() << " ";
q1.pop();//删除首个元素
}
//
std::priority_queue<int, std::vector<int>>p1(std::less<int>(),v1);
std::priority_queue<int, std::deque<int>>p2(std::less<int>(),d1);
std::priority_queue<int>p3(std::less<int>(),v1);//所以优先队列默认是使用容器vector
std::cout << std::endl;
p1.push(5);
p1.push(3);
std::priority_queue<int>p4(p3);
std::cout << p1.top() << std::endl;
std::cout << p1.size() << std::endl;
for(int i=0,j=p1.size();i<j;++i)
{
std::cout << p1.top() << " ";
p1.pop();
}
std::cout << std::endl;
return 0;
}
输出
4
4
1 4 3 2 1 1
11
1 2 3 4 5 6 10 11
5
6
5 4 3 3 2 1
C:\Users\xhh\Source\Repos\c++study\queue_S\Debug\queue_S.exe (进程 71228)已退出,代码为 0。
按任意键关闭此窗口. . .
Heap,堆,通常指二叉堆,二叉堆是一个近似完全二叉树的数据结构。子节点的键值总是小于(或者大于)它的父节点,且每个节点的左右子树又是一个二叉堆(大根堆或者小根堆)。根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或者小根堆。常用作实现优先队列。
特点:
2*i+1
,右子节点位置2*i+2
,子节点i的父节点位置floor((i-1)/2)
基本操作:
#pragma once
#include
#include
#include
#include
#include
#include
template<typename T,typename TComparator=std::equal_to<T>,typename PComparator=std::less<double>,typename Hasher=std::hash<T>>
class maxHeap
{
public:
maxHeap(int m=2,const PComparator &c=PComparator(),const Hasher &hasj=Hasher(),const TComparator &tcomp=TComparator());
~maxHeap();
void push(double pri,const T &item);
T const &top()const;
void pop();
bool empty()const;
void decreaseKey(double newpri,const T &item);
private:
void trickleUp(int loc);
void trickDown(int loc);
std::vector<std::pair<double,T>> store_;
int m_;
PComparator c_;
std::unordered_map<T,size_t,Hasher,TComparator>KeyTOLocation_;
};
template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline maxHeap<T, TComparator, PComparator, Hasher>::maxHeap(int m, const PComparator& c, const Hasher& hash, const TComparator& tcomp):store_(),m_(m),KeyTOLocation_(100,hash,tcomp){}
template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline maxHeap<T, TComparator, PComparator, Hasher>::~maxHeap()
{
}
template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline void maxHeap<T, TComparator, PComparator, Hasher>::push(double pri, const T& item)
{
std::pair<double,T>temp(pri,item);
store_.push_back(temp);
KeyTOLocation_[item]=store_.size();
trickleUp(store_.size()-1);
}
template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline T const& maxHeap<T, TComparator, PComparator, Hasher>::top() const
{
if(empty()){
throw std::logic_error("can't top an empty heap");
}
return store_[0].second;
}
template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline void maxHeap<T, TComparator, PComparator, Hasher>::pop()
{
if(empty())
throw std::logic_error("can't pop an empty heap");
store_[0]=store_[store_.size()-1];
KeyTOLocation_.erase(store_[0].second);
store_.pop_back();
if(empty())return;
trickleUp(0);
}
template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline bool maxHeap<T, TComparator, PComparator, Hasher>::empty() const
{
return store_.empty();
}
template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline void maxHeap<T, TComparator, PComparator, Hasher>::decreaseKey(double newpri, const T& item)
{
std::pair<double,T>temp=store_[KeyTOLocation_[item]];
temp.first=newpri;
trickleUp(KeyTOLocation_[item]);
}
template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline void maxHeap<T, TComparator, PComparator, Hasher>::trickleUp(int loc)
{
int parent=(loc-1)/m_;
while(parent>=0&&c_(store_[loc].first,store_[parent].first)){
std::pair<double,T>temp=store_[loc];
store_[loc]=store_[parent];
store_[parent]=temp;
double to_swap=KeyTOLocation_[store_[loc].second];
KeyTOLocation_[store_[loc].second]=KeyTOLocation_[store_[parent].second];
KeyTOLocation_[store_[parent].second]=to_swap;
loc=parent;
parent=(loc-1)/m_;
}
}
template<typename T, typename TComparator, typename PComparator, typename Hasher>
inline void maxHeap<T, TComparator, PComparator, Hasher>::trickDown(int loc)
{
if(loc*m_+1>store_.size()-1)return;
int smallerChild=m_*loc+1;
for(size_t i=0;i<m_;++i){
if(m_*loc+i<store_.size()){
int rChild=m_*loc+i+1;
if(c_(store_[rChild].first,store_[smallerChild].first)){
smallerChild=rChild;
}
}
}
if(c_(store_[smallerChild].first,store_[loc].first)){
std::pair<double,T>temp=store_[loc];
store_[loc]=store_[smallerChild];
store_[smallerChild]=temp;
double to_swap=KeyTOLocation_[store_[loc].second];
KeyTOLocation_[store_[loc].second]=to_swap;
trickDown(smallerChild);
}
}
main.cpp
#include"maxHeap.h"
#include
int main(){
maxHeap<int>heap;
heap.push(0.3,10);
heap.push(0.1,22);
heap.push(0.5,123);
heap.push(0.8,12);
while(!heap.empty()){
std::cout<<heap.top()<<std::endl;
heap.pop();
}
return 0;
}
参考:《疯狂Java讲义》、Java官方文档
Java集合类主要有:Set、Queue、List、Map四种体系。和C++中的相关的容器类相对应,用途大概都能想到,但实际用法和C++还是有很大区别的,毕竟是不同的语言。Java的集合类主要由两个接口派生而来:Collection
、Map
,他们是集合框架的根接口,两个接口又包含一些子接口或实现类。它们的继承树分别如下:
这些集合类定义在Java.Base
模块下的Java.util
包下。Java.util
包的官方简介如下:
Contains the collections framework, some internationalization support classes, a service loader, properties, random number generation, string parsing and scanning classes, base64 encoding and decoding, a bit array, and several miscellaneous utility classes. This package also contains legacy collection classes and legacy date and time classes.
Collection
和Map
都是泛型接口。当然,Java里面实例化泛型类时可以不用尖括号显示指定类型,这样一般容器中元素的类型都是Object
的,这样虽然没有语法错误,但IDEA会给出警告的,因为这样可能会出现一些问题,一般还是用棱形语法指定容器内元素类型比较好。
首先是Collection接口的通用的一些方法:
package algo_study;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.function.Predicate;
public class collectionStudy {
public static void main(String[] args) {
Collection test=new ArrayList();
test.add(122);
test.add(1232);
test.add(11);
System.out.println("test集合的元素个数"+test.size());
test.remove(122);
test.add('a');
test.add("adfs");
test.forEach(obj->System.out.println("->"+obj));//用Lambda表达式遍历集合中的元素
for(Iterator c = test.iterator();c.hasNext();) {//用迭代器遍历集合中的元素
Object o = c.next();
System.out.println(o);
if (o.equals(11)) {
c.remove();
}
}
Iterator it=test.iterator();
it.forEachRemaining(obj->System.out.println("-"+obj));//用迭代器提供的forEachRemaining方法的lambda来遍历
Object[] oo=test.toArray();
System.out.println(oo[0]);
System.out.println(cal(test,ele->ele.equals('a')));
//用stream来操作集合
System.out.println(test.stream().filter(ele->ele.toString().length()>1).count());
}
public static int cal(Collection c, Predicate p){//利用Predicate来操作集合
int t=0;
for(Iterator it=c.iterator();it.hasNext();){
if(p.test(it.next())){
t++;
}
}
return t;
}
}
输出
test集合的元素个数3
->1232
->11
->a
->adfs
1232
11
a
adfs
-1232
-a
-adfs
1232
1
2
主要的是一些遍历结合元素的方法:用foreach
结合lambda表达式、用迭代器遍历、用Predicate
操作、用Stream
操作集合。
Set接口不允许添加相同的元素,其它基本与Collect相同。而HashSet
是它的典型实现,通常用HashSet来作为Set的实现类。
HashSet的特点:
HashSet集合存入一个元素时会调用对象的hashCode()
方法来得到其hashCode
值,然后根据该值决定对象在HashSet中的存储位置。如果有两个值通过equals()
方法返回true,但它们的hashCode()
返回值不同,还是可以添加成功,被储存在不同位置。所以集合实际上在添加元素时同时做出两个判断:通过equals
方法来判断新加入的元素是否和已有的元素相等、计算新加入的元素的hashCode值确定已有元素的hashCode是否存在和它相等的。这时会存在几种情况:如果新加入的元素–equals方法返回true且hashCode相同则添加失败、equals返回true但hashCode不同则添加成功、equals返回false且hashCode不同则添加失败、equals返回false但hashCode相同–>这时HashSet会试图将它们保存在同一个位置,会在这个位置用链式结构来保存多个对象,这样会导致HashSet的新能降低的,所以应尽量避免出现这种情况。如下
package algo_study;
import java.util.*;
import java.util.function.Predicate;
class test{
public boolean equals(Object obj){
return false;
}
public int hashCode(){
return s.hashCode();
}
test(int i,String s){
this.id=i;
this.s=s;
}
private int id;
private String s;
}
public class collectionStudy {
public static void main(String[] args) {
test t=new test(1,"test");
Set<test> s=new HashSet<>();
s.add(t);
System.out.println(s.add(new test(2,"test")));//true
System.out.println(s.add(new test(2,"testt")));//true
}
}
LinkedHashSet
是HashSet的子类,它同时用链表来维护元素的次序,这样,遍历时它会按元素的添加顺序来访问集合的元素,虽然维护插入顺序会降低它的性能,但遍历元素时它具有更好的性能。
TreeSet
是SortedSet
接口的实现类。它确保集合元素处于排序状态。相比较HashSet它提供了一些额外的方法。TreeSet
是采用红黑树的数据结构来存储集合元素,我们知道红黑树是需要比较两个元素的大小的,它支持两种排序方法:自然排序
和定制排序
,默认采用自然排序。
自然排序
:调用元素的compareTo(Object o)
方法来比较元素之间的大小,然后将元素按升序排列。该方法返回一个int类型,相等则返回0,小于则返回负整数,大于则返回正整数。
Java提供了一个Comparable
接口,该接口中定义了compareTo(Object o)
的方法,相当于定义了一个规范。所以如果要让TreeSet
的元素为自定义的类型,简单的做法是让这个自定义的类型实现Comparable
接口,如果该类没有实现Comparable接口,添加元素时会引发ClassCaseException
异常。Java中常用的一些类自然是实现了该接口的,如:BigDecimal
、BigInteger
、Character
、Boolean
、String
、Date
、Time
等。
定制排序
:自然排序时这个类型通过实现Comparable接口实现,如果不想让类实现Comparable接口,也可以在实例化TreeSet对象时提供一个Comparator
对象与TreeSet集合关联,可以用Lambda表达式来代替Comparator。
这两种排序选择其中一种就行了。
EnumSet
类:枚举类的集合类。其中的元素都是指定的枚举类型的枚举值。EnumSet的集合元素是有序的,以枚举值在枚举类中的定义顺序来决定集合元素的顺序。EnumSet内部通过位向量的形式来存储,它占用内存小,运行效率高,尤其在进行批量操作时。
EnumSet不允许插入null元素。
注意:上面介绍的Set的三个实现类HashSet、TreeSet、EnumSet都是线程不安全的,为此,可以通过Collections
工具类中的synchronizedSortedSet
方法来包装该Set集合。
SortedSet s=Collections.synchronizedSortedSet(new TreeSet(...));
上面介绍的几个实现类的实例如下:
package algo_study;
import java.util.*;
import java.util.function.Predicate;
class test {//implements Comparable
public boolean equals(Object obj){
return true;
}
public boolean equals(test t){
return id==t.id && s.equals(t.s);
}
public int hashCode(){
return s.hashCode();
}
test(int i,String s){
this.id=i;
this.s=s;
}
public String toString(){
return "id:"+this.id+" name:"+this.s;
}
private int id;
private String s;
public int getId(){return id;}
/*
@Override
public int compareTo(Object o) {
test t=(test)o;
if(idt.id)
return 1;
return 0;
}
*/
}
enum Week{
Monday,Tuesday,Wednesday,Thursday,Firday,Saturday,Sunday
}//一个枚举类
public class collectionStudy {
public static void main(String[] args) {
test t=new test(1,"test");
//HashSet
Set<test> s=new HashSet<>();
s.add(t);
System.out.println(s.add(new test(2,"test")));
System.out.println(s.add(new test(2,"testt")));
System.out.println(s);
s.forEach(o->System.out.println(o));
//LinkedHashSet
Set<test> s1=new LinkedHashSet<>();
s1.add(t);
s1.add(new test(3,"testt"));
s1.add(new test(2,"testtt"));
s1.add(new test(3,"tttt"));
System.out.println(s1);
//TreeSet
TreeSet<test> s2=new TreeSet<>((o1,o2)->
{
test m1=(test)o1;
test m2=(test)o2;
return m1.getId()<m2.getId()?-1:m1.getId()>m2.getId()?1:0;
});//Lambda表达式
s2.add(t);
System.out.println(s2.add(new test(2,"ttest")));
System.out.println(s2.add(new test(0,"tests")));
System.out.println(s2.add(new test(4,"ttt")));
System.out.println(s2);
System.out.println(s2.first());
System.out.println(s2.last());
System.out.println(s2.lower(t));
System.out.println(s2.higher(t));
System.out.println(s2.comparator());
System.out.println(s2.subSet(t,s2.last()));//返回子集
System.out.println(s2.headSet(t));//返回子集包含t之前的元素
System.out.println(s2.tailSet(t));//返回子集包含t之后的元素
//EnumSet
EnumSet<Week> ew=EnumSet.allOf(Week.class);
System.out.println(ew);
EnumSet<Week>ew1=EnumSet.noneOf(Week.class);
ew1.add(Week.Firday);
ew1.add(Week.Monday);
System.out.println(ew1);
EnumSet<Week>ew2=EnumSet.of(Week.Monday,Week.Firday);
System.out.println(ew2);
EnumSet<Week>ew3=EnumSet.range(Week.Thursday,Week.Saturday);
System.out.println(ew3);
EnumSet<Week>ew4=EnumSet.complementOf(ew3);
System.out.println(ew4);
}
}
false
true
[id:2 name:testt, id:1 name:test]
id:2 name:testt
id:1 name:test
[id:1 name:test, id:3 name:testt, id:2 name:testtt, id:3 name:tttt]
true
true
true
[id:0 name:tests, id:1 name:test, id:2 name:ttest, id:4 name:ttt]
id:0 name:tests
id:4 name:ttt
id:0 name:tests
id:2 name:ttest
algo_study.collectionStudy$$Lambda$2/1747585824@3d075dc0
[id:1 name:test, id:2 name:ttest]
[id:0 name:tests]
[id:1 name:test, id:2 name:ttest, id:4 name:ttt]
[Monday, Tuesday, Wednesday, Thursday, Firday, Saturday, Sunday]
[Monday, Firday]
[Monday, Firday]
[Thursday, Firday, Saturday]
[Monday, Tuesday, Wednesday, Sunday]
List集合:有序、可重复集合,可通过索引访问指定位置的元素。
ArrayList
和Vector
是List
接口的两个典型实现。ArrayList更常用一些。二者的显著区别是:ArrayList是线程不安全的,而Vector是线程安全的。所以Vector的新能更低一些。然而即使需要一个线程安全的List集合,也不推荐用Vector而是用Collections
工具类将一个ArrayList变成线程安全的。
Vector
提供了一个Stack
子类,用于模拟栈:peek()
显示第一个元素、pop()
弹出第一个元素、push()
将一个元素进栈。(线程安全的)。同样由于性能较差,不推荐用,取而代之的是ArrayDeque
。
package algo_study;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
public class listStudy {
public static void main(String[] args) {
//List
List l=new ArrayList();
l.add("元素1");
l.add("元素2");
l.add("element 3");
System.out.println(l);
l.add(0,"element4");
System.out.println(l);
for(int i=0;i<l.size();++i){
System.out.println(l.get(i));//通过索引访问元素,Java中并不能重载操作符,所以不能像C++中利用中括号
}
System.out.println(l.indexOf("element 3"));
l.remove(2);
System.out.println(l);
System.out.println(l.subList(1,l.size()));
l.set(0,"ele 0");
System.out.println(l);
l.sort((e1,e2)->{
return e1.toString().length()-e2.toString().length() ;
});//排序
System.out.println(l);
l.replaceAll(ele->ele.toString().length());
System.out.println(l);
//ArrayList
ArrayList<String>a1=new ArrayList<>();
a1.add("tetst");
a1.add("ele1");
a1.add("ele2");
a1.add(1,"ele3");
System.out.println(a1);
a1.remove(1);
System.out.println(a1);
a1.set(1,"test");
System.out.println(a1);
ListIterator it=a1.listIterator();
while(it.hasNext()){//用迭代器遍历
System.out.println(it.next());
}
while(it.hasPrevious()){
System.out.println(it.previous());//反向遍历
}
//固定长度的List
List<String>l1=Arrays.asList("test","ele");
System.out.println(l1);//l1不允许添加删除元素
}
}
输出
[元素1, 元素2, element 3]
[element4, 元素1, 元素2, element 3]
element4
元素1
元素2
element 3
3
[element4, 元素1, element 3]
[元素1, element 3]
[ele 0, 元素1, element 3]
[元素1, ele 0, element 3]
[3, 5, 9]
[tetst, ele3, ele1, ele2]
[tetst, ele1, ele2]
[tetst, test, ele2]
tetst
test
ele2
ele2
test
tetst
[test, ele]
Queue
接口模拟队列,它有一个PriorityQueue
实现类,它还有一个Deque
接口,代表一个双端队列,可同时从两端添加删除元素。Deque
的实现类即可以当成队列使用也可以当成栈来使用。两个实现类:ArrayDeque
和LinkedList
。
PriorityQueue
会按元素的大小将元素排序。
Deque方法对比:
Queue | Deque | Stack | Deque |
---|---|---|---|
add、offer | addLast、offerLast | push | addFirst、offerFirst |
remove、poll | removeFirst、pollFirst | pop | removeFirst、pollFirst |
element、peek | getFirst、peekFirst | peek | getFirst、peekFirst |
ArrayDeque内部以数组的形式来保存元素,所以随机访问集合时有比较好的性能。而LinkedList内部以链表的形式来保存集合中的元素,所以随机访问集合元素时性能差,但插入删除元素时性能很好。另外Vector也是以数组形式存储元素的,但它实现线程同步同时实现机制不好导致各方面性能都不好。
实际使用时自然是根据实际需求灵活选择那个容器。如果不知道选择那个时就选ArrayList
就行了。
package algo_study;
import java.util.ArrayDeque;
import java.util.LinkedList;
import java.util.PriorityQueue;
public class QueueLearn {
public static void main(String[] args) {
//PriorityQueue
PriorityQueue p1=new PriorityQueue();
p1.offer(1);
p1.offer(4);
p1.offer(-2);
p1.offer(4);
System.out.println(p1);
System.out.println(p1.poll());
//ArrayDeque
//模拟栈,后进先出
ArrayDeque st=new ArrayDeque();
st.push(1);//或者addFirst,入栈
st.push(2);
st.push(4);
st.push(6);
System.out.println(st);
System.out.println(st.peek());//显示栈顶元素
while(st.size()!=0){
System.out.println(st.pop());//出栈
}
//模拟队列
ArrayDeque que=new ArrayDeque();
que.add(123);//入队
que.offer(112);//入队
que.add(23);
que.add(122);
System.out.println(que);
System.out.println(que.element());//显示队首元素
System.out.println(que.peek());//显示队首元素
System.out.println(que.remove());//出队
while(que.size()!=0){
System.out.println(que.poll());//出队
}
//LinkedList
//栈,和ArrayList的方法名相同,还是很不错的
LinkedList sta=new LinkedList();
sta.push(11);
sta.push(22);
sta.push(2);
sta.push(4);
sta.push(6);
System.out.println(sta);
System.out.println(sta.peek());//显示栈顶元素
while(st.size()!=0){
System.out.println(sta.pop());//出栈
}
//队列,
ArrayDeque quel=new ArrayDeque();
quel.add(123);//入队
quel.offer(112);//入队
quel.add(23);
quel.add(122);
System.out.println(quel);
System.out.println(quel.element());//显示队首元素
System.out.println(quel.peek());//显示队首元素
System.out.println(quel.remove());//出队
while(que.size()!=0){
System.out.println(quel.poll());//出队
}
}
}
输出
[-2, 4, 1, 4]
-2
[6, 4, 2, 1]
6
6
4
2
1
[123, 112, 23, 122]
123
123
123
112
23
122
[6, 4, 2, 22, 11]
6
[123, 112, 23, 122]
123
123
123
Map
接口下有:HashMap
、LinkedHashMap
、SortedMap
(接口)、TreeSet
、EnumSet
等子接口和实现类,正好是和Set
对应的,这样正好方便我们去理解这些子接口和实现类。
HashMap和Hashtable都是HashMap接口的典型实现类,它们的区别类似于ArrayList和Vector。
Hashtable类有个子类为Properities
,可以用来读写属性文件,这个类挺有用的。
而WeakHashMap
与HashMap用法相似,区别在于WeakHashMap对象的key只保留对实际对象的弱引用。
IdentityHashMap
与HashMap也相似,但它处理两个key相等时仅当两个key严格相等时才认为它们相等。
package algo_study;
import java.io.*;
import java.util.*;
class mapTest{
private int id;
private String name;
public int getId(){return id;}
public String getName(){return name;}
@Override//对于HashMap来说必须要重载equals和hashCode这两个方法,顺便重载toString
public boolean equals(Object o){
mapTest m=(mapTest)o;
return id==m.getId() && name.equals(m.getName());
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
@Override
public String toString(){
return "id->"+id+"&& name->"+name;
}
public mapTest(int i,String s){
this.id=i;
this.name=s;
}
}
public class mapLearn {
public static void main(String[] args) throws IOException {
//HashMap
HashMap hm=new HashMap();
hm.put(new mapTest(10,"Tom"),1212);
hm.put(new mapTest(11,"Jack"),21212);
mapTest m=new mapTest(111,"Joker");
hm.put(m,111);
hm.put(m,12112);
hm.put(m,123);//放入重复的key会覆盖原有的Value。
System.out.println(hm);
System.out.println(hm.put(m,11111));//如果覆盖原有值时会返回被覆盖的值
System.out.println(hm.containsKey(m));
System.out.println(hm.containsValue(1212));
for(Object k:hm.keySet()){
System.out.println(k+"---->"+hm.get(k));//遍历
}
hm.forEach((k,v)->System.out.println(k+"->"+v));//用forEach时有两个参数,key和value
HashMap<mapTest, Integer>mi=new HashMap<>();
hm.forEach((k,v)->mi.put((mapTest) k,(int)v));
System.out.println(mi);
//Properities类,这个类是Hashtable的子类,用于读写属性文件,非常有用的类
//
Properties properties=new Properties();
properties.setProperty("name","XieHuiHui");
properties.setProperty("QQ","0000000000");
properties.setProperty("phone number","00000000000000");
properties.store(new FileOutputStream("test.ini"),"coment line");//写入属性文件
Properties pro2=new Properties();
File f=new File("test.ini");
System.out.println(f.exists());
pro2.load(new FileInputStream("test.ini"));//读取属性文件
System.out.println(pro2);
System.out.println(pro2.get("QQ"));
//LinkedHashMap
LinkedHashMap<mapTest,Integer>lmi=new LinkedHashMap<>();
lmi.put(m,111);
lmi.put(new mapTest(11,"sss"),11);
lmi.put(new mapTest(12313,"sdfa"),1231);
System.out.println(lmi);
//TreeMap
TreeMap<mapTest,Integer>tmi=new TreeMap<>((o1,o2)->{
mapTest t1=(mapTest) o1;
mapTest t2=(mapTest)o2;
return o1.getId()-o2.getId();
});//定制排序
tmi.put(m,111);
tmi.put(new mapTest(1,"2w324"),12);
tmi.put(new mapTest(123,"sadfs"),2);
System.out.println(tmi);
//identityHashMap
IdentityHashMap id=new IdentityHashMap();
id.put(m,111);
id.put(new mapTest(1,"aaa"),12);
id.put(new mapTest(123,"sadfs"),2);
id.put(new mapTest(1,"aaa"),12);
System.out.println(id);
//EnumMap
EnumMap<Week,String> en=new EnumMap(Week.class);
en.put(Week.Monday,"周一");
en.put(Week.Tuesday,"周二");
System.out.println(en);
}
}
{id->111&& name->Joker=123, id->11&& name->Jack=21212, id->10&& name->Tom=1212}
123
true
true
id->111&& name->Joker---->11111
id->11&& name->Jack---->21212
id->10&& name->Tom---->1212
id->111&& name->Joker->11111
id->11&& name->Jack->21212
id->10&& name->Tom->1212
{id->111&& name->Joker=11111, id->11&& name->Jack=21212, id->10&& name->Tom=1212}
true
{phone number=00000000000000, name=XieHuiHui, QQ=0000000000}
0000000000
{id->111&& name->Joker=111, id->11&& name->sss=11, id->12313&& name->sdfa=1231}
{id->1&& name->2w324=12, id->111&& name->Joker=111, id->123&& name->sadfs=2}
{id->111&& name->Joker=111, id->1&& name->aaa=12, id->123&& name->sadfs=2, id->1&& name->aaa=12}
{Monday=周一, Tuesday=周二}
synchronizedXXX()
方法package algo_study;
import java.util.*;
public class collectionsLearn {
public static void main(String[] args) {
//改变List中元素次序的操作
List l=new ArrayList();
l.add(11);
l.add(22);
l.add(1);
l.add(23);
System.out.println(l);
Collections.reverse(l);
System.out.println(l);
Collections.shuffle(l);
System.out.println(l);
Collections.sort(l);
System.out.println(l);
Collections.sort(l,(o1,o2)->{
int i1=(int)o1;
int i2=(int)o2;
return i2-i1;
});
System.out.println(l);
Collections.swap(l,0,1);
System.out.println(l);
Collections.rotate(l,2);
System.out.println(l);
//查找、替换
l.add(2);
l.add(4);
Collections.sort(l);
System.out.println(l);
System.out.println(Collections.binarySearch(l,4));//二分法搜索
System.out.println(Collections.max(l));
System.out.println(Collections.min(l));
List l2=new ArrayList(l);
Collections.fill(l2,2);
System.out.println(l2);
l.add(1);
System.out.println(Collections.frequency(l,1));
System.out.println(Collections.indexOfSubList(l,l2));
System.out.println(Collections.replaceAll(l,1,2));
System.out.println(l);
//同步控制,下面创建的几个对象都是线程安全的
List l3=Collections.synchronizedList(new ArrayList<>());
Set s=Collections.synchronizedSet(new HashSet<>());
Map m=Collections.synchronizedMap(new HashMap<>());
}
}
[11, 22, 1, 23]
[23, 1, 22, 11]
[11, 22, 23, 1]
[1, 11, 22, 23]
[23, 22, 11, 1]
[22, 23, 11, 1]
[11, 1, 22, 23]
[1, 2, 4, 11, 22, 23]
2
23
1
[2, 2, 2, 2, 2, 2]
2
-1
true
[2, 2, 4, 11, 22, 23, 2]
前面可以看出Java和C++的容器类不管时用途还是用法都是比较类似的,也都有很多细节需要注意。而python的容器就显得和不一样。
python内置的容器主要有三个list
、tuple
、dict
,这三个list最常用,我反正能用list的基本都用list,list本身很随意,可以任意添加元素,元素也可以是不同类型的,元素也可以是list、tuple、dict。
要说对应的化,list可勉强对应vector,tuple为不可变集合,dict可对应map。
通过help(list)
可以注意到list类自身有以下常见的方法:
dict中的函数有
python中通过heapq的lib来实现priority queue。
队列和栈都可以通过list来实现,既然list通用性这么强,效率自然是有所牺牲的。为了提升数据的处理效率,一些高效的数据结构放在了collections
中。如deque
类。
官方文档中的描述为:
This module implements specialized container datatypes providing alternatives to Python's general purpose built-in containers, dict,list, set, and tuple.
主要有以下的一些包装类
* namedtuple factory function for creating tuple subclasses with named fields
* deque list-like container with fast appends and pops on either end
* ChainMap dict-like class for creating a single view of multiple mappings
* Counter dict subclass for counting hashable objects
* OrderedDict dict subclass that remembers the order entries were added
* defaultdict dict subclass that calls a factory function to supply missing values
* UserDict wrapper around dictionary objects for easier dict subclassing
* UserList wrapper around list objects for easier list subclassing
* UserString wrapper around string objects for easier string subclassing
具体的一些用法自己摸索完全没问题的。这个类的意义就是提供一些更高效的容器,当然可能用起来不如内置的容器方便,但可以确定的是当我们更注重运行效率而不是自己的开发效率时最好首先考虑使用collections类中的元素,这时再不用的话其它时候更用不到了。