常用基本数据结构

基本数据结构

链表手写

动态链表

叫到第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;
}
posted @ 2022-11-14 23:20  SilveryZz  阅读(48)  评论(0)    收藏  举报