算法基础第二章

news2024/11/27 20:58:30

算法基础第二章

  • 第二章:数据结构
    • 1、链表
      • 1.1、单链表(写邻接表:存储图和树)
      • 1.2、双链表(优化某些问题)
    • 2、栈与队列
    • 2.1、栈
      • 2.1.1、数组模拟栈
      • 2.1.2、单调栈
    • 2.2、队列
      • 2.2.1、数组模拟队列
      • 2.2.2、滑动窗口(单调队列的使用)
    • 3、KMP(字符串匹配)
    • 4、Trie树(高效地存储和查找字符串集合的数据结构)
    • 5、并查集
    • 6、堆
    • 7、Hash表
      • 7.1、字符串前缀哈希(KMP的劲敌)
    • 8、STL常用容器(用的时候查一下就行了)
      • 8.1、vector
      • 8.3、pair<int,int>
      • 8.2、string
      • 8.3、queue(队尾进队头出,固定方向)
      • 8.4、priority_queue(优先队列就是堆,默认是大根堆)
      • 8.5、stack
      • 8.6 deque(双端队列)
      • 8.7 set、map、multiset、multimap(基于平衡二叉树(红黑树),动态维护有序序列)
      • 8.8、unordered_set、unordered_map、unordered_multiset、unordered_multimap(哈希表)
      • 8.9、bitset

第二章:数据结构

1、链表

  • 笔试中遇到需要链表写法解题的话尽量用数组替代,而不是用new创建节点,new非常的费时间

1.1、单链表(写邻接表:存储图和树)

  • 解析:如下四句输入,在0前面插入1的时候,0不是链表中的节点,插入链表尾,ne[0]是头指针,里面存着1,意思是头指针指向1节点,整个过程如下图所示。ne保存的是指向下一个元素的位置值,e保存的是自己本身的值。
    在这里插入图片描述在这里插入图片描述
  • 题目链接:数组模拟链表
  • 代码:这个代码使用了数组,并未使用stl,但并没有全a,后面输入数据过大不给调试,望路过的大佬能够补充
#include <iostream>
#include<vector>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 10010;
typedef pair<long long,int>PII;

int n;
PII tmp[N];//保存元素的位置和该元素的值
long long e[N];//存储元素值得数组
int ne[N];//存储元素指向的下一个位置
int cur = 1;//当前链表的位置

void init()
{
    memset(tmp,0x7fffffff,sizeof tmp);//初始化为最大值
}

int findx(int cur,int x)
{
    while(cur>=0)
    {
        if(tmp[cur].first==x)
        {
            return tmp[cur].second;//返回x元素在数组中的位置
        }
        cur--;
    }
    return -1;
}

int findy(int cur,int x)
{
    while(cur>=0)
    {
        if(e[ne[cur]] == x)
        {
            return cur;//返回指向x元素的元素的位置
        }
        cur--;
    }
    return -1;
}

void insert(int x,int y)
{
    auto t = findx(cur,x);//这个是寻找x自己所在的位置
    auto u = findy(cur,x);//这个是寻找指向x的元素所在的位置
    if(t != -1 && u != -1)//链表里面有这个节点,则在x前插入y,且排除之前插入后又被删除的元素
    {
        e[cur] = y;
        ne[cur] = t;
        tmp[cur].first = y,tmp[cur].second = cur;
        ne[u] = cur,cur++;
    }
    else {//链表里面没这个节点,则在链表末尾插入这个元素,还要分是不是空链表
        if(ne[0])//非空链表
        {
            e[cur] = y;
            tmp[cur].first = y,tmp[cur].second = cur;
            ne[cur] = 0;
            ne[cur-1] = cur,cur++;
        }
        else {//空链表,需要处理头指针
            e[cur] = y;
            ne[0] = cur;//头指针指向当前位置
            tmp[cur].first = y,tmp[cur].second = cur;
            ne[cur] = 0;
            cur++;
        }
    }
}

