算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP

目录

  • 1.单链表
    • 1.1用数组模拟单链表
    • 1.2 初始化
    • 1.3 将x插入头结点
    • 1.4 将x插入到k节点的后面
    • 1.5 删除操作
    • 1.6 单链表
  • 2. 双链表
    • 2.1 初始化
    • 2.2 在一个节点的右边插入一个点
    • 2.3 在一个节点的左边插入一个点
    • 2.4 删除节点
    • 2.5 代码
  • 3.栈
    • 3.1 表达式求值
  • 4.队列
  • 5. 考试考记忆力与自制力
  • 6.单调栈
    • 6.1考点
    • 6.2 暴力做法
    • 6.3 优化算法
    • 6.4代码
  • 7.单调队列(滑动窗口)
    • 7.1 暴力做法
    • 7.2 优化
    • 7.3单调栈和单调队列的总结
  • 8、KMP
    • 8.1 什么是kmp以及暴力算法
    • 8.2 模板串找最长后缀
    • 8.3 主串与模板串的比较
    • 8.4 kmp匹配过程代码(主串与模板串比较)
    • 8.5 求next数组(模板串找最长后缀)
    • 8.6 求next数组代码
    • 8.7 kmp实现代码

1.单链表

1.1用数组模拟单链表

不用结构体,速度很慢。用数组模拟链表。
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第1张图片
空节点用-1表示
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第2张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第3张图片

1.2 初始化

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第4张图片

1.3 将x插入头结点

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第5张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第6张图片

1.4 将x插入到k节点的后面

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第7张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第8张图片

1.5 删除操作

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第9张图片
在这里插入图片描述

1.6 单链表

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第10张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第11张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第12张图片
ide -》idx
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第13张图片

#include 

using namespace std;

const int N = 100010;


// head 表示头结点的下标
// e[i] 表示节点i的值
// ne[i] 表示节点i的next指针是多少
// idx 存储当前已经用到了哪个点
int head, e[N], ne[N], idx;

// 初始化
void init()
{
    head = -1;
    idx = 0;
}

// 将x插到头结点
void add_to_head(int x)
{
    e[idx] = x, ne[idx] = head, head = idx ++ ;
}

// 将x插到下标是k的点后面
void add(int k, int x)
{
    e[idx] = x, ne[idx] = ne[k], ne[k] = idx ++ ;
}

// 将下标是k的点后面的点删掉
void remove(int k)
{
    ne[k] = ne[ne[k]];
}

int main()
{
    int m;
    cin >> m;

    init();

    while (m -- )
    {
        int k, x;
        char op;

        cin >> op;
        if (op == 'H')
        {
            cin >> x;
            add_to_head(x);
        }
        else if (op == 'D')
        {
            cin >> k;
            if (!k) head = ne[head];
            else remove(k - 1);
        }
        else
        {
            cin >> k >> x;
            add(k - 1, x);
        }
    }

    for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';
    cout << endl;

    return 0;
}

2. 双链表

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第14张图片

2.1 初始化

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第15张图片

2.2 在一个节点的右边插入一个点

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第16张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第17张图片

2.3 在一个节点的左边插入一个点

代码不需要重新写,调用insert( l[k],x );
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第18张图片

2.4 删除节点

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第19张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第20张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第21张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第22张图片

2.5 代码

#include 

using namespace std;

const int N = 100010;

int m;
int e[N], l[N], r[N], idx;

// 在节点a的右边插入一个数x
void insert(int a, int x)
{
    e[idx] = x;
    l[idx] = a, r[idx] = r[a];
    l[r[a]] = idx, r[a] = idx ++ ;
}

// 删除节点a
void remove(int a)
{
    l[r[a]] = l[a];
    r[l[a]] = r[a];
}

