• <数据结构> - 数据结构在算法比赛中的应用(上)


    目录

    单链表

    双链表

    单调栈

    单调队列&滑动窗口

    KMP字符串


    单链表
     

    思路:

    工程链表:

    1. typedef struct SListNode
    2. {
    3. int data; // val
    4. struct SListNode* next; // 存储下一个节点的地址
    5. }SLN;

     算法表示法: 

    head 表示头结点的下标,数组e[]表示链表 date值,ne[]表示存储下一个节点的地址的指针next,idx 存储当前已经用到了哪个点

    1. #include
    2. using namespace std;
    3. const int N = 100010;
    4. // head 表示头结点的指针
    5. // e[i] 表示节点i的值
    6. // ne[i] 表示节点i的next指针是多少
    7. // idx 存储当前已经用到了哪个点,工程链表中的新地址
    8. int head, e[N], ne[N], idx;
    9. // 初始化
    10. void init()
    11. {
    12. head = -1; //-1表示指向空
    13. idx = 0; //下标索引从0开始
    14. }
    15. // 将x插到头结点
    16. void add_to_head(int x)
    17. {
    18. e[idx] = x, ne[idx] = head, head = idx ++ ;
    19. }
    20. // 将x插到下标是k的点后面
    21. void add(int k, int x)
    22. {
    23. e[idx] = x, ne[idx] = ne[k], ne[k] = idx ++ ;
    24. }
    25. // 将下标是k的点后面的点删掉
    26. void remove(int k)
    27. {
    28. ne[k] = ne[ne[k]]; //让结点直接指向下一个结点的next,不用管内存泄漏
    29. }
    30. int main()
    31. {
    32. int m;
    33. cin >> m;
    34. init();
    35. while (m -- )
    36. {
    37. int k, x;
    38. char op;
    39. cin >> op;
    40. if (op == 'H')
    41. {
    42. cin >> x;
    43. add_to_head(x);
    44. }
    45. else if (op == 'D')
    46. {
    47. cin >> k;
    48. if (!k) head = ne[head]; //如果k为0,删除头结点,ne[head]必指向空
    49. else remove(k - 1); //k-1对应从0开始的idx
    50. }
    51. else
    52. {
    53. cin >> k >> x;
    54. add(k - 1, x);
    55. }
    56. }
    57. for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';
    58. cout << endl;
    59. return 0;
    60. }

    双链表

     思路:

    与单链表类似,e[N]存值,l[N]、r[N]表示左右指针

    双链表初始化:

    0号店表示头结点,1号表示尾节点

    1. r[0] = 1, l[1] = 0;
    2. idx = 2;

    删除节点a的remove()函数

    1. void remove(int a)
    2. {
    3. l[r[a]] = l[a];
    4. r[l[a]] = r[a];
    5. }

     在节点k的右边插入一个数x方法

    第一步:开一个新节点,左右指针指向k,与k的下一个节点

    第二步:先让k的下一个节点的左指针指向新点,再用k的右指针指向新点;顺序搞错会导致数据覆盖 

    1. void insert(int a, int x)
    2. {
    3. e[idx] = x;
    4. l[idx] = a, r[idx] = r[a];
    5. l[r[a]] = idx, r[a] = idx ++ ;
    6. }

    1. #include
    2. using namespace std;
    3. const int N = 100010;
    4. int m;
    5. int e[N], l[N], r[N], idx;
    6. // 在节点a的右边插入一个数x
    7. void insert(int a, int x)
    8. {
    9. e[idx] = x;
    10. l[idx] = a, r[idx] = r[a];
    11. l[r[a]] = idx, r[a] = idx ++ ;
    12. }
    13. // 删除节点a
    14. void remove(int a)
    15. {
    16. l[r[a]] = l[a];
    17. r[l[a]] = r[a];
    18. }
    19. int main()
    20. {
    21. cin >> m;
    22. // 0是左端点,1是右端点
    23. r[0] = 1, l[1] = 0;
    24. idx = 2;
    25. while (m -- )
    26. {
    27. string op;
    28. cin >> op;
    29. int k, x;
    30. if (op == "L")
    31. {
    32. cin >> x;
    33. insert(0, x);
    34. }
    35. else if (op == "R")
    36. {
    37. cin >> x;
    38. insert(l[1], x);
    39. }
    40. else if (op == "D")
    41. {
    42. cin >> k;
    43. remove(k + 1); //idx从2开始,插入节点夹在head与tail之间
    44. }
    45. else if (op == "IL")
    46. {
    47. cin >> k >> x;
    48. insert(l[k + 1], x);
    49. }
    50. else
    51. {
    52. cin >> k >> x;
    53. insert(k + 1, x);
    54. }
    55. }
    56. for (int i = r[0]; i != 1; i = r[i]) cout << e[i] << ' ';
    57. cout << endl;
    58. return 0;
    59. }

    单调栈

    在这里插入图片描述

    cin,cout速度大幅提高方法: 

    1. cin.tie(0);
    2. ios::sync_with_stdio(false);
    1. #include
    2. using namespace std;
    3. const int N = 100010;
    4. int stk[N], tt;
    5. int main()
    6. {
    7. //cin.tie(0);
    8. // ios::sync_with_stdio(false);
    9. int n;
    10. cin >> n;
    11. while (n -- )
    12. {
    13. int x;
    14. cin>>x;
    15. while (tt && stk[tt] >= x) tt -- ; //不符合,出栈
    16. if (!tt) cout<<"-1"<<" ";
    17. else cout<" ";
    18. stk[ ++ tt] = x; //当前值入栈,与下一个数比较
    19. }
    20. return 0;
    21. }

    单调队列&滑动窗口

    思路:

    • 利用双端队列思想
    • 设 队列q[hh],q[tt]分别表示窗口左边界(队头)与右边界(队尾),存储下标
    • 用 i 表示窗口进程,则窗口范围【i-k+1,i】

    (先求最小值)根据滑动窗口性质,队头的数会先消失,如果队尾插入的值比前一个数小,则前数不是最小值,所以直到出窗口也不会被输出

    核心操作:如果队尾插入的值比前一个数小,那么将前一个数移出队列,最终队列会形成单调递增,取最小值永远在q[hh]处取

    1. #include
    2. using namespace std;
    3. const int N = 1000010;
    4. int a[N], q[N];
    5. int main()
    6. {
    7. int n, k;
    8. scanf("%d%d", &n, &k);
    9. for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
    10. int hh = 0, tt = -1; //分别表示窗口左边界与右边界
    11. for (int i = 0; i < n; i ++ )
    12. {
    13. if (hh <= tt && i - k + 1 > q[hh]) hh ++ ; //队首出窗口,hh++
    14. while (hh <= tt && a[q[tt]] >= a[i]) tt -- ; //队列不满足单调,tt--将元素移出
    15. q[ ++ tt] = i; //将新元素下标入队尾
    16. if (i >= k - 1) printf("%d ", a[q[hh]]); //满足遍历个数大于窗口k值,输出队头
    17. }
    18. puts("");
    19. hh = 0, tt = -1;
    20. for (int i = 0; i < n; i ++ )
    21. {
    22. if (hh <= tt && i - k + 1 > q[hh]) hh ++ ;
    23. while (hh <= tt && a[q[tt]] <= a[i]) tt -- ; //对于最大值,直接改为单调递减即可
    24. q[ ++ tt] = i;
    25. if (i >= k - 1) printf("%d ", a[q[hh]]);
    26. }
    27. puts("");
    28. return 0;
    29. }

    KMP字符串

    一个人能走的多远不在于他在顺境时能走的多快,而在于他在逆境时多久能找到曾经的自己。
                                                                                                                                          ------- KMP

    1. #include
    2. using namespace std;
    3. const int N=100010,M=1000010;
    4. int n,m;
    5. int ne[N];
    6. char s[M],p[N];
    7. int main()
    8. {
    9. cin>>n>>p+1>>m>>s+1;
    10. for(int i=2,j=0;i<=n;i++) //构造next数组
    11. {
    12. while(j&&p[i]!=p[j+1]) j=ne[j];
    13. if(p[i]==p[j+1]) j++;
    14. ne[i]=j;
    15. }
    16. for(int i=1,j=0;i<=m;i++)
    17. {
    18. while(j&&s[i]!=p[j+1]) j=ne[j];
    19. if(s[i]==p[j+1]) j++;
    20. if(j==n)
    21. {
    22. printf("%d ",i-n);
    23. //j=ne[j]; 找到后直接跳过j段
    24. }
    25. }
    26. return 0;
    27. }

  • 相关阅读:
    容器服务(三)自动化监控 Prometheus、Grafana
    给定一个数组,求任意个数的和,可能的结果。
    数据库管理工具Navicat与DBeaver,谁是你心目中的白月光?
    Elasticsearch:使用 Docker 来安装 FSCrawler 并摄入 Word 及 PDF 文件
    SVN常用操作
    32 道 Spring 常见面试题!万字总结!
    Stream()流常用方法
    IDEA小技巧:Debug条件断点
    .net 转java 项目管理浅谈
    C++:AVL树
  • 原文地址:https://blog.csdn.net/qq_61386381/article/details/126513706