链式前向星,字典树(Trie),01字典树,cin关闭流同步的与cout的endl使用

目录:

一.链式前向星

二.字典树(Trie)

三.01字典树

四:cin关闭流同步的与cout的endl使用


 一. 链式前向星:
 图的存储方法很多,最常见的除了邻接矩阵、邻接表和边集数组外,还有链式前向星
 链式前向星是一种静态链表存储,用边集数组和邻接表相结合,
 可以快速访问一个顶点的所有邻接点,在算法竞赛中广泛应用。
 1.边集数组:
 边集数组是由两个一维数组构成。一个是存储顶点的信息;
 另一个是存储边的信息。
 这个边数组每个数据元素由一条边的起点下标(begin)、终点下标(end)和权(weight)组成。
 性质:
  边集数组关注的是边的集合,
 在边集数组中要查找一个顶点的度需要扫描整个边数组,效率并不高。
 因此它更适合对边依次进行处理的操作,而不适合对顶点相关的操作。
 2.前向星:
 前向星是一种特殊的边集数组,
 我们把边集数组中的每一条边按照起点从小到大排序,
 如果起点相同就按照终点从小到大排序,
 并记录下以某个点为起点的所有边在数组中的起始位置和存储长度, 
 那么前向星就构造好了.
 3. 链式前向星存储包括两种结构:
 边集数组:edge[],edge[i]表示第i条边;
 头结点数组:head[],head[i]存以i为起点的第一条边的下标(在edge[]中的下标)

 struct node{
int to, next, w;
}edge[maxe];//边集数组,边数一般要设置比maxn*maxn大的数,如果题目有要求除外
int head[maxn];//头结点数组


 4.添加一条边u v w的代码如下:

 void add(int u,int v,int w){//添加一条边
edge[cnt].to = v;
edge[cnt].w = w;
edge[cnt].next = head[u];
head[u] = cnt++;
}


 5.使用链式前向星访问一个结点u的所有邻接点:

 for(int i=head[u];~i;i=edge[i].next){
int v = edge[i].to; //u的邻接点
int w = edge[i].w; //u—v的权值
…
}


 6.完整代码:

#include<iostream>//创建无向网的链式前向星 
#include<cstring>
using namespace std;
const int maxn = 100000 + 5;
int maxx[maxn], head[maxn];
int n, m, x, y, w, cnt;
struct Edge {
    int to, w, next;
}e[maxn];
void add(int u, int v, int w) {//添加一条边u--v 
    e[cnt].to = v;            //头插法
    e[cnt].w = w;              //每一个新节点的next始终指向头结点的next;
    e[cnt].next = head[u];    //头结点的next始终指向新结点
    head[u] = cnt++;
}
void printg() {//输出链式前向星
    cout << "----------链式前向星如下:----------" << endl;
    for (int v = 1; v <= n; v++) {
        cout << v << ":  ";
        for (int i = head[v]; ~i; i = e[i].next) {
            int v1 = e[i].to, w1 = e[i].w;
            cout << "[" << v1 << " " << w1 << "]\t";
        }
        cout << endl;
    }
}
int main() {
    cin >> n >> m;
    memset(head, -1, sizeof(head));
    cnt = 0;
    for (int i = 1; i <= m; i++) {
        cin >> x >> y >> w;
        add(x, y, w);//添加边
        add(y, x, w);//添加反向边 
    }
    printg();
    return 0;
}


二:字典树(Trie):
 字典树(Trie)是一个比较简单的数据结构,也叫前缀树,用来存储和查询字符串。
其中每个字符占据一个节点,拥有相同前缀的字符串可以共用部分节点。
 起始点是特殊点(我们设为1号点),不存储字符。
 建树的代码如下:

const int MAXN = 500005;
int Next[MAXN][26], cnt; // 用类似链式前向星的方式存图,
                 //Next[i][c]表示i号点所连、存储字符为c+'a'的点的编号
void init() // 初始化
{
    memset(Next, 0, sizeof(Next)); // 全部重置为0,表示当前点没有存储字符
    cnt = 1;
}
void insert(const string& s) // 插入字符串
{
    int cur = 1;
    for (auto c : s){
        // 尽可能重用之前的路径,如果做不到则新建节点
        if (!next[cur][c - 'a'])
            next[cur][c - 'a'] = ++cnt;
        cur = next[cur][c - 'a']; // 继续向下
    }
}
字典树可以方便地查询某个前缀是否存在:
bool find_prefix(const string& s) // 查找某个前缀是否出现过
{
    int cur = 1;
    for (auto c : s)
    {
        // 沿着前缀所决定的路径往下走,如果中途发现某个节点不存在,
             //说明前缀不存在
        if (!next[cur][c - 'a'])
            return false;
        cur = next[cur][c - 'a'];
    }
    return true;
}


洛谷P2580 于是他错误的点名开始了
对于每个教练报的名字,输出一行。
如果该名字正确且是第一次出现,
输出 OK,如果该名字错误,输出 WRONG,
如果该名字正确但不是第一次出现,输出 REPEAT。