int main()
{
    cin >> m;

    // 0是左端点,1是右端点
    r[0] = 1, l[1] = 0;
    idx = 2;

    while (m -- )
    {
        string op;
        cin >> op;
        int k, x;
        if (op == "L")
        {
            cin >> x;
            insert(0, x);
        }
        else if (op == "R")
        {
            cin >> x;
            insert(l[1], x);
        }
        else if (op == "D")
        {
            cin >> k;
            remove(k + 1);
        }
        else if (op == "IL")
        {
            cin >> k >> x;
            insert(l[k + 1], x);
        }
        else
        {
            cin >> k >> x;
            insert(k + 1, x);
        }
    }

    for (int i = r[0]; i != 1; i = r[i]) cout << e[i] << ' ';
    cout << endl;

    return 0;
}

3.栈

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第23张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第24张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第25张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第26张图片

3.1 表达式求值

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第27张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第28张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第29张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第30张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第31张图片

#include
#include
#include
#include
#include
using namespace std;

stack<int> num;
stack<char> op;

void eval()
{
    auto b=num.top();num.pop();
    auto a=num.top();num.pop();
    auto c=op.top();op.pop();
    int x;
    if(c=='+') x=a+b;
    else if(c=='-') x=a-b;
    else if(c=='*') x=a*b;
    else x=a/b;
    num.push(x);
}
int main()
{
    unordered_map<char,int> pr{{'-',1},{'+',1},{'*',2},{'/',2}};
    string str;
    cin>>str;
    for(int i=0;str[i];i++)
    {
        auto c=str[i];
        if(isdigit(c))
        {
            int x=0,j=i;
            while(j<str.size()&&isdigit(str[j])) x=x*10+str[j++]-'0';
            i=j-1;
            num.push(x);
        }
        else if(c=='(') op.push(c);
        else if(c==')')
        {
            while(op.top()!='(') eval();
            op.pop();
        }
        else
        {
            while(op.size()&&op.top()!='('&&pr[op.top()]>=pr[c]) eval();
            op.push(c);
        }
    }
    while(op.size()) eval();
    cout<<num.top()<<endl;

    return 0;
}

4.队列

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第32张图片

5. 考试考记忆力与自制力

6.单调栈

6.1考点

所有元素在其左侧或者右侧,离他最近的最大或者最小值。
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第33张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第34张图片

6.2 暴力做法

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第35张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第36张图片

6.3 优化算法

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第37张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第38张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第39张图片

6.4代码

#include
#include
using namespace std;
const int N=1e5+10;
int stk[N];
int tt=0;
int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int x;
        scanf("%d",&x);
        while(tt&&stk[tt]>=x) tt--;
        if(tt)  printf("%d ",stk[tt]);
        else printf("-1 ");
        stk[++tt]=x;
    }
    return 0;
}

7.单调队列(滑动窗口)

遇到滑动窗口,就可以想单调队列
在这里插入图片描述
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第40张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第41张图片

7.1 暴力做法

保证每次存放与窗口大小相等的元素。
时间复杂度o(nk) 含有的元素个数n,窗口大小k。
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第42张图片

7.2 优化

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第43张图片
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第44张图片
一个严格单调上升的序列的最小值是队头

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第45张图片
求最小值,只需要取队头。

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第46张图片