void del(int x)
{
    auto t = findy(cur,x);
    auto u = findx(cur,x);
    if(t != -1)
    {
        ne[t] = ne[u];//将指向需要被删除的元素的指针指向需要被删除的元素指向的元素
    }
}

int main() {
    init();
    scanf("%d",&n);
    char str[7];
    int x,y,z;
    while(n--)
    {
        scanf("%s",str);
        if(!strcmp(str,"insert"))
        {
            scanf("%d%d",&x,&y);
            insert(x, y);
        }
        else {
            scanf("%d",&z);
            del(z);
        }
    }
    if(ne[0])//链表为非空
    {
        for(int i=ne[0];i;i=ne[i])
        {
            printf("%lld ",e[i]); 
        }
    }
    else {
        printf("NULL");
    }
    return 0;
}
// 64 位输出请用 printf("%lld")

1.2、双链表(优化某些问题)

  • 解析:
  • 题目链接:
  • 代码

2、栈与队列

2.1、栈

2.1.1、数组模拟栈

  • 解析:用一个top指针来进行栈的弹出与压入,压栈top加,出栈top减
    在这里插入图片描述
  • 题目链接:数组模拟栈
  • 代码
#include <iostream>
#include<string>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 100010;
int s[N];//栈数组
int n;
int top;//top指针

int main() {
    char opt[5];//输入的操作字符串
    int num;//如果是push则还有输入操作数
    scanf("%d",&n);
    for(int i = 0; i < n; i++)
    {
        scanf("%s",opt);
        if(!strcmp(opt,"push"))//比较成功strcmp返回0
        {
            scanf("%d",&num);
            s[++top] = num;//数组的0的位置用来标识栈为空,0位置不存元素,这是压栈
        }
        else if(!strcmp(opt,"pop"))
        {
            if(top)
            {
                printf("%d\n",s[top--]);//top指针减1相当于出栈
            }
            else {
                printf("error\n");
            }
        }
        else {
            if(top)
            {
                printf("%d\n",s[top]);
            }
            else {
                printf("error\n");
            }
        }
    }
    return 0;
}

2.1.2、单调栈

  • 解析:在一个可能包含重复数字的序列中,找到每个数左边最近比它小的数,栈的压入和弹出如下图所示
    在这里插入图片描述
  • 题目链接:单调栈的应用
  • 代码
#include <array>
class Solution {
public:
    vector<vector<int>> foundMonotoneStack(vector<int>& nums) {
        // write code here.
        const int N = 100010;
        int n = nums.size();
        int l[N],r[N],tl=0,tr=0;
        vector<vector<int>>arry(n,vector<int>(2));
        for(int i = 0; i < n; i++)
        {
            while(tl && nums[l[tl]] >= nums[i])
                tl--;
            if(tl)
                arry[i][0] = l[tl];
            else
                arry[i][0] = -1;
            l[++tl] = i;
        }
        for(int i = n-1; i >= 0; i--)
        {
            while(tr && nums[r[tr]] >= nums[i])
                tr--;
            if(tr)
                arry[i][1] = r[tr];
            else
                arry[i][1] = -1;
            r[++tr] = i;
        }
        return arry;
    }
};

2.2、队列

2.2.1、数组模拟队列

  • 解析:通过一个头指针和尾指针来进行队列的压入与弹出,压入的时候队尾指针加,弹出的时候队头的指针加,front的位置是h+1
    在这里插入图片描述
  • 题目链接:数组模拟队列
  • 代码
#include <iostream>
#include <cstring>

using namespace std;

const int N = 100010;
int q[N];//队列数组
int n;
int hh,tt;//队头队尾指针

int main() {
    scanf("%d",&n);
    char opt[5];
    int num;
    for(int i = 0; i < n; i++)
    {
        scanf("%s",opt);
        if(!strcmp(opt,"push"))
        {
            scanf("%d",&num);
            q[++tt] = num;//从队尾插入,所以队尾指针先加1在放入元素
        }
        else if(!strcmp(opt,"pop"))
        {  
            if(hh == tt)//队头指针和队尾指针相等的时候表示队列是空的
            {
                printf("error\n");
            }
            else {
                printf("%d\n",q[++hh]);//从队头弹出元素,队头指针先加1再弹出
            }
        }
        else {
            if(hh != tt)
            {
                printf("%d\n",q[hh+1]);//非空的时候,front的元素为头指针加1的位置
            }
            else {
                printf("error\n");
            }
        }
    }
}

