- 初级数据结构
-
1、链表、双向链表(
估计考试前是不会用了) -
定义链表:
struct LinkList {
int value;
LinkList *next;
};
-
根据输入建立单链表
将输入的节点插入到链表头部。
LinkList *BuildList() {
LinkList *head = NULL;
int data;
int i = 0;
while (scanf("%d", &data) != EOF) {
//scanf("%d", &data);++i;
LinkList *new_node = (LinkList *)malloc(sizeof(LinkList));
if (NULL == new_node) {
fprintf(stderr, "malloc failed");
return head;
}
new_node->value = data;
if (head == NULL) {
new_node->next = NULL;
head = new_node;
}
else {
new_node->next = head;
head = new_node;
}
}
return head;
}
-
插入
//在链表头部插入节点
LinkList *InsertToHead(int value, LinkList *head) {
LinkList *new_node = (LinkList *)malloc(sizeof(LinkList));
if (new_node == NULL) {
fprintf(stderr, "malloc failed");
return head;
}
new_node->value = value;
new_node->next = NULL;
if (head == NULL) {
head = new_node;
}
else {
new_node->next = head;
head = new_node;
}
return head;
}
//链表尾部插入节点
LinkList *InsertToTail(int value, LinkList *head) {
LinkList *new_node = (LinkList *)malloc(sizeof(LinkList));
if (new_node == NULL) {
fprintf(stderr, "malloc failed");
return head;
}
new_node->value = value;
new_node->next = NULL;
if (head == NULL)
head = new_node;
else {
LinkList *pnode = head;
while (pnode->next != NULL)
pnode = pnode->next;
pnode->next = new_node;
}
return head;
}
-
删除
//删除某节点
LinkList *DeletebyValue(int value, LinkList* head) {
if (head == NULL)
return head;
LinkList *pToDelete = NULL;
if (head->value == value) {
pToDelete = head;
head = head->next;
}
else {
LinkList *p = head;
while (p->next != NULL && p->next->value != value)
p = p->next;
if (p->next != NULL) {
pToDelete = p->next;
p->next = pToDelete->next;
}
}
if (pToDelete != NULL) {
free(pToDelete);
pToDelete = NULL;
}
return head;
}
-
求单链表中结点的个数
注意检查链表是否为空。时间复杂度为O(n)。该操作不用特意检查链表是否为空,如下代码,链表为空会返回0。
unsigned int Length(LinkList *head) {
unsigned int length = 0;
LinkList *p = head;
while (p) {
++length;
p = p->next;
}
return length;
}
-
打印链表元素
//打印单链表 顺序
void PrintList(LinkList *head) {
LinkList *p = head;
while (p) {
printf("%d ", p->value);
p = p->next;
}
printf("\n");
}
//逆序打印单链表:非递归
void RPrintList(LinkList* head) {
if (NULL == head)
return;
stack list_stack;
while (head) {
list_stack.push(head->value);
head = head->next;
}
while (!list_stack.empty()) {
printf("%d ", list_stack.top());
list_stack.pop();
}
printf("\n");
}
//逆序打印单链表:递归
void RPrintListRecursively(LinkList* head) {
if (NULL == head)
return;
else {
RPrintListRecursively(head->next);
printf("%d ", head->value);
}
}
双向链表是什么
-
2、队列、单调队列、栈、单调栈
-
队列:
遵循先进先出的原则(FIFO)的数据结构。
-
STL:
#include
#include
#include
#include
using namespace std;
int main() {
queue qu;
int a;
scanf("%d",&a);
qu.push(a); //插入元素
if(!qu.empty()) { //判断队列是否为空
cout<
-
手写队列:
#include
#include
#include
using namespace std;
struct queue {
int Q[500005],head,tail;
queue() {
head=0,tail=0;
}
void push(int a) {
Q[++tail]=a;
return ;
}
void pop() {
head++;
return ;
}
bool empty() {
return head>=tail;
}
int front() {
return Q[head+1];
}
int size() {
if(head<=tail) return tail-head;
}
};
int main() {
queue qu;
int a;
scanf("%d",&a);
qu.push(a); //插入元素
if(!qu.empty()) { //判断队列是否为空
cout<
-
单调队列:
给定一个队列,维护三个操作。
1:将一个数x压入队列中。
2:求队列中数的最大值。
3:弹出队列尾的数。
这就要用到单调队列。
在每次进入队伍时,判断当前元素与队尾元素的大小关系,若小于队尾元素,则队尾不可能成为最小值,直接删除即可。
每次查询的答案一定在队首位置。
由于每个元素至多进队列和出队列一次,时间复杂度为O(n)。
下面以Sliding Window为例
STL(用deque(双端队列))
#include
#include
#include
#include
using namespace std;
typedef pair P;
#define maxn 1000000 + 10
deque Q1;
deque
Q2;
int n,k;
int Min[maxn], Max[maxn];
int main() {
while(~scanf("%d%d", &n, &k)) {
while(!Q1.empty()) Q1.pop_back();
while(!Q2.empty()) Q2.pop_back();
int x;
for(int i=1; i<=n; i++) {
scanf("%d", &x);
while(!Q1.empty() && Q1.back().first >= x) Q1.pop_back();
Q1.push_back(P(x,i));
if(i >= k) {
while(!Q1.empty() && Q1.front().second <= i-k) Q1.pop_front();
Min[i] = Q1.front().first;
}
while(!Q2.empty() && Q2.back().first <= x) Q2.pop_back();
Q2.push_back(P(x,i));
if(i >= k) {
while(!Q2.empty() && Q2.front().second <= i-k) Q2.pop_front();
Max[i] = Q2.front().first;
}
}
for(int i=k; i<=n; i++)
i == n ? printf("%d\n", Min[i]) : printf("%d ", Min[i]);
for(int i=k; i<=n; i++)
i == n ? printf("%d\n", Max[i]) : printf("%d ", Max[i]);
}
return 0;
}
手写
#include
#include
#include
#define MAXN 1000005
using namespace std;
struct s {
int v,p;
void f(int a,int b) {
v=a,p=b;
}
}Q1[MAXN],Q2[MAXN];
int h1,t1,h2,t2,Min[MAXN],Max[MAXN];
int n,k;
int main() {
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) {
int x;
scanf("%d",&x);
while(h1=x) t1--;
Q1[++t1].f(x,i);
if(i>=k) {
while(h1=k) {
while(h2
-
栈:
遵循先进后出的原则(FILO)的数据结构。
-
STL:
#include
#include
#include
#include
using namespace std;
int main() {
stack st;
int a;
scanf("%d",&a);
st.push(a); //插入元素
if(!st.empty()) { //判断栈是否为空
cout<
-
手写:
#include
#include
#include
using namespace std;
struct stack {
int a[23333],pos;
stack() {
pos=0;
}
void push(int x) {
a[++pos]=x;
}
void pop() {
pos--;
}
bool empty() {
return !pos;
}
int top() {
return a[pos];
}
int size() {
return pos;
}
};
int main() {
stack st;
int a;
scanf("%d",&a);
st.push(a); //插入元素
if(!st.empty()) { //判断栈是否为空
cout<
-
单调栈 -
3、堆
STL多好用
#include
priority_queue big_heap; //大根堆
priority_queue ,greater > small_heap; //小根堆
heap.top();
heap.push();
heap.pop();
heap.empty();
heap.size();
手写:
struct priority_queue {
int a[23333],size;
priority_queue() {
size=0;
}
void push(int x) {
a[++size]=x;
int son=size,dad;
while(son!=1) {
dad=son>>1;
if(a[dad]>a[son]) swap(a[dad],a[son]);
son=dad;
}
return ;
}
int top() {
return a[1];
}
void pop() {
a[1]=a[size--];
int dad=1,son=2;
while(son<=size) {
son=dad<<1;
if(sona[son+1]) son++;
if(a[dad]>a[son]) swap(a[son],a[dad]);
dad=son;
}
return ;
}
bool empty() {
return !size;
}
};
- 基础数据结构
-
1、线段树
二分的思想把区间内的点分成若干个线段然后处理问题,查询和修改都是O(nlogn)
#include
#include
#include
#include
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define maxn 1000005
#define ll long long
using namespace std;
inline ll read() {
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
ll a[maxn],sum[maxn<<2],col[maxn<<2];
void updata(ll rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void color(ll l,ll r,ll rt,ll c) {
col[rt]+=c;
sum[rt]+=c*(r-l+1);
}
void push_col(ll l,ll r,ll rt) {
if(col[rt]) {
ll m=(l+r)>>1;
color(lson,col[rt]);
color(rson,col[rt]);
col[rt]=0;
}
}
void build(ll l,ll r,ll rt) {
if(l==r) {
sum[rt] = a[l];
return ;
}
ll m=(l+r)>>1;
build(lson);
build(rson);
updata(rt);
}
ll query(ll l,ll r,ll rt,ll L,ll R) {
if(L<=l && R>=r)return sum[rt];
push_col(l,r,rt);
ll ret=0;
ll m=(l+r)>>1;
if(L<=m) ret+=query(lson,L,R);
if(m=r) {
color(l,r,rt,v);
return ;
}
push_col(l,r,rt);
ll m=(l+r)>>1;
if(L<=m) modify(lson,L,R,v);
if(m
-
2、树状数组
前缀和和差分的思想,利用二进制,反正就是跳来跳去。反正背过就行。
就当存一下模板。
/*
1.将某一个数加上x
2.求出某区间每一个数的和
*/
#include
#include
#include
#define MAXN 500005
using namespace std;
int a[MAXN],c[MAXN],n,m;
int lowbit(int x) {
return x&(-x);
}
void modify(int p,int x) {
while(p<=n) {
c[p]+=x;
p+=lowbit(p);
}
return ;
}
int sum(int p) {
int sum=0;
while(p) {
sum+=c[p];
p-=lowbit(p);
}
return sum;
}
int query(int l,int r) {
return sum(r)-sum(l-1);
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]),modify(i,a[i]);
for(int i=1;i<=m;i++) {
int id,x,y;
scanf("%d%d%d",&id,&x,&y);
if(id==1) modify(x,y);
else printf("%d\n",query(x,y));
}
return 0;
}
/*
1.将某区间每一个数数加上x
2.求出某一个数的和
*/
#include
#include
#define maxn 500000+5
using namespace std;
int n,m,pre,c[maxn];
int lowbit(int x) {
return x&(-x);
}
void modify(int pos,int x) {
while(pos<=n) {
c[pos]+=x;
pos+=lowbit(pos);
}
return ;
}
int query(int pos) {
int ans=0;
while(pos>0) {
ans+=c[pos];
pos-=lowbit(pos);
}
return ans;
}
int main() {
scanf("%d%d",&n,&m);
for(int i=1; i<=n; i++) {
int x;
scanf("%d",&x);
modify(i,x-pre);
pre=x;
}
while(m--) {
int id;
scanf("%d",&id);
if(id==1) {
int l,r,k;
scanf("%d%d%d",&l,&r,&k);
modify(l,k);
modify(r+1,-k);
} else {
int pos;
scanf("%d",&pos);
printf("%d\n",query(pos));
}
}
return 0;
}
-
ST表
给定一个数列a,O(nlogn)预处理,O(1)查询数列在区间[l,r]的最值。
这里介绍求最大值。
int st[N][K],a[N],log_2[N];
inline void ini_st(){
log_2[1]=0;
for(int i=2;i<=n;++i){
log_2[i]=log_2[i-1];
if((1<
-
并查集
貌似我在图伦里写过了。
-
带权并查集
-
hash表
这个主要用于字符串hash,整理字符串的时候在写吧。
-
分块
把问题分成许多(一般是根号n)块,在块中完成。时间复杂度为O(m根号n)
int belong[maxn],block,num,l[1000],r[1000];
int n,m;
void build() {
block=sqrt(n);
num=block;
if(n%num)num++;
for(int i=0;i