链表
一种链式数据结构,前一个指向后一个,可以通过这种指针执行遍历操作
作用:
高效执行指定位置的插入元素操作o(1)
//数组模拟
const int N=1e5;
int e[N];//编号为i的边指向的节点
int ne[N];//ne代表编号为i的节点的下一条边
int idx;
void add_edge(int x,int val)//向x节点后添加一个节点值为val的
{
e[++idx]=val;
ne[idx]=ne[x];
ne[x]=idx;
}
# 结构体
class node:
def __init__(self,val=0):
self.val=val
self.ne=None
class linkedlist:
def _init__(self):
self.head=None
def append(self,val)://在最后添加一个节点
{
newnode=node(val)
if not self.head:
self.head=newnode
else:
cur_node=self.head
while cur_node.ne:
cur_node=cur_node.ne
cur_node.ne=newnode
}
def head_insert(self,val):
{
newnode=node(val)
newnode.ne=self.head
self.head=newnode
}
def insert_node(self,pos,val):
{
newnode=node(val)
prev = self.head
for i in range(pos - 1):
if not prev:
return -1
prev = prev.next
if not prev:
return -1
newnode.next = prev.next
prev.next = newnode
}
邻接链表
用于存储图的关系
链表只能存储一条链形式的图,而对其进行改造(加入h数组)可以存储一对多的关系。
关键:
e数组 edge边
ne数组 next edge
h数组 当前节点上一个节点的
const int N=1e5+10;
int e[N],ne[N],h[N],idx;
memset(h,-1,sizeof h);
void add_adge(int a,int b)
{
e[idx]=a,ne[idx]=h[a],h[idx]=++idx;
}
栈
满足先进后出的数据结构,就像往坑里边丢土豆,必须拿出来上层的(后丢进去的)才能拿下层的
栈的顺序是天然倒序的:比如放入abc取出时就是cba,这种性质可以用于撤回操作
栈与树的关系也很重要,涉及到dfs,树的遍历顺序前中后序
栈的性质:保留最新操作的同时,保存到达最新操作的旧操作这个过程
//数组模拟栈
const int N=1e5+10;
int idx=0;
int stk[N];
void add(int val)
{
stk[++idx]=val;
}
void rm()
{
idx--;
}
//stl
#include<stack>
队列
//数列模拟
int l,r;
int q[N];
int idx;
void push_(int a)
{
q[++r]=a;
}
void pop_()
{
l++;
}
优先队列
一种二叉树状数据结构
父节点比子节点大/小
一个大根堆(父节点的值总是大于子节点)的删除逻辑是:与最后一个节点相交换然后执行down函数,因为直接删除会导致变成两个树。
插入的逻辑是:将新元素插入堆的底部然后进行up函数
关键是将对堆的所有更改操作都限定在堆底部
down函数调整的时候涉及到一个优先级问题,那就是父节点总和更大的子节点交换,否则交换后的结构不能保证父节点比子节点大
#include <iostream>
using namespace std;
const int N = 1e5 + 10;
int h[N]; // 堆数组(1-indexed)
int n = 0; // 当前堆大小
void swap_(int x, int y) {
int t = h[x];
h[x] = h[y];
h[y] = t;
}
// 向上调整
void up(int x) {
while (x > 1 && h[x] > h[x / 2]) {
swap_(x, x / 2);
x /= 2;
}
}
// 向下调整
void down(int x) {
while (x <= n / 2) { // 有至少一个孩子
int child = x * 2; // 默认左孩子
// 如果右孩子存在且更大
if (child + 1 <= n && h[child + 1] > h[child]) {
child = child + 1;
}
if (h[x] >= h[child]) break; // 堆性质已满足
swap_(x, child);
x = child; // 下沉到孩子位置
}
}
// 插入元素
void add(int val) {
h[++n] = val;
up(n);
}
// 删除堆顶
void pop_() {
if (n == 0) return;
swap_(1, n);
n--;
if (n > 0) down(1); // 如果还有元素,调整
}
#面向对象编程
import heapq
class PriorityQueue:
def __init__(self, max_heap=False):
self.heap = []
self.max_heap = max_heap # True 表示大根堆
def push(self, val):
if self.max_heap:
heapq.heappush(self.heap, -val)
else:
heapq.heappush(self.heap, val)
def pop(self):
if not self.heap:
raise IndexError("pop from empty priority queue")
val = heapq.heappop(self.heap)
return -val if self.max_heap else val
def top(self):
if not self.heap:
raise IndexError("top from empty priority queue")
val = self.heap[0]
return -val if self.max_heap else val
def size(self):
return len(self.heap)
def empty(self):
return len(self.heap) == 0
单调栈(栈的应用)
问题:找到某个元素的左边的最近的一个比它小的元素
int idx=-1;
int stk[N];
int target[N];
for(int i=0;i<n;i++)
{
//先弹出
while(idx!=-1&&stk[idx]>target[i])idx--;
cout<<stk[idx];
stk[++idx]=target[i];
}
单调队列
滑动窗口问题,要求输出一个滑动窗口里数组元素的最值(窗口大小不变为k)问所有可能答案是什么
核心思想是只维护可能的答案
#include<bits/stdc++.h>
using namespace std;
const int N=1000010;
int a[N],q[N];
int tt=-1,hh;
int n,k;
int main()
{
cin>>n>>k;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
for(int i=0;i<n;i++)
{
if(hh<=tt&&q[hh]<i-k+1)hh++;
while(hh<=tt&&a[q[tt]]>=a[i])tt--;
q[++tt]=i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
}
printf("\n");
hh = 0, tt = -1;
for (int i = 0; i < n; i ++ )
{
if (hh <= tt && i - k + 1 > q[hh]) hh ++ ;
while (hh <= tt && a[q[tt]] <= a[i]) tt -- ;
q[ ++ tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
}
return 0;
}
1441

被折叠的 条评论
为什么被折叠?