#include
#include
using namespace std;
const int N=1e6+10;
//a是数据 q是队列,队列中存储的是下标
int a[N],q[N];
int main()
{
//n数据量  k滑动窗口大小
    int n,k;
    scanf("%d%d",&n,&k);
    int hh=0,tt=-1;
    for(int i=0;i<n;i++) scanf("%d",&a[i]);

    for(int i=0;i<n;i++)
    {
   	   //判断队列是否已经滑出了窗口
   	   //队列中存储的是下标
   	  //右端点是i 左端点是i-k+1 如果大于->滑出窗口
   	  //出队
        if(hh<=tt&&i-k+1>q[hh]) hh++;
       //当前插入的值a[i]比队尾(a[q[tt]])的值要小 队尾出队
        while(hh<=tt&&a[q[tt]]>=a[i]) tt--;
        //插入新元素
        q[++tt]=i;
        //对于前k个数求滑动窗口口,不足k个不需要输出
        //单调上升队列 队头即为最小值
        if(i>=k-1) printf("%d ",a[q[hh]]);
    }
    cout<<endl;

    hh=0,tt=-1;
    for(int i=0;i<n;i++)
    {
        if(hh<=tt&&i-k+1>q[hh]) hh++;
        while(hh<=tt&&a[q[tt]]<=a[i]) tt--;
        q[++tt]=i;
        //单调下降队列,队头即为最大值
        if(i>=k-1) printf("%d ",a[q[hh]]);
    }
    cout<<endl;
}


7.3单调栈和单调队列的总结

首先用栈和队列暴力的做法模拟算法。删除不必要的元素,观察新的元素是否具有单调性。可以通过取队头的方式找最大值和最小值。以及二分的方法查找某个元素。

8、KMP

8.1 什么是kmp以及暴力算法

在主串中寻找子串 s主串 p模板串
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第47张图片

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第48张图片

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第49张图片
找最长前缀与后缀 王道专业术语:部分匹配值
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第50张图片

8.2 模板串找最长后缀

对于模板串中的每一个点都需要枚举出来最长的后缀
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第51张图片

8.3 主串与模板串的比较

算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第52张图片

8.4 kmp匹配过程代码(主串与模板串比较)

 for (int i = 1, j = 0; i <= m; i ++ )
    {
    //为什么j=0。是将s[i]与p[j+1]相比较,则p应该向前错一位,j=0
    /*在比较过程中,当两者不想等时就让p向后退,
    直到j=0时为止 */
    	//j不等于0->不为初始状态
    	//s[i]!=p[j+1] 红色圆和绿色圆不相等
        while (j && s[i] != p[j + 1]) j = ne[j];
        if (s[i] == p[j + 1]) j ++ ;
        //匹配成功
        if (j == n)
        {
            printf("%d ", i - n);
            //见图中的解释
            j = ne[j];
        }
    }

解释j=ne[j]
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第53张图片

8.5 求next数组(模板串找最长后缀)

图片模拟的是当p[i]!=p[j+1]时,p一直向后退的过程。
前提是在之前的比较过程中积累了最长后缀的数值。
这个过程也适合主串与模板串的比较过程。
算法基础值之单链表、双链表、栈、队列、单调栈、单调队列以及KMP_第54张图片

8.6 求next数组代码

	//ne[1]=0 第一个字母失败了只能从头开始, 从2开始即可
 for (int i = 2, j = 0; i <= n; i ++ )
    {
        while (j && p[i] != p[j + 1]) j = ne[j];
        if (p[i] == p[j + 1]) j ++ ;
        ne[i] = j;
    }

8.7 kmp实现代码

#include 

using namespace std;

const int N = 100010, M = 1000010;

int n, m;
int ne[N];
char s[M], p[N];

int main()
{
    cin >> n >> p + 1 >> m >> s + 1;

    for (int i = 2, j = 0; i <= n; i ++ )
    {
        while (j && p[i] != p[j + 1]) j = ne[j];
        if (p[i] == p[j + 1]) j ++ ;

        if(p[i+1]!=p[j+1]) ne[i] = j;
        else ne[i]=ne[j];
    }

    for (int i = 1, j = 0; i <= m; i ++ )
    {
        while (j && s[i] != p[j + 1]) j = ne[j];
        if (s[i] == p[j + 1]) j ++ ;
        if (j == n)
        {
        	//题目中下标从0开始,代码中下标从1开始
        	//i-n即可 不需要+1
            printf("%d ", i - n);
            j = ne[j];
        }
    }

    return 0;
}
 

你可能感兴趣的:(算法基础,算法,链表,数据结构)