【暖*墟】 #链表# 邻值查找 + 动态中位数离线做法

目录

链表的特征

// struct储存建立

// 建新链表,初始时将head、tail相连

// 在p后插入包含数据val的新节点

// 删除元素p

// 链表内存回收

// 数组模拟链表清空

【例题】邻值查找

【例题】动态中位数的离线做法 poj3784


链表的特征

支持在任意位置插入或删除,但只能按顺序访问其中的元素。

常用于“倒序处理”,因为链表容易执行“删除”操作。

以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;
} 

猜你喜欢

转载自blog.csdn.net/flora715/article/details/81095854
今日推荐