数据结构

本文介绍了数组模拟链表、单链表和双链表的基本操作,包括在节点后插入和删除节点。接着讨论了栈和队列的模拟实现,以及如何用单调栈和单调队列解决滑动窗口问题。此外,还涉及了KMP字符串匹配算法和Trie树在字符串存储和查找中的应用。
摘要由CSDN通过智能技术生成

数组模拟链表

注意初始化时使用了数组的几个元素,让第k个数与数组下标对应,需要 k 加减 x。

单链表

AcWing826 单链表

在k后插入值插入

        ne[idx] = ne[k];
        ne[k] = idx;

双链表

AcWing829 双链表

在k的右节点插入一个数,若想在左边插入 add( l[k] , x )

        r[idx] = r[k];
        l[idx] = k;
        l[r[k]] = idx;
        r[k] = idx;

删除k节点。

     r[l[k]] = r[k];
     l[r[k]] = l[k];

模拟栈

栈:先进先出  队列:先进后出

    static int N = 100010, tt;
    static int stk[] = new int[N];

    static void push(int x){
        stk[++tt] = x;   //进栈
    }
    static void pop(){
        tt --;  //出栈
    }
    static String empty(){
        return tt > 0 ? "NO" : "YES";  //判断是否为空并返回
    }
    static int query(){
        return stk[tt];       //返回栈顶元素
    }

双栈运算

AcWing 3302 表达式求值

1+2+3*4*5,

运算符栈(op_stack)和数字栈(num_stack),下面图解:从左到右

 

 

模拟队列

AcWing 829模拟队列

 看个人习惯,我初始化队列的两指针为0,队尾插入 q[end ++] = x,队头弹出 q[ head ++],

判空  head == end ? "YES" : "NO",查询队头对尾 return q[head],return q[end]

单调栈

AcWing830 单调栈

当某个点在左边且他的值大于右边的点,就将他删除,从而形成单调栈。

//循环判断若栈顶点大于新注入点,将栈顶点移除栈,相同也要移除只留一个保证单调性
 while(!numStack.empty() && numStack.peek() >= number)  numStack.pop();

单调队列 - 滑动窗口

154. 滑动窗口 - AcWing题库

左边是原数组,红框是画图窗口、右边是队列。

求滑动窗口中的最小值:当窗口中左边的数大于右边,那么她永远不可能作为最小值输出,直接从队列里移除(先进先出)。

而后进来的数比队尾的数小,则剔除队尾继续比较,直到该队列为”单调递增“位置。将每次滑动窗口最小的数放在队头,每次取最小值时取对头就行。最大值同理。

        for(int i = 0; i < n; i ++){
            a[i] = Integer.parseInt(str[i]);
        
            //判断当队列不为空、窗口已经形成并移动下一格时出队上一格最小值
            if(head != end && i >= k && a[i - k] == q[head])  head ++;

            while(head != end && q[end - 1] > a[i]){ end --; q[end] = 0;}
            q[end++] = a[i];

            //判断已形成了窗口输出最小值,刚开始没有形成窗口
            if(i + 1>= k)  log.write(q[head]+" ");
        }

KMP

AcWing 831. KMP字符串题解

kmp、next数组 图解

字符串中最长的后缀和最长的前缀相等,称为next[],

下面分别是构建 next[ ] ,和kmp匹配过程

        int next[] = new int[n + 1];
        //构建next数组,next[1]肯定为0,因为只有一个字符
        for(int i = 2, j = 0; i < n + 1; i ++){
            while(j != 0 && cp[i] != cp[j + 1]) j = next[j]; //前后缀匹配失败,指针回退到next[j]
            
            if(cp[i] == cp[j + 1]) j ++; //前后缀匹配后长度改变
            
            next[i] = j;  //写入模板串该点的next数组(即这么长的字符串前后缀匹配最大的长度)
        }
        
        for(int i = 1, j = 0; i < m + 1; i ++){
            while(j != 0 && cs[i] != cp[j + 1]) j = next[j];
            
            if(cs[i] == cp[j + 1]) j ++; //计数并进行下一个字符匹配
            
            if(j == n){  //完全匹配
             log.write(i - n +" ");
             j = next[j]; //继续搜索
            }
        }

Trie树

高效存储和查找字符串。

求字符串存在的数量  AcWing 字符串统计

已存在字符串标记 *,方便查找。

//形成结构有点像链表,son[p][num] 相当于ne[]指针。idx 代表指针使用的下标

    static void insert(String s){
        int p = 0; //从根节点开始遍历
        for(int i = 0; i < s.length(); i ++){
            int num = s.charAt(i) - 'a';
            if(son[p][num] == 0) son[p][num] = ++idx;
            p = son[p][num];
        }
        count[p] ++; //结束循环后 p指向字符串末尾,标记数量
    }

求异或最大

合并集合

可能存在以下关系:

基本原理:每个集合通过一棵树来表示,树根编号就是整个集合的编号,每个节点存放它的父节点,p[x] 表示x集合的父节点。

判断树根:if(p[x] == x)

求x集合的集合编号while(p[x] != x)x = p[x]

合并两个集合:px是x的集合编号,py是y得集合编号,x = y,将两个树根联系在一起

优化:压缩路径

    static int find(int x){  //返回p[x]得祖宗节点 + 路径压缩
        if(p[x] != x) p[x] = find(p[x]);
        return p[x];
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值