11 篇文章 0 订阅

# <数据结构与算法>学习笔记（一）基础知识-基本数据结构

## 7.常见容器学习

### 1. C++

array （c++11)setunordered_set(c++11起)stack(LIFO)栈
vectormapunordered_map(c++11起)queue(FIFO)队列
dequemultisetunordered_multiset(c++11起)priority_queue(优先级)
forward_list (c++11起)multimapunordered_multimap(c++11起)
list

stack必须提供函数：back()、push_back()、pop_back()vector、deque（默认）和list
queueback()、front()、push_back()、pop_front()deque和list
priority_queue迭代器必须满足遗留随机访问迭代器的要求，front()、push_back()、pop_back()vector和deque

#### 1.顺序容器

#include<array>
#include<iostream>
#include<string>
#include<algorithm>
#include<iterator>
#include<vector>
#include<deque>
#include <forward_list>
#include <list>
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。



#### 2.关联容器

set：含有key类型的已排序集，用比较函数进行排序。搜素、移除和插入拥有对数复杂度。set通常以红黑树来实现。

set按照键排序，所以不会出现相等的元素。

map：有序键值对容器，元素的键是唯一的，用比较函数排序键。map通常也用红黑树来实现。

mutlisetmultimap是set和map对应的键值不唯一的，相等的键的排列顺序按照插入顺序，且不会改变。

#include<iostream>
#include<string>
#include<algorithm>
#include<iterator>
#include <set>
#include <map>
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::set<int>s4(s1);
//std::set<int>s5(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。



#### 3. 无序关联容器

unordered_set：键值唯一，搜索、插入和移除具有平均常数时间复杂度。在内部元素不以特别的顺序排列，而是组织进桶中。元素被放进那个同依赖于其值的哈希。这允许对单独元素的快速访问。但不可修改容器元素。注意无序容器比较特殊，如果要用这一容器来装自定义的某个类，要么自定义一个这个类的散列函数然后再容器声明时使用，或者使用注入的std::hash特化。

#include<iostream>
#include<string>
#include<algorithm>
#include<iterator>
#include <unordered_map>
#include <unordered_set>
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);
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

3605745226
unordered_map
Suika,id:1:1
Yuka,id:2:2
1:2
2:3
3:4
5:6
7:2
6:6
5:6
7:2
6:6
8
0

3605745226

C:\Users\xhh\Source\Repos\c++study\queue_S\Debug\queue_S.exe (进程 61952)已退出，代码为 0。



#### 4.容器适配器

stack ：栈，先进后出。

queue：队列，先进先出

priority_queue：优先队列，提供常数时间的最大元素查找，对数代价的插入与释出。可用用户提供的compare更改顺序。

#include<iostream>
#include<stack>
#include <queue>
#include <vector>
#include <list>
#include <deque>

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，堆，通常指二叉堆，二叉堆是一个近似完全二叉树的数据结构。子节点的键值总是小于（或者大于）它的父节点，且每个节点的左右子树又是一个二叉堆（大根堆或者小根堆）。根节点最大的堆叫做最大堆或大根堆，根节点最小的堆叫做最小堆或者小根堆。常用作实现优先队列。

• 以数组表示，以完全二叉树的方式理解
• 唯一能同时最优的利用时间和空间的方法
• 索引从0开始，父节点i的左子节点位置：2*i+1，右子节点位置2*i+2，子节点i的父节点位置floor((i-1)/2)

• 最大堆调整：将堆的末端子节点作调整，使得子节点永远小于父节点
• 创建最大堆：将堆所有数据重新排序
• 堆排序：移除位于第一个数据的根节点，并作最大堆调整的递归运算
#pragma once
#include<algorithm>
#include<functional>
#include<stdexcept>
#include<unordered_map>
#include<utility>
#include<vector>

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<iostream>
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;

}


### 2. Java

Java集合类主要有：Set、Queue、List、Map四种体系。和C++中的相关的容器类相对应，用途大概都能想到，但实际用法和C++还是有很大区别的，毕竟是不同的语言。Java的集合类主要由两个接口派生而来：CollectionMap，他们是集合框架的根接口，两个接口又包含一些子接口或实现类。它们的继承树分别如下：

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.


CollectionMap都是泛型接口。当然，Java里面实例化泛型类时可以不用尖括号显示指定类型，这样一般容器中元素的类型都是Object的，这样虽然没有语法错误，但IDEA会给出警告的，因为这样可能会出现一些问题，一般还是用棱形语法指定容器内元素类型比较好。

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();
System.out.println("test集合的元素个数"+test.size());
test.remove(122);
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
1232
11
a
-1232
-a
1232
1
2


#### 1. Set

Set接口不允许添加相同的元素，其它基本与Collect相同。而HashSet是它的典型实现，通常用HashSet来作为Set的实现类。

HashSet的特点：

• 元素的顺序不与添加顺序相同
• 不是同步的，多个线程同时访问时需要通过代码保证其同步
• 集合元素可以是null

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<>();
}
}


LinkedHashSet是HashSet的子类，它同时用链表来维护元素的次序，这样，遍历时它会按元素的添加顺序来访问集合的元素，虽然维护插入顺序会降低它的性能，但遍历元素时它具有更好的性能。

TreeSetSortedSet接口的实现类。它确保集合元素处于排序状态。相比较HashSet它提供了一些额外的方法。TreeSet是采用红黑树的数据结构来存储集合元素，我们知道红黑树是需要比较两个元素的大小的，它支持两种排序方法：自然排序定制排序，默认采用自然排序。

