手写堆、栈、队列

前言

作为忠实的STL党,有时也不得不做出一些改变,毕竟STL经常TLE、MLE等等。学习一下实现原理并手动实现还是很有必要的,因为它跑的快 这是我们计算机专业最基础的数据结构

数组形式

优点:一次性可以存很多元素

缺点:只能创建少数的栈

const int maxn=1e5+10;
struct Stack{
    ll a[maxn];
    int cur=0;

    void push(ll x){
        a[++cur]=x;
    }
    
    void pop(){
        if(!empty()) cur--;
    }
    
    ll top(){
        return a[cur];
    }
    
    int size(){
        return cur;
    }

    int empty(){
        return !cur;
    }
};

链表形式

优点:动态存储元素,而且一次性可以创建很多栈

缺点:每个栈不能都存很多,否则溢出

struct node{
    int val;
    node *next;
    node(int w,node *t){
        val=w;
        next=n;
    }
};

struct Stack{
    int num;
    node *head;

    Stack(){
        num=0;
        head=nullptr;
    }

    node* push(int val){
        head=new node(val,head);
        num++;
        return head;
    }

    void pop(){
        if(empty()) return;
        node *t=head;
        head=head->next;
        num--;
        delete t;
    }

    int top(){
        if(!empty()) return head->val;
        return 0;
    }

    int size(){
        return num;
    }

    bool empty(){
        return !num;
    }
};
队列

简单数组

缺点:尽管可以一次性入队很多元素,但是当实现过很多次入队出队操作后,队列内存越来越小

const int maxn=1e5+10;

struct Queue{
    int a[maxn];
    int l=0,r=0;

    void push(int x){
        a[r++] = x;
    }

    int front(){
        return a[l];
    }

    void pop(){
        if(!empty()) l++;
    }
	
    int size(){
      	return r-l;  
    }
    
    int empty(){
        return l>=r?1:0;
    }
};

循环数组

实际上是数据结构的队列中提到要循环数组,但是一般情况下普通数组即可

const int maxn=1e5+10;

struct Queue{
    int a[maxn];
    int l=0,r=0;

    void push(int x){
        a[r] = x;
        r = (r+1)%maxn;
    }

    int front(){
        return a[l];
    }

    void pop(){
        if(!empty()) l = (l+1)%maxn;
    }

    int size(){
        return (r-l+maxn)%maxn;
    }

    int empty(){
        return l==r?1:0;
    }
};
堆(优先队列)

这里说的堆即二叉堆,其实并不是多么高深的数据结构,只是数组形式的二叉树,但是该二叉树必须是完全二叉树。二叉堆有两种,大根堆和小根堆,区别就是维护堆的最大值还是最小值:

大根堆:父结点的权值总是大于或等于任何一个子结点的权值

小根堆:父结点的权值总是小于或等于任何一个子节点的权值

二叉堆实际上不能保证数组是有序的,但是每次添加元素,一层层和父节点比较交换,维护每个父节点都是所有子节点中的最值,因此我们从堆中取出的首元素一定是该集合中最大元素或者最小元素,时间复杂度为O(log2n)

小根堆

小根堆就是维护堆的根节点是所有元素的最小值

以序列"1 3 4 2"入堆为例,前面三个都能保证父节点是最小的,当2入堆后:
在这里插入图片描述
此时2小于了父节点(fa=sz>>1),那么就交换(swap(heap[now],heap[fa])):
在这里插入图片描述
这时再和2的父节点比较时,满足了父节点(根节点)是最小的,break。否则就交换最后一次然后退出,因此while循环结束条件是now是否等于1

接下来我们要将堆中根节点出堆,首先和堆的最后一个元素交换:
在这里插入图片描述
sz–即删去了最后一个元素,那么就代表根节点出堆了。但是因为根节点不是最小值了,那么我们从根节点开始,每次和下面的两个最近子节点比较:
在这里插入图片描述
很明显左孩子更小,那么就和左孩子交换(否则就和右孩子交换):
在这里插入图片描述
现在的now到了下标2,堆也已经更新完成,while循环结束条件就是(now>>1)<=sz,这个sz是更新后的sz

小根堆

struct Heap_less{
    int heap[maxn];
    int sz=0;

    void push(int x){
        heap[++sz]=x;
        int now=sz;
        while(now>1){
            int fa=now>>1;
            if(heap[fa]>heap[now])
                swap(heap[fa],heap[now]);
            else break;
            now=fa;
        }
    }

    void pop(){
        swap(heap[sz],heap[1]);
        sz--;
        int now=1;
        while((now<<1)<=sz){
            int son=now<<1;
            if(son+1<=sz && heap[son+1]<heap[son]) son++;
            if(heap[son]<heap[now])
                swap(heap[son],heap[now]);
            else break;
            now=son;
        }
    }

    int top(){
        return heap[1];
    }

    int size(){
        return sz;
    }

    bool empty(){
        return !sz;
    }
};

大根堆

struct Heap_greater{
    int heap[maxn];
    int sz=0;

    void push(int x){
        heap[++sz]=x;
        int now=sz;
        while(now>1){
            int fa=now>>1;
            if(heap[fa]<heap[now])
                swap(heap[fa],heap[now]);
            else break;
            now=fa;
        }
    }

    void pop(){
        swap(heap[sz],heap[1]);
        sz--;
        int now=1;
        while((now<<1)<=sz){
            int son=now<<1;
            if(son+1<=sz && heap[son+1]>heap[son]) son++;
            if(heap[son]>heap[now])
                swap(heap[son],heap[now]);
            else break;
            now=son;
        }
    }

    int top(){
        return heap[1];
    }

    int size(){
        return sz;
    }

    bool empty(){
        return !sz;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值