排序
ListNode *AddToTail(ListNode *pHead, int value) {
ListNode *pNew = new ListNode();
pNew->val = value;
pNew->next = nullptr;
if (pHead == nullptr) {
return pNew;
} else {
ListNode *p = pHead;
while(p->next) {
p = p->next;
}
p->next = pNew;
}
return pHead;
}
void PrintListReversingly(ListNode *head) {
std::stack s;
ListNode *p = head;
while(p != nullptr) {
s.push(p->val);
p = p->next;
}
while(!s.empty()) {
printf("%d\t", s.top());
s.pop();
}
}
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(l1 == NULL) return l2;
if(l2 == NULL) return l1;
if(l1->val < l2->val) {
l1->next=mergeTwoLists(l1->next,l2);
return l1;
} else {
l2->next=mergeTwoLists(l1,l2->next);
return l2;
}
}
};
struct ListNode* ReverseList(struct ListNode* pHead ) {
// write code here
if(pHead == NULL || pHead->next == NULL) return pHead;
struct ListNode* p = ReverseList(pHead->next);
pHead->next->next = pHead;
pHead->next = NULL;
return p;
}
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode *pre = nullptr, *cur = pHead, *last;
while(cur) {
last = cur->next;
cur->next = pre;
pre = cur;
cur = last;
}
return pre;
}
};
class Solution {
public:
ListNode *reverse(ListNode *head) {
ListNode *p = head, *q, *pre = NULL;
while(p){
q = p->next;
p->next = pre;
pre = p;
p = q;
}
return pre;
}
bool isPalindrome(ListNode* head) {
ListNode *fast = head, *last = head;
while (fast && fast->next) {
last = last->next;
fast = fast->next;
fast = fast->next;
}
if (fast) last = last->next;
last = reverse(last);
fast = head;
while(last && fast) {
if (last->val != fast->val) return false;
fast = fast->next;
last = last->next;
}
return true;
}
};
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) {
struct ListNode *p = headA, *q = headB;
while (p != q) {
p = p ? p ->next : headB;
q = q ? q ->next : headA;
}
return p;
}
快指针p, 慢指针q;
环的起始位置为o, 相遇位置为x, 从起点到环的起始位置长la, 环长lc;1.la < lc
当慢指针q到环的起始位置o时,慢指针q走了la长,快指针p已经走了2*la,此时快指针距离起始位置o还有lb, 得la + lb = lc,现在两者都在环上, 接下来的运动轨迹,可以视为追击问题, 每次运动,快指针p与慢指针q的距离-1,相遇需运动lb次,此时慢指针q走了lb, 在往后走la步可到环的起点(注:la + lb = lc);相遇后让快指针p到起点,此时p距离环的起始位置la,q距离环的起始位置也是la,p, q同时运动,每次移动一个单元距离,相遇的点为环的起始位置.此时慢指针的移动长度为环的长度.
bool hasCycle(struct ListNode *head) {
struct ListNode *q = head, *p = head;
if (p == NULL) return false;
do {
if (q->next == NULL || q->next->next == NULL) return false;
p = p->next;
q = q->next;
q = q->next;
} while (p != q);
return true;
}
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *p = head, *q = head;
if (p == NULL) return NULL;
int p_sum = 0;
do {
p =p->next;
p_sum += 1;
q =q->next;
if (q == NULL || q->next == NULL) return 0;
q =q->next;
} while (p != q);
q = head;
int q_sum = 0;
while(p != q) {
p_sum += 1;
q_sum += 1;
p = p->next;
q = q->next;
}
return p_sum - q_sum;
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *p = head, *q = head;
if (p == NULL) return NULL;
do {
p =p->next;
q =q->next;
if (q == NULL || q->next == NULL) return false;
q =q->next;
} while (p != q);
q = head;
while(p != q) {
p = p->next;
q = q->next;
}
return p;
}
用的是链表的思维
int init(int n) {
int temp = n, sum = 0;
while (temp) {
sum += (temp % 10) * (temp % 10);
temp /= 10;
}
return sum;
}
bool isHappy(int n){
int p = n, q = n;
while (p - 1) {
p = init(p);
q = init(init(q));
if (!(p - q)) break;
}
return (p == 1);
}
1.基础排序开辟一个可容k个有序数组所有元素的结果空间.让后按传统的排序进行排序.
2.小顶堆开辟一个可存k个数组所有元素的结果空间
因为k 个数组都是升序序列,第一个元素是数组的最小值,将所有数组的第一个元素维护成一个小顶堆,调整后的小顶堆的根节点是最小值,将根节点从其数组弹出,存入到结果空间去.将弹出来的元素所在数组的首元素添加到小顶堆,不断的谈出元素, 在往小顶堆中添加元素, 直到数组中没有元素为止,当数组没有元素时, 往小顶堆中添加一个极大值,这样小顶堆经调整后这个极大值会成为子节点, 不会被弹出.直到结果空间存满为止.
小顶堆:时间复杂度o(nlogk)
通过外部设备多路共同运行的多并发程序,工作效率提高,归并排序可通过采用分治,将数据分成若干份小数据,并存在外存上.
两个数据合并时,只需将首元素存于内存中便可,对内存的需求较小,合并后的结果也是存于外存上.
排序名 | 时间复杂度 |
---|---|
大根堆 | o(nlogk) |
小根堆 | o(n + klogn) |
BFPRT (快速选择算法) | o(n) |
快排 | 最优o(nlogn), 最差o(n2) |
大根堆:(前k小)选k个数构造大根堆,经调整后根为最大值,遍历数组,如果小于根的值, 则弹出根,将此元素加到堆中,直到数组遍历完为止.最后得到的堆的根就是第k小的元素
小根堆:
线性建堆法创建小根堆,时间复杂度o(n),弹k次,每次堆的调整的复杂度是o(n),所以总的时间复杂度是n + klog(n),第k次得到就是第k小的元素.
bfprt解法:
O(n)
bfprt解法和常规解法唯一不同的就是在number的选取上,其他地方一模一样,所以我们只讲选取number这一过程。
第一步:我们将数组每5个相邻的数分成一组,后面的数如果不够5个数也分成一组。
第二步:对于每组数,我们找出这5个数的中位数,将所有组的中位数构成一个median数组(中位数数组)。
第三步:我们再求这个中位数数组中的中位数,此时所求出的中位数就是那个number。
第四步:以 number为分界点,把小于number的放在左边,大于number的放在右边;
第五步: 判断主元的位置与 k 的大小,有选择的对左边或右边递归。
1.直接全部排序(只适用于内存够的情况)
当数据量较小的情况下,内存中可以容纳所有数据。则最简单也是最容易想到的方法是将数据全部排序,然后取排序后的数据中的前K个。
这种方法对数据量比较敏感,当数据量较大的情况下,内存不能完全容纳全部数据,这种方法便不适应了。即使内存能够满足要求,该方法将全部数据都排序了,而题目只要求找出top K个数据,所以该方法并不十分高效,不建议使用。
2.快速排序的变形 (只使用于内存够的情况)
这是一个基于快速排序的变形,因为第一种方法中说到将所有元素都排序并不十分高效,只需要找出前K个最大的就行。
这种方法类似于快速排序,首先选择一个划分元,将比这个划分元大的元素放到它的前面,比划分元小的元素放到它的后面,此时完成了一趟排序。如果此时这个划分元的序号index刚好等于K,那么这个划分元以及它左边的数,刚好就是前K个最大的元素;如果index > K,那么前K大的数据在index的左边,那么就继续递归的从index-1个数中进行一趟排序;如果index < K,那么再从划分元的右边继续进行排序,直到找到序号index刚好等于K为止。再将前K个数进行排序后,返回Top K个元素。这种方法就避免了对除了Top K个元素以外的数据进行排序所带来的不必要的开销。
3.最小堆法
这是一种局部淘汰法。先读取前K个数,建立一个最小堆。然后将剩余的所有数字依次与最小堆的堆顶进行比较,如果小于或等于堆顶数据,则继续比较下一个;否则,删除堆顶元素,并将新数据插入堆中,重新调整最小堆。当遍历完全部数据后,最小堆中的数据即为最大的K个数。
4.分治法
将全部数据分成N份,前提是每份的数据都可以读到内存中进行处理,找到每份数据中最大的K个数。此时剩下NK个数据,如果内存不能容纳NK个数据,则再继续分治处理,分成M份,找出每份数据中最大的K个数,如果M*K个数仍然不能读到内存中,则继续分治处理。直到剩余的数可以读入内存中,那么可以对这些数使用快速排序的变形或者归并排序进行处理。
5.Hash法
如果这些数据中有很多重复的数据,可以先通过hash法,把重复的数去掉。这样如果重复率很高的话,会减少很大的内存用量,从而缩小运算空间。处理后的数据如果能够读入内存,则可以直接排序;否则可以使用分治法或者最小堆法来处理数据。
1.如果接收的数据量小于k,则依次放入蓄水池。2.当接收到第i个数据时,i > k,在[0, i]范围内取以随机数d,若d的落在[0, k]范围内,则用接收到的第i个数据替换蓄水池中的第d个数据, 如果落在[k + 1, i]范围内则继续接受数据。
证:
假设当有i - 1个元素时,每个元素被选中的概率为 k i − 1 \frac{k}{i - 1} i−1k,当第i个元素出现后,第i 个元素想被放入蓄水池内,就必须从蓄水池内拿出一个来(蓄水池有k个元素).因此第i 个元素能放入蓄水池的概率为 k i \frac{k}{i} ik,而前i - 1个元素此时能在蓄水池内的概率是多少呢?
此时前i - 1个元素想要在蓄水池内,有两个条件要满足:一是该元素在第i 个元素出现之前就在蓄水池内,概率为 k i − 1 \frac{k}{i - 1} i−1k;二是在第i个元素出现后,该元素不被替换(也就是不被取出来).假设该元素的下标为l(l <= k).不被替换又分两种情况:1.随机选中的元素下标在[k + 1, i],不用从蓄水池内替换元素,继续接受数据,概率为 i − k i \frac{i - k}{i} ii−k; 2.随机选中的元素的下标d在[1, k],但不是该元素(下标l的元素)的下标(l != d),因此将第i个元素会将下标为d的元素替换掉,概率为 k − 1 i \frac{k-1}{i} ik−1.
因此可得 p l = k i − 1 × ( k − 1 i + i − k i ) = k i p_l=\frac{k}{i-1}\times(\frac{k-1}{i}+\frac{i - k}{i})=\frac{k}{i} pl=i−1k×(ik−1+ii−k)=ik.综上可知每个元素被选中的概率是等概的.
大数据场景下的采样问题数据流采样问题
1.创建一个一百位的链表,;2.将1~100插入链表;
3.在链表的下标中随机生成一个下标;4.输出该下标对应的数字,;
5.删除该点,6.重复3~5步,直到链表的值全部输出.
x j + 1 = ( a x j + b ) x_j+1= (ax_j+b) % m xj+1=(axj+b)
例:4k % 7
k = 1, 4 % 7 = 4;k = 2, 42 % 7 = 2;
k = 3, 43 % 7 = 1;k = 4, 44 % 7 = 4;
k = 5, 45 % 7 = 2;…
我们会得到一个{4, 2, 1}的剩余系
要得到1~100的剩余系,需用公式3k %101;
#include
#include
#include
#include
#include
#include
using namespace std;
int my_rand() {
static int seed = 1;
seed = seed * 3 % 101;
return seed;
}
int main() {
for (int i = 1; i <= 100; i++) cout << my_rand() << endl;
return 0;
}
srand:获取随机种子rand:获取随机值
#include
#include
#include
#include
#include
#include
using namespace std;
int main() {
//起点随时间的变化而变化 srand(time(0));
for (int i = 0; i < 10; i++) {
cout << rand() % 10 << endl;
}
cout << endl; //起点为srand设置的固定起点
srand(4);
for (int i = 0; i < 10; i++) {
cout << rand() << endl;
}
cout << endl; //起点为系统的默认起点
for (int i = 0; i < 10; i++) {
cout << rand() << endl;
}
return 0;
}
数字出现的次数
思路:删除抵消: 遍历数组,将两个数值不同的数字相抵消,最后剩下的那个数字便是要求的数字.
代码实现
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAX_N 10000int num[MAX_N + 5];
int n;
void read() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> num[i];
}
return ;
}
bool checkMoreThanHalf(int res) {
int times = 0;
for (int i = 0; i < n; i++) {
if (num[i] == res) times += 1;
}
if (times * 2 <= n) return false;
return true;
}
int solve() {
int res = -1, ans = 0;
if (num == NULL || n <= 0) return res;
for (int i = 0; i < n; i++) {
if (ans == 0) {
res = num[i];
ans = 1;
} else if (res != num[i]) {
ans -= 1;
} else {
ans += 1;
}
}
if (!checkMoreThanHalf(res)) res = -1;
return res;
}
int main() {
read();
cout << solve() << endl;
return 0;
}
**思路分析:**摩尔投票法:
维护k个元素,遍历数组, 当元素储存的储存数量为0时,将该元素储存进去, 当该元素储存的数量不为0时,则将该元素的储存数量加1, 则当储存k个不同的元素时,k个元素相抵消,即k个元素都减1,不断地储存和抵消, 最后遍历储存的元素,找到数量最多的元素.
代码实现
#include
#include
#include
#include
#include
#include
#include
转换成二进制, 找到每个位的和不为3的位, 和不为3的位便是要找的数的二进制位为1的位.
代码演示
#include
using namespace std;
int main() {
int n, c, a = 0, b = 0;
cin >> n;
while (n--) {
cin >> c;
int a1 = (a & ~b & ~c) | (~a & b & c);
int b1 = (~a & b & ~c) | (~a & ~b & c);
a = a1;
b = b1;
}
cout << (a | b) << endl;
return 0;
}
思路:1.在栈NewStack里添加一个用于储存最小值, 次小值的栈m_stack.
2.当往栈NewStack里插入元素时, 先将元素的值和压入的时间压入栈NewStack,如果栈m_stack是空的,或者该元素比栈m_stack的栈顶元素的值小时, 将该元素压入到my_stack中存放起来,此时m_stack的栈顶元素的值就是栈NewStack的最小值.3.当NewStack弹栈时,比较NewStack 弹出元素的时间和辅助栈m_stack栈顶元素的时间是否一致, 一致证明是同一元素,需将该元素弹出辅助栈.
4.NewStack的最小值就辅助栈m_stack的栈顶元素.
代码实现
include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef pair PII;
struct NewStack : stack {
public:
NewStack() {
this->t == 0;
}
void push(int val);
void pop();
int min();
private:
int t;
stack m_stack;
};
void NewStack::push(int val) {
PII data(val, t++);
this->stack ::push(data);
if (m_stack.empty() || val < m_stack.top().first) {
m_stack .push(data);
}
return ;
}
void NewStack::pop() {
if (this->empty()) return ;
if (this->top().second == m_stack.top().second) {
m_stack.pop();
}
this->stack::pop();
return ;
}
int NewStack::min() {
if (this->empty()) return 0;
return m_stack.top().first;
}
int main() {
NewStack s;
int op, val;
while (cin >> op) {
switch (op) {
case 0: {
cin >> val;
s.push(val);
}break;
case 1:{
cin >> val;
s.pop();
}break;
case 2:
cout << s.min() << endl;
break;
}
}
return 0;
}
思路用滑动窗口来维护两个单调队列,维护获取每k个位置的最大值max和最小值min, 如果max - min == k - 1; 那么这k个位置便是要找的连续k个位置.
代码实现
#include
#include
#include
#include
#include
#include
using namespace std;
#define MAX_N 300000int max_q[MAX_N + 5], min_q[MAX_N + 5];
int max_head = 0, max_tail = 0, min_head = 0, min_tail = 0;
int val[MAX_N + 5];
int main() {
int n,k;
cin >> n >> k;
for (int i = 1; i <= n; i++) {
cin >> val[i];
}
int min_loc, max_loc;
for (int i = 1; i <= n; i++) {
while(min_tail - min_head && val[min_q[min_tail - 1]] > val[i]) min_tail--;
while(max_tail - max_head && val[max_q[max_tail - 1]] < val[i]) max_tail--;
min_q[min_tail++] = i;
max_q[max_tail++] = i;
if (min_q[min_head] <= i - k) min_head++;
if (max_q[max_head] <= i - k) max_head++;
if (i >= k) {
if (val[max_q[max_head]] - val[min_q[min_head]] == k - 1) {
min_loc = min_q[min_head];
max_loc = max_q[max_head];
break;
}
}
}
int s = min(min_loc, max_loc);
cout << "下标为: " << s << " ~ " << s + k - 1 << endl;
return 0;
}
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
if (head == nullptr || k == 0) return nullptr; ListNode *q = head;
int n = 0;
while(q) {
q = q->next;
n++;
}
if (k > n) return nullptr;
int l = n - k;
q = head;
while(l--) {
q = q->next;
}
return q;
}
};
class Solution {
public:
ListNode* getKthFromEnd(ListNode* head, int k) {
if (head == nullptr || k == 0) return nullptr;
ListNode *q = head, *p = head;
while(--k) {
q = q->next;
if (q == nullptr) return nullptr;
}
while(q->next) {
q = q->next;
p = p->next;
}
return p;
}
};
请实现一个函数, 把字符串中的每个空格替换成"%20".
class Solution {
public:
string replaceSpace(string s) {
int oldlength = s.size();
if (oldlength == 0) return s;
int black_num = 0;
for (int i = 0; i < oldlength; i++) {
if (s[i] == ' ') black_num += 1;
}
int newlength = oldlength + black_num * 2;
s.resize(newlength);
for (int j = newlength - 1, i = oldlength - 1; j > i && i >= 0; i--) {
if (s[i] == ' ') {
s[j--] = '0';
s[j--] = '2';
s[j--] = '%';
} else {
s[j--] = s[i];
}
}
return s;
}
};
有额外空间
class Solution {
public:
void replaceSpace(char s[], int oldlength) {
if (s == nullptr || oldlength <= 0) return ;
int black_num = 0;
for (int i = 0; i < oldlength; i++) {
if (s[i] == ' ') black_num += 1;
}
int newlength = oldlength + black_num * 2;
if (newlength > oldlength) return ;
for (int j = newlength - 1, i = oldlength - 1; j > i && i >= 0; i--) {
if (s[i] == ' ') {
s[j--] = '0';
s[j--] = '2';
s[j--] = '%';
} else {
s[j--] = s[i];
}
}
return ;
}
};
思路
1.我们可以从前序遍历中, 找到根节点, 即前序遍历的第一个数字的值就是根节点的值.
2.知道根节点的值后, 在后序遍历中遍历, 由于数字都是不重复的, 因此我们能很容易地找到根节点在后续遍历的位置.
3.找到根节点在后续遍历的位置ind后, 在后续遍历中0~ind是左子树, ind ~ n(n为后续遍历的长度)为右子树.同时我们可知左右子树的节点个数.以及左右字数后序遍历.
4. 在前序遍历中, 根节点后面的ind个节点是左字数,这样我们也可知道了左子树和右子树前序遍历.
5.既然我们已经得知了左右子树的前序遍历和后序遍历, 通过同样的方法去分别构建左右子树.
代码实现
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
unordered_map loc;
TreeNode* buildTree(vector& preorder, vector& inorder) {
int n = preorder.size(), m = inorder.size();
if (n <= 0 || m <= 0 || n != m) return nullptr;
for (int i = 0; i < m; i++) {
loc[inorder[i]] = i;
}
return buildTree_func(preorder, 0, 0, m - 1);
}
TreeNode *buildTree_func(vector& preorder, int preRootLoc, int l, int r) {
if (l > r) return nullptr;
TreeNode *root = new TreeNode();
root->val = preorder[preRootLoc];
int ind = loc[root->val];
int l_num = ind - l;
root->left = buildTree_func(preorder, preRootLoc + 1, l, ind - 1);
root->right = buildTree_func(preorder, preRootLoc + l_num + 1, ind + 1, r);
return root;
}
};
思路
1.如果一个节点有右节点那么它的下一个节点就是它的右子树的最左子节点.
2.如果一个节点没有右节点, 但是有父亲节点(即是子节点不是根节点), 且它为它的父亲节点的左子节点, 那么它的下一个节点是它的父亲节点
3.如果一个节点没有右节点, 但是有父亲节点(即是子节点不是根节点), 且它为它的父亲节点的右子节点, 那么我们需要沿着指向父节点的指针一直向上
遍历, 直到找到一个是它父节点的左子节点的节点.如果这样的节点存在, 那么这个节点的父节点就是我们要找的下一个节点.
代码实现
typedef struct TreeNode{
int val;
struct TreeNode *left, *right, *parent;
} TreeNode;
TreeNode *getNext(TreeNode *Node) {
if (Node == NULL || (Node->parent == NULL && Node->right == NULL)) return NULL;
TreeNode *nextNode = NULL;
if (Node->right != NULL) {
TreeNode *nodeRight = Node->right;
while(nodeRight->left != NULL) {
nodeRight = nodeRight->left;
}
nextNode = nodeRight;
} else {
TreeNode *curNode = Node;
TreeNode *parent = Node->parent;
while((parent != NULL) && (curNode == parent->right)) {
curNode = parent;
parent = parent->parent;
}
nextNode = parent;
}
return nextNode;
}
思路
1.创建两个栈, 分别为stack1, stack2.
2.当队列要插入元素时, 往stack1添加元素.
3.当队列要删除一个元素时,按照队列的先进先出的原则, 如果栈stack2中有元素时那么就将stack2栈顶的元素弹出,如果栈stack2中没有元素时, 而栈stack1中有元素时, 我们将stack1的元素依次弹到stack2中, 这样stack1为空,而stack2的栈顶元素正好时我们要弹的元素,将此值弹出.如果两个栈都为空, 说明队列没有元素.
代码实现
class CQueue {
public:
CQueue() {
}
void appendTail(int value) {
this->stack1.push(value);
}
int deleteHead() {
if (this->stack2.empty()) {
while(!this->stack1.empty()) {
this->stack2.push(this->stack1.top());
this->stack1.pop();
}
}
if (this->stack2.empty()) return -1;
int head = this->stack2.top();
this->stack2.pop();
return head;
}
private:
stack stack1;
stack stack2;
};
解题思路
根据栈的先入后出的原则,最后被压入栈的元素先被弹出,我们可以设置两个队列q1, q2, 每次将元素压栈时, 将元素添加到q1中, 弹出时将除了q1的最后一个元素的其他元素依次从q1中弹入到q2中去, 然后q1中的剩下的那个元素就是栈顶元素,弹出即可, 然后交换q1和q2.q1的队尾元素就是栈顶元素.判断栈是否为空, 因q2是空的, 只需判断q1即可.
class MyStack {
public:
MyStack() {
}
void push(int x) {
que1.push(x);
}
int pop() {
if(empty()) return -1;
while(que1.size() > 1) {
que2.push(que1.front());
que1.pop();
}
int top = que1.front();
que1.pop();
swap(que1, que2);
return top;
}
/** Get the top element. */
int top() {
return que1.back();
}
/** Returns whether the stack is empty. */
bool empty() {
return que1.empty();
}
private:
queue que1;
queue que2;
};
时间复杂度 O ( n ) O(n) O(n)
long long Fibonaqie(int n) {
if (n < 2) return n;
long long fibOne = 0;
long long fibTwo = 1;
long long fibN = 1;
for (int i = 2; i <= n; i++) {
fibN = fibOne + fibTwo;
fibOne = fibTwo;
fibTwo = fibN;
}
return fibN;
}
青蛙跳台阶问题
一只青蛙一次可以跳1级台阶, 也可以跳上2级台阶, 求该青蛙跳上一个n级的台阶共有多少种跳法.
同样是斐波那契数列f(n ) = f(n -1) + f(n -2);
如果把条件改成:一只青蛙跳可以上一级台阶, 也可以跳两级台阶…它也可以跳n级台阶, 则此时该青蛙跳上一个n级的台阶共有多少种跳法 ?
f ( n ) f(n) f(n) = 2n-1.
找n 个数之和为S的n个数
map | unordered_map | |
---|---|---|
底层实现 | 红黑树 | 哈希表 |
复杂度 | O ( l o g n ) O(logn) O(logn) | O ( c ) O(c) O(c) |
有序性 | 有序 | 无序 |
稳定性 | 稳定 | 不稳定 |
效率 | 查询,插入, 删除慢 | 查找, 删除, 插入快 |
栈溢出;
堆与栈的区别;
两栈实一队列;
堆,大根堆,小根堆
数组和链表的区别