目录
链表的特征
支持在任意位置插入或删除,但只能按顺序访问其中的元素。
常用于“倒序处理”,因为链表容易执行“删除”操作。
以struct存储节点的价值,以及prev,next。
prev,next两个指针指向前后相邻的两个节点,构成双向链表。
通常建立额外的两个节点head、tail来代表链表的头尾,规范边界。
// struct储存建立
struct Node{
int value; //数据
Node *prev,*next; //指针
};
Node *head,*tail;
或者
struct Node{
int value;
int prev,next;
}node[SIZE];
int head,tail,tot;
// 建新链表,初始时将head、tail相连
void init(){
head=new Node();
tail=new Node();
head->next = tail;
tail->prev = head;
}
或者
void init(){
tot=2; head=1; tail=2;
node[head].next=tail;
node[tail].prev=head;
}
// 在p后插入包含数据val的新节点
void insert(Node *p,int val){
q=new node();
q->value = val;
p->next->prev = q;
//↑↑↑在p原来连接的next上,prev值变成q
q->next = p->next;
p->next = q;
q->prev = p;
}
或者
void insert(int p,int val){
q=++tot; //增加新的编号
node[q].value=val;
node[node[p].next].prev=q;
node[q].next=node[p].next;
node[p].next=q;
node[q].prev=p;
}
// 删除元素p
void remove(Node *p){
p->prev->next=p->next;
p->next->prev=p->prev;
delete p; //指针型记得写删除
}
或者
void remove(int p){
node[node[p].prev].next=node[p].next;
node[node[p].next].prev=node[p].prev;
}
// 链表内存回收
void recycle(){
while(head!=tail){
head=head->next; //找下一个
delete head->prev; //删除前一个
}
delete tail; //删除链表的所有元素
}
// 数组模拟链表清空
void clear(){
memset(node,0,sizeof(node));
head=tail=tot=0;
}
【例题】邻值查找
#include <cmath>
#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
#include <vector>
#include <algorithm>
#include <stack>
#include <queue>
using namespace std;
typedef long long ll;
/*【邻值查找】
给定一个长度为n的无重复序列A。对于A中的每一个数Ai,求:
min|Ai-Aj| (1≤j<i),以及令上式取到最小值的 j(记为Pi)。
若最小值点不唯一,则选择使 Aj 较小的那个。 */
struct node{
int value;
int prev,next;
}a[110000];
int head,tail,tot;
int init(){ //新建链表
tot=2; head=1; tail=2;
a[head].next=tail;
a[tail].prev=head;
}
int insert(int p,int val){ //在位置p后插入包含数据val的新节点
int q=++tot;
a[q].value=val;
a[a[p].next].prev=q; //p原来的下一个节点的上一个点改为q
a[q].next=a[p].next; //q的下一个点改为p的下一个点
a[q].prev=p; //q的上一个点改为p
a[p].next=q; //p的下一个点改为q
}
void remove(int p){ //删除
a[a[p].prev].next=a[p].next;
a[a[p].next].prev=a[p].prev;
}
void clear(){ //数组模拟链表清空
memset(a,0,sizeof(a));
head=tail=tot=0;
}
struct node2{
int x,id; //记录a[i]的值和原序号
}A[110000];
int cmp(const void*xx,const void*yy){
node2 n1=*(node2 *)xx; //指针写法
node2 n2=*(node2 *)yy;
if(n1.x>n2.x) return 1; //x从小到大,然后id从小到大
if(n1.x<n2.x) return -1;
if(n1.x==n2.x){
if(n1.id>n2.id) return 1;
if(n1.id<n2.id) return -1;
}
return 0;
}
int c[110000],s[110000]; //编号之间建立关系
int sum[110000],ans[110000];
int main(){
int n; scanf("%d",&n);
memset(c,0,sizeof(c));
memset(s,0,sizeof(s));
for(int i=1;i<=n;i++){
scanf("%d",&A[i].x);
A[i].id=i; //记录a[i]的原始编号
}
sort(A+1,n,sizeof(node2),cmp);
init(); //建立链表
for(int i=1;i<=n;i++){
insert(tot,A[i].x);
s[tot]=A[i].id; //记录新编号对应的原编号
c[A[i].id]=tot; //记录原编号对应的新编号
}
memset(sum,63,sizeof(sum));
memset(ans,63,sizeof(ans));
for(int i=n;i>=1;i--){
int kk=a[c[i]].value;
if(s[a[c[i]].prev]!=0){ //链表前一个有元素
sum[i]=abs(a[a[c[i]].prev].value-kk);
ans[i]=s[a[c[i]].prev];
}
if((sum[i]>abs(a[a[c[i]].next].value-kk))&&(s[a[c[i]].next]!=0)){
//链表后一个有元素,且有更小的答案,更新
sum[i]=abs(a[a[c[i]].next].value-kk);
ans[i]=s[a[c[i]].next]; //记录最接近的元素的初始位置
}
if((sum[i]==abs(a[a[c[i]].next].value-kk))&&
(a[c[ans[i]]].value>a[a[c[i]].next].value)&&(s[a[c[i]].next]!=0))
ans[i]=s[a[c[i]].next]; //选择最接近的元素编号较小的
remove(c[i]); //此值已经查找到最终答案
}
for(int i=2;i<=n;i++) //i>j,i必须从2开始
printf("%d %d\n",sum[i],ans[i]);
return 0;
}
【例题】动态中位数的离线做法 poj3784
题意:读入一串数列,当i为奇数时输出它的中位数。
将所有的数列都读入后排序,最后一个中位数显而易见。
然后链表连一发 再按照输入的顺序 从后往前 两个两个删除。
1.当删除的两个数分别大于和小于当前的中位数 直接记录。
2.当删除的两个数都大于等于当前的中位数 记录last。
3.当删除的两个数都小于等于当前的中位数 记录next。
//链表离线
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct node{
int last,next,c,p;
}a[110000];
int b[11000],ans[11000],v[11000];
bool cmp(node x,node y){
if (x.c==y.c) return x.p<y.p;
else return x.c<y.c;
}
int main(){
freopen("3784.in","r",stdin);
freopen("3784.out","w",stdout);
int t;
scanf("%d",&t);
while (t--){
int n,p,ss=0,sl=0,sr=0,s,sx;
scanf("%d%d",&p,&n);
printf("%d %d\n",p,(n+1)/2);
for (int i=1;i<=n;i++){
scanf("%d",&b[i]);
a[i].c=b[i];
a[i].p=i;
}
sort(a+1,a+1+n,cmp);
for (int i=1;i<=n;i++){
a[i].last=i-1;
a[i].next=i+1;
v[a[i].p]=i;//v[i]:原来位置在i的数字排序后的新位置
}
s=a[n/2+1].c;sx=n/2+1;
if (n%2==0) ans[++ss]=(a[n/2].c+a[n/2+1].c)/2;
else ans[++ss]=a[n/2+1].c;
for (int i=n;i>1;i-=2){
if (v[i]<sx&&v[i-1]>sx||v[i]>sx&&v[i-1]<sx)
ans[++ss]=a[sx].c;
else if (v[i]<=sx&&v[i-1]<=sx){
sx=a[sx].next;
ans[++ss]=a[sx].c;
}
else{
sx=a[sx].last;
ans[++ss]=a[sx].c;
}
int x=a[v[i]].last,y=a[v[i]].next;
a[y].last=x;a[x].next=y;
x=a[v[i-1]].last,y=a[v[i-1]].next;
a[y].last=x;a[x].next=y;
}
int z=0;
for (int i=ss;i>0;i--){
if (z!=0) printf(" ");
z++;
printf("%d",ans[i]);
if (z==10) printf("\n"),z=0;
}
if (z!=0) printf("\n");
}
return 0;
}