#include <iostream>
using namespace std;
const int MAXN = 500005;
namespace trie
{//解决不同模块命名冲突的问题
    int next[MAXN][26], cnt;// 用类似链式前向星的方式存图,
    bool vis[MAXN], exist[MAXN];//next[i][c]表示i号点所连、存储字符为c+'a'的点的编号
    void init()             //next前一维相当于头结点,充当索引功能
    {
        memset(next, 0, sizeof(next));// 全部重置为0,表示当前点没有存储字符
        cnt = 1;
    }
    void insert(const string& s)
    {
        int cur = 1;
        for (auto c : s)
        {// 尽可能重用之前的路径,如果做不到则新建节点
            if (!next[cur][c - 'a'])
                next[cur][c - 'a'] = ++cnt;
            cur = next[cur][c - 'a'];
        }
        exist[cur] = true;// 继续向下
    }
    int find(const string& s)
    {
        int cur = 1, ans;
        for (auto c : s)
        {
            if (!next[cur][c - 'a'])
                return 0;
            cur = next[cur][c - 'a'];
        }
        if (!exist[cur])
            ans = 0;
         if (!vis[cur])
            ans = 1;
        else
            ans = 2;
        vis[cur] = true;
        return ans;
    }
} // namespace trie
int main()
{
    int n;
    cin >> n;
    trie::init();
    while (n--)
    {
        string s;
        cin >> s;
        trie::insert(s);
    }
    int q;
    cin >> q;
    while (q--)
    {
        string s;
        cin >> s;
        switch (trie::find(s))
        {
        case 0:
            cout << "WRONG" << endl;
            break;
        case 1:
            cout << "OK" << endl;
            break;
        case 2:
            cout << "REPEAT" << endl;
        }
    }
    return 0;
}


三:01字典树
01字典树(01 - trie)是一种特殊的字典树,它的字符集只有{ 0,1 } ,
主要用来解决一些异或问题
(HDU4825 Xor Sum)
Zeus 和 Prometheus 做了一个游戏,
Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,
随后 Prometheus 将向 Zeus 发起M次询问,每次询问中包含一个正整数 S ,
之后 Zeus 需要在集合当中找出一个正整数 K ,使得 K 与 S 的异或结果最大。
Prometheus 为了让 Zeus 看到人类的伟大,随即同意 Zeus 可以向人类求助。
你能证明人类的智慧么?
思路:
数字化为二进制后,当作01串,从高位到低位像普通字典树那样存储
现在对于上面问题,我们贪心地解决即可。
如果我们要找与给定数异或最大的数,就尽可能走与该数当前位不同的路径。
反之则尽可能走与当前位相同的路径。

const int MAXN = 3200000, MAXBIT = 31;
int next[MAXN][2], cnt;
int num[MAXN];
void init()
{
    memset(next, 0, sizeof(next));
    memset(num, 0, sizeof(num));
    cnt = 1;
}
void insert(int n)
{
    int cur = 1;
    for (int i = MAXBIT; i >= 0; --i)
    {
        int bit = n >> i & 1; // 求出当前位并插入
        if (!next[cur][bit])
            next[cur][bit] = ++cnt;
        cur = next[cur][bit];
    }
    num[cur] = n;
}
int find_max(int x) // 找到与x异或最大的那个数
{
    int cur = 1;
    for (int i = MAXBIT; i >= 0; --i)
    {
        int bit = x >> i & 1;
        if (next[cur][bit ^ 1]) // 优先走与当前位不同的路径
            cur = next[cur][bit ^ 1];
        else
            cur = next[cur][bit];
    }
    return x ^ num[cur];
}


以上是int范围内的模板,并不能通过杭电这个题,因为它的数据范围大于unsigned int上界了,需要开long long。
 四:cin关闭流同步的与cout的endl使用
在算法题中涉及到大量数据读入的时候,
通常建议大家避免使用cin读入数据而改用scanf,原因是scanf相对速度更快。
解决:
1. cin效率低的原因一是在于默认cin与stdin总是保持同步,
cin会把要输出的东西先存入缓冲区,进而消耗时间。通过关闭同步,
可以有效提高cin效率;
2.. 默认情况下cin绑定的是cout,每次执行<<的时候都要调用flush,
进而增加IO负担,因此可以通过tie(0)解绑。

实现:

#include <iostream>
using namespace std;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
}


 注意:
 如果在同一个流上使用两组 I/O 函数(头文件<stdio.h>/<cstdio>和<iostream>)
 (例如stdin流同时关联cin与scanf),那么最好让它们保持同步;
 如果任何一个流只使用一个 I/O 系列,则可以关闭同步
 (比如在各自涉及单独的流的情况下,可以同时使用scanf与cin)。
3.每次使用endl,都要flush缓冲区,造成大量时间耗费。
推荐cout << << "\n"的写法。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
向星和链向星的区别在于实现方和数据结构的不同。 向星是一种存储图的边的数据结构,它使用两个数组来存储边的信息。一个数组存储每个顶点的第一条边的索引,另一个数组存储每条边的下一条边的索引。这种方可以方便地遍历每个顶点的所有边。 链向星是一种基于链表的存储方,它使用链表来存储每个顶点的边。每个顶点都有一个指向第一条边的指针,每条边都有一个指向下一条边的指针。这种方可以动态地添加和删除边。 总结来说,向星使用数组存储边的信息,链向星使用链表存储边的信息。链向星相比向星更加灵活,可以方便地进行边的插入和删除操作。但是链向星的空间复杂度较高,因为需要额外的指针来存储链表的连接关系。 引用\[1\]中提到,链向星的整体结构很像邻接表,但是实现方不同。链向星的思想和邻接表一致,只是在实现上有所区别。因此,链向星使用和邻接表相一致,可以用于存储和遍历图的边的信息。 #### 引用[.reference_title] - *1* *2* *3* [链向星](https://blog.csdn.net/MuShan_bit/article/details/123882339)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值