自然排序：调用元素的compareTo(Object o)方法来比较元素之间的大小，然后将元素按升序排列。该方法返回一个int类型，相等则返回0，小于则返回负整数，大于则返回正整数。

Java提供了一个Comparable接口，该接口中定义了compareTo(Object o)的方法，相当于定义了一个规范。所以如果要让TreeSet的元素为自定义的类型，简单的做法是让这个自定义的类型实现Comparable接口，如果该类没有实现Comparable接口，添加元素时会引发ClassCaseException异常。Java中常用的一些类自然是实现了该接口的，如：BigDecimalBigIntegerCharacterBooleanStringDateTime等。

定制排序：自然排序时这个类型通过实现Comparable接口实现，如果不想让类实现Comparable接口，也可以在实例化TreeSet对象时提供一个Comparator对象与TreeSet集合关联，可以用Lambda表达式来代替Comparator。

EnumSet类：枚举类的集合类。其中的元素都是指定的枚举类型的枚举值。EnumSet的集合元素是有序的，以枚举值在枚举类中的定义顺序来决定集合元素的顺序。EnumSet内部通过位向量的形式来存储，它占用内存小，运行效率高，尤其在进行批量操作时。

EnumSet不允许插入null元素。

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(id<t.id)
return -1;
if(id>t.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<>();
System.out.println(s);
s.forEach(o->System.out.println(o));
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表达式
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.tailSet(t));//返回子集包含t之后的元素
//EnumSet
EnumSet<Week> ew=EnumSet.allOf(Week.class);
System.out.println(ew);
EnumSet<Week>ew1=EnumSet.noneOf(Week.class);
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.collectionStudyLambda\$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]


#### 2. List集合

List集合：有序、可重复集合，可通过索引访问指定位置的元素。

ArrayListVectorList接口的两个典型实现。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();
System.out.println(l);
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<>();
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

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]


#### 3.Queue集合

Queue接口模拟队列，它有一个PriorityQueue实现类，它还有一个Deque接口，代表一个双端队列，可同时从两端添加删除元素。Deque的实现类即可以当成队列使用也可以当成栈来使用。两个实现类：ArrayDequeLinkedList

PriorityQueue会按元素的大小将元素排序。

Deque方法对比：

QueueDequeStackDeque
remove、pollremoveFirst、pollFirstpopremoveFirst、pollFirst
element、peekgetFirst、peekFirstpeekgetFirst、peekFirst

package algo_study;

import java.util.ArrayDeque;
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(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.offer(112);//入队
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());//出队
}
//栈，和ArrayList的方法名相同，还是很不错的
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.offer(112);//入队
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


#### 4. Map

Map接口下有：HashMapLinkedHashMapSortedMap（接口）、TreeSetEnumSet等子接口和实现类，正好是和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());
System.out.println(pro2);
System.out.println(pro2.get("QQ"));
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);
System.out.println(tmi);
//identityHashMap
IdentityHashMap id=new IdentityHashMap();
id.put(m,111);
id.put(new mapTest(1,"aaa"),12);
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=周二}


#### 5. Collections工具类

1. 改变List元素次序的方法：
1. void reverse(List list)
2. void shuffle(List list): 对list集合元素进行随机排序（模拟“洗牌”行为）
3. void sort(List list)：根据元素的自然排序对集合元素进行升序
4. void sort(List list,Comparator c)：根据指定的Comparator排序
5. void swap(List list ,int i,int j)：将i处元素和j处元素进行交换
6. void rotate(List list,int distance)：distance为正时，将结合后distance个元素整体移动到前面，否则将前distance个元素整体移动到后面
2. 查找、替换操作
1. int binarySearch(List,list,Object key)：用二分法搜索指定的List集合，返回指定对象的索引，必须确保List中元素处于有序状态
2. Object max(Collection coll)：根据元素自然排序返回集合中的最大元素
3. Object max(Collection co,Comparator c)：~
4. Object min(Collection co)
5. Object min(Collection co,Comparator c)
6. void fill(List l,Object o)：用指定元素O替换集合中的所有元素
7. int frequency(Collection c,Object O)：返回O的出现次数
8. int indexOfSubList(List source,List target)：返回子List对象在父List中首次出现的位置，没有则返回-1
9. boolean replaceAll(List list,Object oldVal,Object newVal)：用一个新值替换所有的旧值
3. 同步控制：synchronizedXXX()方法
package algo_study;

import java.util.*;

public class collectionsLearn {
public static void main(String[] args) {
//改变List中元素次序的操作
List l=new ArrayList();
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);
//查找、替换
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);
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]


### 3. python

python内置的容器主要有三个listtupledict，这三个list最常用，我反正能用list的基本都用list，list本身很随意，可以任意添加元素，元素也可以是不同类型的，元素也可以是list、tuple、dict。

1. list

• append
• clear
• copy
• count
• extend
• index
• insert
• pop
• remove
• reverse
• sort
1. tuple
2. dict

dict中的函数有

• clear
• copy
• get
• items
• keys
• pop
• popitem
• setdefault
• update
• values

python中通过heapq的lib来实现priority queue。

#### collections

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


03-08 3万+

11-30 841
06-14 981
02-13 783
07-10 5085
07-12 562
01-28 1692
07-27 5532
11-09 1万+
04-03 3317
06-24 44

### “相关推荐”对你有帮助么？

• 非常没帮助
• 没帮助
• 一般
• 有帮助
• 非常有帮助

xhh22900

¥2 ¥4 ¥6 ¥10 ¥20

1.余额是钱包充值的虚拟货币，按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载，可以购买VIP、C币套餐、付费专栏及课程。