数据结构

- 初级数据结构

  • 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<int> 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 <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>

using namespace std;

int main() {
    queue <int> qu;
    int a;
    scanf("%d",&a);
    qu.push(a); //插入元素 
    if(!qu.empty()) { //判断队列是否为空 
        cout<<qu.size()<<endl; //输出队列中元素个数 
        cout<<qu.front(); //输出队首元素 
        qu.pop(); //弹出队首元素 
    }
    return 0;
}
  • 手写队列:

#include <iostream>
#include <cstdio>
#include <algorithm>

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<<qu.size()<<endl; //输出队列中元素个数 
        cout<<qu.front(); //输出队首元素 
        qu.pop(); //弹出队首元素 
    }
    return 0;
}
  • 单调队列:

给定一个队列,维护三个操作。
1:将一个数x压入队列中。
2:求队列中数的最大值。
3:弹出队列尾的数。

这就要用到单调队列。

在每次进入队伍时,判断当前元素与队尾元素的大小关系,若小于队尾元素,则队尾不可能成为最小值,直接删除即可。
每次查询的答案一定在队首位置。
由于每个元素至多进队列和出队列一次,时间复杂度为O(n)。

下面以Sliding Window为例

STL(用deque(双端队列))

#include <iostream>
#include <cstdio>
#include <queue>
#include <deque>

using namespace std;
typedef pair<int,int> P;
#define maxn 1000000 + 10

deque<P> Q1;
deque<P> 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 <iostream>
#include <cstdio>
#include <algorithm>
#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<t1 && Q1[t1].v>=x) t1--;
        Q1[++t1].f(x,i);
        if(i>=k) {
            while(h1<t1 && Q1[h1+1].p<=i-k) h1++;
            Min[i]=Q1[h1+1].v;
        }
        while(h2<t2 && Q2[t2].v<=x) t2--;
        Q2[++t2].f(x,i);
        if(i>=k) {
            while(h2<t2 && Q2[h2+1].p<=i-k) h2++;
            Max[i]=Q2[h2+1].v;
        }
    }
    for(int i=k;i<=n;i++) printf("%d ",Min[i]);
    printf("\n");
    for(int i=k;i<=n;i++) printf("%d ",Max[i]);
    return 0;
}
  • 栈:

遵循先进后出的原则(FILO)的数据结构。

  • STL:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <stack>

using namespace std;

int main() {
    stack <int> st;
    int a;
    scanf("%d",&a);
    st.push(a); //插入元素 
    if(!st.empty()) { //判断栈是否为空 
        cout<<st.size()<<endl; //输出栈中元素个数 
        cout<<st.top(); //输出栈顶元素 
        st.pop(); //弹出栈顶元素 
    }
    return 0;
}
  • 手写:

#include <iostream>
#include <cstdio>
#include <algorithm>

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<<st.size()<<endl; //输出栈中元素个数 
        cout<<st.top(); //输出栈顶元素 
        st.pop(); //弹出栈顶元素 
    }
    return 0;
}
  • 单调栈

  • 3、堆

STL多好用

#include <queue>
priority_queue <int> big_heap; //大根堆
priority_queue <int,vector<int>,greater<int> > 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(son<size && a[son]>a[son+1]) son++;
            if(a[dad]>a[son]) swap(a[son],a[dad]);
            dad=son;
        }
        return ;
    }
    bool empty() {
        return !size;
    }
}; 

- 基础数据结构

  • 1、线段树

二分的思想把区间内的点分成若干个线段然后处理问题,查询和修改都是O(nlogn)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstdlib>
#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) ret+=query(rson,L,R);
    return ret;
}
void modify(ll l,ll r,ll rt,ll L,ll R,ll v) {
    if(L<=l && R>=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<R) modify(rson,L,R,v);
    updata(rt);
}
ll n,m;
int main() {
    n=read(),m=read();
    for(ll i=1;i<=n;i++)a[i]=read();
    build(1,n,1);
    while(m--) {
        ll id=read();
        if(id==2) {
            ll x=read(),y=read();
            printf("%lld\n",query(1,n,1,x,y));
        }
        else {
            ll x=read(),y=read(),z=read();
            modify(1,n,1,x,y,z);
        }
    }
    return 0;
}

  • 2、树状数组

前缀和和差分的思想,利用二进制,反正就是跳来跳去。反正背过就行。
就当存一下模板。

/*
1.将某一个数加上x
2.求出某区间每一个数的和
*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#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 <iostream>
#include <cstdio>
#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<<log_2[i]+1)==i)
            ++log_2[i];
    }
    for(int i=n;i;--i){
        st[i][0]=a[i];
        for(int j=1;(i+(1<<j)-1)<=n;++j)
            st[i][j]=max(st[i][j-1],st[i+(1<<j-1)][j-1]);
    }
}
inline int ask(int l,int r){
    int k=log_2[r-l+1];
    return max(st[l][k],st[r-(1<<k)+1][k]);
}

  • 并查集

貌似我在图伦里写过了。

  • 带权并查集


  • 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<n;i++) {
        belong[i]=(i-1)/block;
    }
    for(int i=1;i<=num;i++) {
        l[i]=(i-1)*block;
        r[i]=i*block-1;
    }
    r[num]=n;
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容

  • 1.1 基本数据结构 1. 数组 2. 链表,双向链表 3. 队列,单调队列,双端队列 4. 栈,单调栈 1.2 ...
    AlanCong阅读 619评论 0 1
  • 1.什么是数据结构? 数据结构就是计算机存储、组织数据的方式。 2.常见的数据结构 1.数组 2.栈 3.队列 4...
    奥特曼打_小怪兽阅读 1,180评论 0 1
  • 久违的晴天,家长会。 家长大会开好到教室时,离放学已经没多少时间了。班主任说已经安排了三个家长分享经验。 放学铃声...
    飘雪儿5阅读 7,518评论 16 22
  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,561评论 0 11
  • 可爱进取,孤独成精。努力飞翔,天堂翱翔。战争美好,孤独进取。胆大飞翔,成就辉煌。努力进取,遥望,和谐家园。可爱游走...
    赵原野阅读 2,724评论 1 1