2.2.2、滑动窗口(单调队列的使用)

  • 解析:这个问题一般就三步:1、什么时候队头需要前进;2、队头的最大值或者最小值是否要更新;3、什么时候输出或者保存队头的最大或最小值
    在这里插入图片描述
  • 题目链接:滑动窗口
  • 代码
class Solution {
public:
    vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
        const int N = 10010;
        int n = num.size();
        int q[N],front=0,tail=-1;
        vector<int>ret;
        for (int i = 0; i < n; i++) {
            int a = i - size + 1;//单独写这一句,直接写在if里面会出错,i是int,size是unsigned int,当是负数的时候会出问题
            //超出了滑动窗口的个数front就要加加
            if (front <= tail && a > q[front]) front++;
            //要输出最大值,则队头维护的是窗口里面的最大值
            while (front <= tail && num[i] >= num[q[tail]]) tail--;
            q[++tail] = i;
            if (i >= size - 1) ret.push_back(num[q[front]]);
        }
        return ret;
    }
};

3、KMP(字符串匹配)

  • 解析:解析,较抽象
  • 题目链接:KMP算法
  • 代码:
#include <iostream>
#include<string>

using namespace std;

const int N = 500010;

int main() {
    
    string str1,str2;
    cin >> str1 >> str2;
    char S[N],P[N];
    bool flag = false;
    for(int i = 0; i < str1.length();i++)
    {
        S[i+1] = str1[i];
    }    
    for(int i = 0; i < str2.length();i++)
    {
        P[i+1] = str2[i];
    }
    int ne[N];
    for(int i = 2,j = 0; i <= str2.length(); i++)
    {
        while(j && P[i] != P[j+1]) j = ne[j];
        if(P[i] == P[j+1]) j++;
        ne[i] = j;
    }

    for(int i = 1,j = 0;i <= str1.length(); i++)
    {
        while(j && S[i] != P[j+1]) j = ne[j];
        if(S[i] == P[j+1]) j++;
        if(j == str2.length())
        {
            flag = true;
            cout << i-str2.length() << " ";
            j = ne[j];
        }
    }
    if(flag)
        return 0;
    else
        cout << -1;
}

4、Trie树(高效地存储和查找字符串集合的数据结构)

  • 解析:trie树的存储方式如下所示,从根节点开始,依次找子节点,没有就新创建一个,并在每个单词的末尾打上一个标记,表示这是一个完整的单词
    在这里插入图片描述
  • 题目链接:字典树的实现
  • 代码

5、并查集

  • 解析:每个集合的根节点就是这个集合的编号,初始每个节点各自为一个集合,合并1,2这两个集合只需要让2作为1的父节点,节点1里面存的就是2这个父节点的值
    在这里插入图片描述
  • 题目链接:并查集的实现
  • 代码:一下几次提交结果是使用cin和cout以及scanf和printf的结果,大数据量的时候使用cin读入和cout读出特别费时间,所以最好使用scanf和printf以节省时间。
    在这里插入图片描述
#include <iostream>
using namespace std;

const int N = 1000010;
int n,m;
int p[N];//存储父节点的树,用数组替代

