常用基本数据结构
基本数据结构
链表手写
动态链表
叫到第m的人淘汰
struct node {
int data;
node *next;
}; 动态链表实现,需要管理空间
int main() {
int n, m; scanf("%d %d", &n, &m);
node *head, *p, *now, *prev; //定义变量
head = new node; head->data = 1; head->next = NULL; //分配第一个结点,数据置为1
now = head; //当前指针是头
for (int i = 2; i <= n; i++) {
p = new node; p->data = i; p->next = NULL; //p是新结点
now->next = p; //把申请的新结点连到前面的链表上
now = p; //尾指针后移一个
}
now->next = head; //尾指针指向头:循环链表建立完成
//以上是建立链表,下面是本题的逻辑和流程。后面4种代码,逻辑流程完全一致。
//////////////////////////////////////////////////////////////////////////////////
now = head, prev = head; //从第1个开始数
while ((n--) > 1 ) {
for (int i = 1; i < m; i++) { //数到m,停下
prev = now; //记录上一个位置,用于下面跳过第m个结点
now = now->next;
}
printf("%d ", now->data); //输出第m结点,带空格
prev->next = now->next; //跳过这个结点
delete now; //释放结点
now = prev->next; //新的一轮
}
printf("%d", now->data); //打印最后一个,后面不带空格
delete now; //释放最后一个结点
return 0;
}
静态链表
分配连续空间
结构体
const int N = 105;
struct node {
int id, nextid;
//int data随缘定义
//node *next;无动态
} nodes[N];
双向
const int N = 105;
struct node {
int id,nextid,previd;
//int data;
//node *next;
} nodes[N];
//show
int main() {
int n, m; scanf("%d%d", &n, &m);
nodes[0].nextid = 1;
for (int i = 1; i <= n; i++) { //建立链表
nodes[i].id = i;
nodes[i].preid = i - 1; //前结点
nodes[i].nextid = i + 1; //后结点
}
nodes[n].nextid = 1; //循环链表:尾指向头
nodes[1].preid = n; //循环链表:头指向尾
int now = 1; //从第1个开始
while ((n--) > 1) {
for (int i = 1; i < m; i++) now = nodes[now].nextid; //数到m,停下
printf("%d ", nodes[now].id); //打印,后面带空格
int prev = nodes[now].preid, next = nodes[now].nextid;
nodes[prev].nextid = nodes[now].nextid; //删除now
nodes[next].preid = nodes[now].preid;
now = next; //新的开始
}
printf("%d", nodes[now].nextid); //打印最后一个,后面不带空格
return 0;
}
数组实现
nodes[i] = i + 1; nodes[i]的值就是下一个节点
STL list
list双向链表函数
int main(int argc, char const *argv[])
{
成员函数 功能
begin() 返回指向容器中第一个元素的双向迭代器。
end() 返回指向容器中最后一个元素所在位置的下一个位置的双向迭代器。
rbegin() 返回指向最后一个元素的反向双向迭代器。
rend() 返回指向第一个元素所在位置前一个位置的反向双向迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
empty() 判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。
size() 返回当前容器实际包含的元素个数。
max_size() 返回容器所能包含元素个数的最大值。这通常是一个很大的值,一般是232 - 1,所以我们很少会用到这个函数。
front() 返回第一个元素的引用。
back() 返回最后一个元素的引用。
assign() 用新元素替换容器中原有内容。
emplace_front() 在容器头部生成一个元素。该函数和 push_front() 的功能相同,但效率更高。
push_front() 在容器头部插入一个元素。
pop_front() 删除容器头部的一个元素。
emplace_back() 在容器尾部直接生成一个元素。该函数和 push_back() 的功能相同,但效率更高。
push_back() 在容器尾部插入一个元素。
pop_back() 删除容器尾部的一个元素。
emplace() 在容器中的指定位置插入元素。该函数和 insert() 功能相同,但效率更高。
insert() 在容器中的指定位置插入元素。
erase() 删除容器中一个或某区域内的元素。
swap() 交换两个容器中的元素,必须保证这两个容器中存储的元素类型是相同的。
resize() 调整容器的大小。
clear() 删除容器存储的所有元素。
splice() 将一个 list 容器中的元素插入到另一个容器的指定位置。
remove(val) 删除容器中所有等于 val 的元素。
remove_if() 删除容器中满足条件的元素。
unique() 删除容器中相邻的重复元素,只保留一个。
merge() 合并两个事先已排好序的 list 容器,并且合并之后的 list 容器依然是有序的。
sort() 通过更改容器中元素的位置,将它们进行排序。
reverse() 反转容器中元素的顺序。
return 0;
}
初始化
int main(int argc, char const *argv[])
{
1 创建一个没有任何元素的空 list 容器:
std::list<int> values;
和空 array 容器不同,空的 list 容器在创建之后仍可以添加元素,因此创建 list 容器的方式很常用。
2 创建一个包含 n 个元素的 list 容器:
std::list<int> values(10);
通过此方式创建 values 容器,其中包含 10 个元素,每个元素的值都为相应类型的默认值(int类型的默认值为 0)。
3 创建一个包含 n 个元素的 list 容器,并为每个元素指定初始值。例如:
std::list<int> values(10, 5);
如此就创建了一个包含 10 个元素并且值都为 5 个 values 容器。
4在已有 list 容器的情况下,通过拷贝该容器可以创建新的 list 容器。例如:
std::list<int> value1(10);
std::list<int> value2(value1);
注意,采用此方式,必须保证新旧容器存储的元素类型一致。
5 通过拷贝其他类型容器(或者普通数组)中指定区域内的元素,可以创建新的 list 容器。例如:
纯文本复制
//拷贝普通数组,创建list容器
int a[] = { 1, 2, 3, 4, 5 };
std::list<int> values(a, a + 5);
//拷贝其它类型的容器,创建 list 容器
std::array<int, 5>arr{ 11, 12, 13, 14, 15 };
std::list<int>values(arr.begin() + 2, arr.end()); //拷贝arr容器中的{13,14,15}
return 0;
}
队列
简单环境下手写队列
const int N = 1e5;
int que[N], head, tail; //size=tail-head+1
head++;//弹出头
que[head];//读取
que[++tail] = data; //入队尾指针加1
stl
int main(int argc, char const *argv[])
{
queue 和 stack 有一些成员函数相似,但在一些情况下,工作方式有些不同:
front():返回 queue 中第一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
back():返回 queue 中最后一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
push(const T & obj):在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
push(T && obj):以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。
pop():删除 queue 中的第一个元素。
size():返回 queue 中元素的个数。
empty():如果 queue 中没有元素的话,返回 true。
emplace():用传给 emplace() 的参数调用 T 的构造函数,在 queue 的尾部生成对象。
swap(queue<T> &other_q):将当前 queue 中的元素和参数 queue 中的元素交换。它们需要包含相同类型的元素。也可以调用全局函数模板 swap() 来完成同样的操作。
return 0;
}
字母的全排列
class Solution {
public List<String> letterCasePermutation(String s) {
List<String> ans = new ArrayList<String>();
Queue<StringBuilder> queue = new ArrayDeque<StringBuilder>();
queue.offer(new StringBuilder());
while (!queue.isEmpty()) {
StringBuilder curr = queue.peek();
if (curr.length() == s.length()) {
ans.add(curr.toString());
System.out.println(queue.poll());
} else {
int pos = curr.length();
if (Character.isLetter(s.charAt(pos))) {
StringBuilder next = new StringBuilder(curr);
next.append((char) (s.charAt(pos) ^ 32));
queue.offer(next);
}
curr.append(s.charAt(pos));
System.out.print(curr+" ");
}
}
return ans;
}
}
手写循环
#include<bits/stdc++.h>
#define N 1003 //队列大小
int Hash[N] = {0}; //用Hash检查内存中有没有单词
struct myqueue {
int data[N]; //分配静态空间
/* 如果动态分配,这样写: int *data; */
int head, rear; //队头、队尾
bool init() { //初始化
/*如果动态分配,这样写:
Q.data = (int *)malloc(N * sizeof(int)) ;
if(!Q.data) return false; */
head = rear = 0;
return true;
}
int size() { return (rear - head + N) % N;} //返回队列长度
bool empty() { //判断队列是否为空
if (size() == 0) return true;
else return false;
}
bool push(int e) { //队尾插入新元素。新的rear指向下一个空的位置
if ((rear + 1) % N == head ) return false; //队列满
data[rear] = e;
rear = (rear + 1) % N;
return true;
}
bool pop(int &e) { //删除队头元素,并返回它
if (head == rear) return false; //队列空
e = data[head];
head = (head + 1) % N;
return true;
}
int front() { return data[head]; } //返回队首,但是不删除
} Q;
int main() {
Q.init(); //初始化队列
int m, n; scanf("%d%d", &m, &n);
int cnt = 0;
while (n--) {
int en; scanf("%d", &en); //输入一个英文单词
if (!Hash[en]) { //如果内存中没有这个单词
++cnt;
Q.push(en); //单词进队列,放到队列尾部
Hash[en] = 1;
while (Q.size() > m) { //内存满了
int tmp; Q.pop(tmp); //删除队头
Hash[tmp] = 0; //从内存中去掉单词
}
}
}
printf("%d\n", cnt);
return 0;
}
deque
方法
int main()
{
函数成员 函数功能
begin() 返回指向容器中第一个元素的迭代器。
end() 返回指向容器最后一个元素所在位置后一个位置的迭代器,通常和 begin() 结合使用。
rbegin() 返回指向最后一个元素的迭代器。
rend() 返回指向第一个元素所在位置前一个位置的迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crbegin() 和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
crend() 和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改元素。
size() 返回实际元素个数。
max_size() 返回容器所能容纳元素个数的最大值。这通常是一个很大的值,一般是 232 - 1,我们很少会用到这个函数。
resize() 改变实际元素的个数。
empty() 判断容器中是否有元素,若无元素,则返回 true;反之,返回 false。
shrink _to_fit() 将内存减少到等于当前元素实际所使用的大小。
at() 使用经过边界检查的索引访问元素。
front() 返回第一个元素的引用。
back() 返回最后一个元素的引用。
assign() 用新元素替换原有内容。
push_back() 在序列的尾部添加一个元素。
push_front() 在序列的头部添加一个元素。
pop_back() 移除容器尾部的元素。
pop_front() 移除容器头部的元素。
insert() 在指定的位置插入一个或多个元素。
erase() 移除一个元素或一段元素。
clear() 移出所有的元素,容器大小变为 0。
swap() 交换两个容器的所有元素。
emplace() 在指定的位置直接生成一个元素。
emplace_front() 在容器头部生成一个元素。和 push_front() 的区别是,该函数直接在容器头部构造元素,省去了复制移动元素的过程。
emplace_back() 在容器尾部生成一个元素。和 push_back() 的区别是,该函数直接在容器尾部构造元素,省去了复制移动元素的过程。
return 0;
}
eg最大子序列 <= m
#include<bits/stdc++.h>
using namespace std;
deque<int> dq;
int s[100005];
int main() {
int n, m; scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i++) scanf("%lld", &s[i]);
for (int i = 1; i <= n; i++) s[i] = s[i] + s[i - 1]; //计算前缀和
int ans = -1e8;
dq.push_back(0);
for (int i = 1; i <= n; i++) {
while (!dq.empty() && dq.front() < i - m) dq.pop_front(); //队头超过m范围:删头
if (dq.empty()) ans = max(ans, s[i]);
else ans = max(ans, s[i] - s[dq.front()]); //队头就是最小的s[k]
while (!dq.empty() && s[dq.back()] >= s[i]) dq.pop_back(); //队尾大于s[i],去尾
dq.push_back(i);
}
printf("%d\n", ans);
return 0;
}
STL Stack
使用方法
int main(int argc, char const *argv[])
{
top():返回一个栈顶元素的引用,类型为 T&。如果栈为空,返回值未定义。
push(const T & obj):可以将对象副本压入栈顶。这是通过调用底层容器的 push_back() 函数完成的。
push(T && obj):以移动对象的方式将对象压入栈顶。这是通过调用底层容器的有右值引用参数的 push_back() 函数完成的。
pop():弹出栈顶元素。
size():返回栈中元素的个数。
empty():在栈中没有元素的情况下返回 true。
emplace():用传入的参数调用构造函数,在栈顶生成对象。
swap(stack<T> & other_stack):将当前栈中的元素和参数中的元素交换。参数所包含元素的类型必须和当前栈的相同。对于 stack 对象有一个特例化的全局函数 swap() 可以使用。
return 0;
}
手写栈
const int N = 100100;
struct mystack {
char a[N]; //存放栈元素,字符型
int t = 0; //栈顶位置
void push(char x) { a[++t] = x; } //送入栈
char top() { return a[t]; } //返回栈顶元素
void pop() { t--; } //弹出栈顶
int empty() { return t == 0 ? 1 : 0;} //返回1表示空
} st;
二叉树和哈曼夫树
手写二叉树
动态
struct node {
int valuel;
node* lson; node* rson;
};
静态
struct node {
char value;
int lson; int rson;
} nodes[N];
遍历
void preorder(node* root) {
cout << root->value;
preorder(root->lson);
preoder(root->rson);
}
void inorder(node *root) {
ineorder(root->lson);
cout << root->value;
inorder(root->rson);
}
void postorder(node* root) {
postorder(root->lson);
postorder(root->rson);
cout << root->value;
}
1 i的父节点在i/2
2 i的子节点在2i 2i+1
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int heap[N], len = 0; //len记录当前二叉树的长度
void push(int x) { //上浮,插入新元素
heap[++len] = x;
int i = len;
while (i > 1 && heap[i] < heap[i / 2]) {
swap(heap[i], heap[i / 2]);
i = i / 2;
}
}
void pop() { //下沉,删除堆头,调整堆
heap[1] = heap[len--]; //根结点替换为最后一个结点,然后结点数量减1
int i = 1;
while ( 2 * i <= len) { //至少有左儿子
int son = 2 * i; //左儿子
if (son < len && heap[son + 1] < heap[son])
son++; //son<len表示有右儿子,选儿子中较小的
if (heap[son] < heap[i]) { //与小的儿子交换
swap(heap[son], heap[i]);
i = son; //下沉到儿子处
}
else break; //如果不比儿子小,就停止下沉
}
}
int main() {
int n; scanf("%d", &n);
while (n--) {
int op; scanf("%d", &op);
if (op == 1) { int x; scanf("%d", &x); push(x); } //加入堆
else if (op == 2) printf("%d\n", heap[1]); //打印堆头
else pop(); //删除堆头
}
return 0;
}
自带
#include<bits/stdc++.h>
using namespace std;
priority_queue<int ,vector<int>,greater<int> >q; //定义堆
int main(){
int n; scanf("%d",&n);
while(n--) {
int op; scanf("%d",&op);
if(op==1) { int x; scanf("%d",&x); q.push(x); }
else if(op==2) printf("%d\n",q.top());
else q.pop();
}
return 0;
}

浙公网安备 33010602011771号