/* find这部分的代码是最重要的 */
int find(int x)
{
    if(p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main() {
    scanf("%d%d",&n,&m);
    //初始的时候每个节点独此为一个集合,所以存储的父节点就是自己
    for(int i = 1; i <= n; i++)
    {
        p[i] = i;
    }
    while(m--)
    {
        int opt,a,b;
        scanf("%d%d%d",&opt,&a,&b);
        if(opt == 1)
        {
            if(find(a) == find(b)) printf("Yes\n");
            else printf("No\n");
        }
        else if(opt == 2)
        {
        	//合并两个子集就是将b作为a的父节点
            p[find(a)] = find(b);
        }
    }
}

6、堆

  • 解析:整个的原理如下所示,堆本质是一颗完全二叉树,但可以通过数组来实现存储。不管是插入还是删除操作,做完之后都要针对该位置重新更新一下排序
    在这里插入图片描述
  • 题目链接:堆模板
  • 代码
#include <iostream>
#include<cstring>
#include<algorithm>
#include<vector>

using namespace std;

const int N = 100010;
int arry[N],len;//数组存储,len数组里面的个数

void up(int x)
{
	//向上排,当前元素比父节点的元素大,则当前元素与父节点的元素交换
    while(x/2 && arry[x] > arry[x/2])
    {
        swap(arry[x],arry[x/2]);
        x >>= 1;//当前元素变成父节点
    }
}

void down(int x)
{
    int t = x;
    if(x*2 <= len && arry[x*2] > arry[t]) t = x * 2;
    if(x*2+1 <= len && arry[x*2+1] > arry[t]) t = x * 2 + 1;
    if(t != x)
    {
        swap(arry[t],arry[x]);
        down(t);
    }
}

int main() {
    int n;
    scanf("%d",&n);
    char opt[5];
    int num;
    for(int i = 0; i < n; i++)
    {
        scanf("%s",opt);
        if(!strcmp(opt,"push"))
        {   
            scanf("%d",&num);
            arry[++len] = num;
            up(len);
        }
        else if(!strcmp(opt,"pop"))
        {
            if(len)
            {
                printf("%d\n",arry[1]);
                arry[1] = arry[len--];
                down(1);
            }
            /* 最后一个元素覆盖根节点元素,再down一遍 */
            else {
                printf("%s\n","empty");
            }
        }
        else if(!strcmp(opt,"top")){
            /* top操作 */
            if(len)
                printf("%d\n",arry[1]);//直接输出顶元素
            else 
                printf("%s\n","empty");
        }
    }
    return 0;
}

7、Hash表

  • 解析:

7.1、字符串前缀哈希(KMP的劲敌)

  • 解析:
  • 题目链接:
  • 代码

8、STL常用容器(用的时候查一下就行了)

8.1、vector

  • size和empty所有的都有这个,但clear有的没有
    • size():返回元素个数
    • empty():是否为空
  • clear():清空
  • front()/back()
  • push_back()/pop_back()
  • begin()/end()
  • []:随机选址
  • 支持比较运算(按字典序)

8.3、pair<int,int>

  • first、second、支持比较运算(以first为第一关键字,second为第二关键字)
  • p = make_pair(10,“lxh”);

8.2、string

  • size()/length()
  • empty()、clear()
  • substr(a,b):返回a开始的长度为b的字串
  • c_str():返回string 的首地址
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<string>

using namespace std;

int main()
{
    string s = "lxh";
    cout << s.substr(1,2) << endl;
    printf("%s",s.c_str());
    return 0;
}

8.3、queue(队尾进队头出,固定方向)

  • **size()、empty()、没有clear() **
  • push():向队尾插入一个元素
  • front():返回队头元素
  • back():返回队尾元素
  • pop():弹出队头元素

8.4、priority_queue(优先队列就是堆,默认是大根堆)

  • push():插入一个元素
  • top():返回堆顶元素
  • pop():弹出堆顶元素
  • 定义小根堆
priority_queue<int,vector<int>,greater<int>>hp;//小根堆

8.5、stack

  • 没有clear()

8.6 deque(双端队列)

8.7 set、map、multiset、multimap(基于平衡二叉树(红黑树),动态维护有序序列)

8.8、unordered_set、unordered_map、unordered_multiset、unordered_multimap(哈希表)

8.9、bitset

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/524680.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

操作系统实验二 进程(线程)同步

前言 实验二相比实验一难度有所提升&#xff0c;首先得先掌握好相应的理论知识&#xff08;读者-写者问题和消费者-生产者问题&#xff09;&#xff0c;才能在实验中得心应手。任务二的代码编写可以借鉴源码&#xff0c;所以我们要先读懂源码。 1.实验目的 掌握Linux环境下&a…

linux系统状态检测命令

1、ifconfig命令 用于获取网卡配置于状态状态的等信息&#xff1a; ens33:网卡名称 inet:ip地址 ether:网卡物理地址&#xff08;mac地址&#xff09; RX、TX:接收数据包与发送数据包的个数及累计流量 我们也可以直接通过网卡名称查对应信息&#xff1a; 2、查看系统版本的…

设计模式 - 工厂 Factory Method Pattern

文章参考来源 一、概念 创建简单的对象直接 new 一个就完事&#xff0c;但对于创建时需要各种配置的复杂对象例如手机&#xff0c;没有工厂的情况下&#xff0c;用户需要自己处理屏幕、摄像头、处理器等配置&#xff0c;这样用户和手机就耦合在一起了。 可以使代码结构清晰&a…

【人工智能】— 贝叶斯网络

【人工智能】— 贝叶斯网络 频率学派 vs. 贝叶斯学派贝叶斯学派Probability&#xff08;概率&#xff09;:独立性/条件独立性&#xff1a;Probability Theory&#xff08;概率论&#xff09;:Graphical models &#xff08;概率图模型&#xff09;什么是图模型&#xff08;Grap…

【每日一题/哈希表运用题】1054. 距离相等的条形码

⭐️前面的话⭐️ 本篇文章介绍【距离相等的条形码】题解&#xff0c;题目标签【哈希表】&#xff0c; 【贪心】&#xff0c;【优先级队列】&#xff0c;展示语言c/java。 &#x1f4d2;博客主页&#xff1a;未见花闻的博客主页 &#x1f389;欢迎关注&#x1f50e;点赞&#…

【计算机网络复习】第四章 网络层 2

源主机网络层的主要工作 路由器网络层的主要工作 目的主机网络层的主要工作 网络层提供的服务 o 屏蔽底层网络的差异&#xff0c;向传输层提供一致的服务 虚电路网络 o 虚电路网络提供面向连接的服务 n 借鉴了电路交换的优点 n 发送数据之前&#xff0c;源主机和目的主机…

MTK耳机识别

MTK耳机检测分为Eint only和EintAccdet 其中主流的是Eint Accdet(multi-key)。 图为MTK 耳机相关电路图的主要部分。 其中&#xff0c;左右声道的33pF主要滤除TDD干扰。串的10R100nf下地电容为低通滤波器。磁珠主要影响的是Fm以及音频THD性能。 Eint&#xff1a;检测耳机是否…

网络基础知识(3)——初识TCP/IP

首先给大家说明的是&#xff0c;TCP/IP 协议它其实是一个协议族&#xff0c;包含了众多的协议&#xff0c;譬如应用层协议 HTTP、 FTP、MQTT…以及传输层协议 TCP、UDP 等这些都属于 TCP/IP 协议。 所以&#xff0c;我们一般说 TCP/IP 协议&#xff0c;它不是指某一个具体的网络…

Casdoor 开始

Casdoor 是一个基于 OAuth 2.0 / OIDC 的中心化的单点登录&#xff08;SSO&#xff09;身份验证平台&#xff0c;简单来说&#xff0c;就是 Casdoor 可以帮你解决用户管理的难题&#xff0c;你无需开发用户登录、注册等与用户鉴权相关的一系列功能&#xff0c;只需几个步骤进行…

C++多线程中共享变量同步问题

目录 1、互斥量 &#xff08;1&#xff09;std::mutex &#xff08;2&#xff09;std::recursive_mutex &#xff08;3&#xff09;std::timed_mutex 2、锁管理器 &#xff08;1&#xff09;std::lock_guardlk &#xff08;2&#xff09;std::unique_locklk &#xff0…

掌控MySQL并发:深度解析锁机制与并发控制

前一篇MySQL读取的记录和我想象的不一致——事物隔离级别和MVCC 讲了事务在并发执行时可能引发的一致性问题的各种现象。一般分为下面3种情况&#xff1a; 读 - 读情况&#xff1a;并发事务相继读取相同的记录。读取操作本身不会对记录有任何影响&#xff0c;不会引起什么问题&…

【C++】C++中的多态

目录 一.多态的概念二.多态的定义及实现2.1虚函数2.2虚函数的重写虚函数重写的两个例外 2.3多态的构成条件2.4C11 override 和final2.5重载、重写、隐藏的对比 三.抽象类3.1概念3.2接口继承和实现继承 四.多态的原理4.1虚函数表4.2多态的原理(1)代码分析(2)清理解决方案 4.3动态…

MySQL高阶语句与连接

目录 高级查询selectDISTINCTWHEREAND ORINBETWEEN通配符与likeORDER BY数学函数聚合函数字符串函数mysql进阶查询GROUP BYHAVING别名子查询EXISTS连接查询inner join(内连接)left join(左连接)right join(右连接)自我连接 高级查询 实验准备&#xff1a; 第一张表&#xff1a…

Cesium入门之六:Cesium加载影像图层(ArcGIS、Bing、Mapbox、高德地图、腾讯地图、天地图等各类影像图)

Cesium加载影像图层 一、ImageryLayer类常用属性常用方法 二、ImageryLayerCollection类常用属性常用方法 三、ImageryProvider类常用属性常用方法 四、ImageryProvider子类1. ArcGisMapServerImageryProvider加载ArcGIS地图服务 2. BingMapsImageryProvider加载BingMap地图服务…

call to non-‘constexpr‘ function

文章目录 call to non-constexpr function概述备注END call to non-‘constexpr’ function 概述 在尝试迁移 openpnp - Smoothieware project 从gcc命令行 MRI调试方式 到NXP MCUXpresso工程. 在加了头文件路径后, 还有一些语法错误. 这和编译器语法有关系. 在运行BuildShe…

阿里云服务器部署flask项目「gunicorn + nginx + 支持https」

最近做了一个微信小程序&#xff0c;使用 flask 实现了对应的后台&#xff0c;上线需要部署到服务器上&#xff0c;之前只是了解并没有全链路试过&#xff0c;靠着网上的资料最终完成部署上线&#xff0c;但中间遇到了较多的一些问题&#xff0c;网上的资料也比较零碎&#xff…

WPF MaterialDesign 初学项目实战(2)首页导航栏样式

其他内容 WPF MaterialDesign 初学项目实战&#xff08;0&#xff09;:github 项目Demo运行 WPF MaterialDesign 初学项目实战&#xff08;1&#xff09;首页搭建 MaterialDesign 确保运行了初学项目实战&#xff08;0&#xff09; MaterialDesign给我们提供了很多的样式库&…

微服务框架【笔记-Nacos环境隔离】

Nacos注册中心 环境隔离 - namespace Nacos 中服务存储和数据存储的最外层都是一个名为namespace的东西&#xff0c;用来做最外层隔离 Nacos默认的命名空间&#xff1a; 创建命名空间复制命名空间ID启动Orderservice服务&#xff0c;在nacos服务列表可以看到环境隔离之后的服…

vue实现电梯锚点导航

1、目标效果 最近喝了不少的咖啡、奶茶&#xff0c;有一个效果我倒是挺好奇怎么实现的&#xff1a; &#xff08;1&#xff09;点击左侧分类菜单&#xff0c;右侧滚动到该分类区域 &#xff08;2&#xff09;右侧滑动屏幕&#xff0c;左侧显示当前所处的分类区域 这种功能会出现…

Jmeter进阶使用:BeanShell实现接口前置和后置操作

一、背景 我们使用Jmeter做压力测试或者接口测试时&#xff0c;除了最简单的直接对接口发起请求&#xff0c;很多时候需要对接口进行一些前置操作&#xff1a;比如提前生成测试数据&#xff0c;以及一些后置操作&#xff1a;比如提取接口响应内容中的某个字段的值。举个最常用…