Trie 字典树 详解

article/2025/9/22 1:36:03
😊 | Powered By HeartFireY | Tire Algorithm

一、字典树

1.字典树简介

字典树,英文名Trie,如其名:就是一棵像字典一样的树。

我们首先通过一张图来理解字典树的结构:

在这里插入图片描述

我们假定结点的顺序按照图中给定的顺序进行编号,容易发现,在一个给定的树上,从每个根节点出发到达子节点的路径边代表一个字母。实际上,每个节点出发的几条边所代表的字母是从左到右的顺序按照字典序排列的。

那么我们可以知道:从根节点出发,到达某个指定的结点的路径可以构成一个字符串。

一个例子: 1 → 4 → 8 → 13 ⟹ c a a 1\to4\to 8\to 13 \Longrightarrow caa 14813caa

在这里插入图片描述

在实际应用中,我们在建立Trie字符集之前,应当首先确定边所映射的字符集,然后可以根据边的编号一一对应映射的字符构成目的字符串。

Trie 的结构非常好懂,我们用 δ ( u , c ) \delta(u,c) δ(u,c) 表示结点 u u u c c c 字符指向的下一个结点,或着说是结点 u u u 代表的字符串后面添加一个字符 c c c 形成的字符串的结点。( c c c 的取值范围和字符集大小有关,不一定是 0 ∼ 26 0\sim 26 026。)

有时需要标记插入进 Trie 的是哪些字符串,每次插入完成时在这个字符串所代表的节点处打上标记即可。

2.字典树的特性以及优缺点

1.优缺点

优点:我们不难发现字典树中实际上记录了多个字符串的公共前缀,因此用于查询公共前缀时是十分高效的,他减少了无意义的字符串匹配,其查询效率要优于哈希树。

缺点:字典树的内存消耗非常大。

我们不难发现,字典树的核心思想是用空间来换时间(利用公共前缀来降低查询时间的开销以达到提高效率的目的)。

2.特性

  1. 根节点不包含字符,除根结点之外每一个结点都只包含一个字符;
  2. 字典树用边表示字母表示
  3. 从根节点到某一结点, 路径上经过的字符连接起来,为该结点对应的字符串
  4. 每个节点的所有子结点包含的字符都不同。每个结点最多有26个子节点(假设给定字符集中包含26个英文字母)
  5. 有相同前缀的单词共用前缀节点
  6. 整棵树的根节点是空的,便于插入和查找
  7. 每个单词结束的时候用一个特殊字符表示,那么从根节点到任意一个特殊字符,所经过的边的所有字母表示一个单词

3.字典树的应用:

从字典树的结构不难发现:字典树可以用来保存大量的字符串(包括但不限于字符串),同时在一些高级的应用中可以用来统计和排序大量的字符串。在实际生产生活的应用中,我们经常将字典树用于文本的词频统计。

二、字典树操作及模板

1.字典树相关操作及实现

1).插入操作

基本思路:从左到右扫描这个单词,如果扫描指针指向的字母在相应的根节点下没有出现过,就插入这个字母;否则沿着字典树往下走,扫描指针后移。

在插入的过程中我们需要给每个字符进行编号。

我们设 t r e e [ i ] [ j ] = k tree[i][j] = k tree[i][j]=k,表示编号为 i i i的结点的第 j j j个孩子是编号为 k k k的结点。

这里需要特别注意结点的编号问题!

我们通常将编号 ( i , j ) (i,\ j) (i, j)分为两类:

实际编号 ( i , k ) (i,\ k) (i, k)即为我们在实际储存树结构时的编号,是相对于整棵树而言的;

关系编号:表示当前结点是结点 i i i的第 j j j个孩子,是相对于单个结点而言的。

因此我们可以知道:对于给定的 t r e e [ i ] [ j ] = k tree[i][j] = k tree[i][j]=k,表示那么实际编号为 ( i , k ) (i,\ k) (i, k),关系编号为 j j j

那么插入操作就应该分为以下的步骤:

  • 首先我们需要记录根结点的编号,初始化为0;
  • 从左到右扫描这个单词,每次计算关系编号 i d id id(在字符集{a~z}中,关系编号为’当前字符’ - ‘a’);
  • 如果之前没有从 r o o t root root i d id id的前缀,则插入该编号(需要预先在声明计数变量,对一种编号进行记录);
  • 执行 r o o t = t r e e [ r o o t ] [ i d ] root = tree[root][id] root=tree[root][id],表示顺着字典树继续向下走。

在这里插入图片描述

2).查找单词

我们可以使用一个数组 v i s [ s t r . l e n ( ) ] vis[str.len()] vis[str.len()]记录到字符第 i i i个字符为止的串是否存在。 v i s [ i ] vis[i] vis[i]表示节点 i i i 是否是单词结束的标志。

搜索完成后直接返回 v i s [ r o o t ] vis[root] vis[root]的值即可。

3).前缀出现次数的记录

在需要统计前缀个数的题目中,我们额外引入一个 s u m [ ] sum[] sum[]数组,表示位置 i 被访问过的次数

那么最后返回 s u m [ r o o t ] sum[root] sum[root]即可,插入操作中每访问一个节点,都要让其对应的 s u m [ i ] + + sum[i]++ sum[i]++

但值得注意的是:此处前缀次数标记在前缀的最后一个字母对应位置的后一个位置上

在这里插入图片描述

例如上图中的字符串前缀"abc",那么对应储存的 s u m sum sum值如图所示。

2.字典树模板

这里放一个用结构体封装的模板:

在使用数组进行储存的时候,由于数组的空间是预先分配好的,因此不需要单独进行释放。

struct trie{int tree[100000][26], cnt;bool exist[100000];			//以该结点结尾的字符串是否存在void insert(char *s, int l){int root = 0;for(int i = 0; i < l; i++){int id = s[i] - 'a';if(!tree[root][id]) tree[root][id] = ++cnt;	//如果之前没有从root到id的前缀,那么就插入root = tree[root][id];}exist[root] = 1;//sum[root]++; //前缀数记录}bool find(char *s, int l){int root = 0;for(int i = 0; i < l; i++){int id = s[i] - 'a';if(!tree[root][id]) return 0;root = tree[root][id];}return exist[root];}
};

下面是一个采用动态内存申请的Trie模板,在这种方式下,空间的利用效率相对更高一些。

struct trie{//代表当前节点可以延伸出来的边,采用动态内存申请,提升空间利用率trie *Next[maxn];//标记当前节点是否保存信息的结尾,也可以代表前缀的个数int flag;trie(){flag = 1; //初始化以该信息为前缀的信息个数memset(Next, NULL, sizeof(Next));}
} * root;//1、插入操作
void Insert(string s){int len = s.size();trie *p = root, *q;//将s的每一个字符插入到trie树for (int i = 0; i < len; i++){int id = s[i] - 'a';//如果没有边,则新建一个trie节点,产生一个新的边代表该字符if (p->Next[id] == NULL){q = new trie();p -> Next[id] = q;p = p -> Next[id];}//如果存在则继续往下走else{p = p->Next[id];(p -> flag)++;}}
}//2、查询操作
int Query(string s){int len = s.size();trie *p = root;//在trie树上顺序搜索s的每一个字符for (int i = 0; i < len; i++){int id = s[i] - 'a';p = p->Next[id];//如果是空集,表示不存在以此为前缀的信息if (p == NULL) return 0;}//返回以该信息为前缀的信息的个数return p->flag;
}//3、删除操作
void Free(trie *t){if (t == NULL) return;//逐个释放结点所占用的内存for (int i = 0; i < maxn; i++)if (t -> Next[i]) free(t -> Next[i]);delete (t);
}

注:以上模板参考自博客_链接

三、字典树的高级应用

1.字典树::排序算法

在许多场景下,我们需要对多个字符串进行排序;我们都知道在传统排序算法下时的时间复杂度为 O ( n log ⁡ n ) O(n\log{n}) O(nlogn),而如果我们通过字典树对给定的串进行排序,则可以将时间复杂度降至 O ( n ) O(n) O(n)

那么如何利用字典树对多个字符串进行排序呢?

首先,我们根据多个字符串建立Trie树,之后,通过先序遍历此树,即可得到字符串从小到大的顺序。

如何证明这个过程?我们都知道在字典树中,我们对于每个节点的出边是按照字典序进行编号,因此在执行先序遍历的时候会首先访问字典序较小的结点,容易得知经过先序遍历可得到有序的字符串组合。

这里给出使用指针建树的先序遍历方式:

void trie_sort(trie *root){if(!root) return;if(root -> flag){ cout << root -> s << endl; return; }for(int i = 0; i < 26; i++) trie_sort(root -> tree[i]);
}
//注:我们假定每个尾结点中储存以该结点结尾的字符串s

2.字典树::维护异或极值

⚠ 此部分内容为扩展内容,难度相对较高,请确保掌握字典树后再深入学习

将数的二进制表示看作一个字符串,就可以建立字符集为 { 0 , 1 } \{0, 1\} {0,1}的Trie树。

首先我们假设场景:给定一颗带边权的树,求 ( u , v ) (u,v) (u,v)使得 u u u v v v路径上的边权异或和最大,输出这个最大值。

数据范围:点数不超过 1 0 5 10^5 105,边权在 [ 0 , 2 31 ) [0, 2^{31}) [0,231)内。

如何使用Trie解决这个问题?

我们随便指定一个根 r o o t root root,用 T ( u , v ) T(u, v) T(u,v) 表示 u u u v v v 之间的路径的边权异或和,那么 T ( u , v ) = T ( r o o t , u ) ⊕ T ( r o o t , v ) T(u,v)=T(root, u)\oplus T(root,v) T(u,v)=T(root,u)T(root,v),因为 L C A LCA LCA以上的部分异或两次抵消了。

那么,如果将所有 T ( r o o t , u ) T(root, u) T(root,u) 插入到一棵 Trie中,就可以对每个 T ( r o o t , u ) T(root, u) T(root,u) 快速求出和它异或和最大的 T ( r o o t , v ) T(root, v) T(root,v)
从 trie 的根开始,如果能向和 T ( r o o t , u ) T(root, u) T(root,u) 的当前位不同的子树走,就向那边走,否则没有选择。

贪心的正确性:如果这么走,这一位为 1 1 1;如果不这么走,这一位就会为 0 0 0。而高位是需要优先尽量大的。

#include <bits/stdc++.h>
using namespace std;const int N = 100010;int head[N], nxt[N << 1], to[N << 1], weight[N << 1], cnt;
int n, dis[N], ch[N << 5][2], tot = 1, ans = 0;void insert(int x){for(int i = 30, u = 1; i >= 0; i--){int c = ((x >> i) & 1);if(!ch[u][c]) ch[u][c] = ++tot;u = ch[u][c];}
}void get(int x){int res = 0;for(int i = 30, u = 1; i >= 0; i--){int c = ((x >> i) & 1);if(ch[u][c ^ 1]){u = ch[u][c ^ 1];res |= (1 << i);}else u = ch[u][c];}ans = max(ans, res);
}void add(int u, int v, int w){nxt[++cnt] = head[u];head[u] = cnt;to[cnt] = v; weight[cnt] = w;
}void dfs(int u, int fa){insert(dis[u]);get(dis[u]);for(int i = head[u]; i; i = nxt[i]){int v = to[i];if(v == fa) continue;dis[v] = dis[u] ^ weight[i];dfs(u, v);}
}signed main(){ios_base::sync_with_stdio(false), cin.tie(0), cout.tie(0);cin >> n;for(int i = 1; i < n; i++){int u, v, w;cin >> u >> v >> w;add(u, v, w);add(v, u, w);}dfs(1, 0);cout << ans << endl;return 0;
}

3.字典树::维护异或和(0-1 Trie)

⚠ 此部分内容为扩展内容,难度相对较高,请确保掌握字典树后再深入学习

相似的,我们可以通过字典树来维护异或和。通常把这种维护异或和的字典树称为0-1 Trie。0-1 Trie可以用来维护一些数字的异或和,同时支持修改(删除+重新插入),和全局+1操作。

如果需要维护异或和,我们需要按值从低位到高位建立Trie。

1).插入 & 删除

如果要维护异或和,我们 只需要 知道某一位上 01 个数的 奇偶性 即可,也就是对于数字 1 来说,当且仅当这一位上数字 1 的个数为奇数时,这一位上的数字才是 1,请时刻记住这段文字:如果只是维护异或和,我们只需要知道某一位上 1 的数量即可,而不需要知道 Trie 到底维护了哪些数字。

对于每一个节点,我们需要记录以下三个量:

  • ch[o][0/1] 指节点 o 的两个儿子,ch[o][0] 指下一位是 0,同理 ch[o][1] 指下一位是 1
  • w[o] 指节点 o 到其父亲节点这条边上数值的数量(权值)。每插入一个数字 xx 二进制拆分后在 trie 上 路径的权值都会 +1
  • xorv[o] 指以 o 为根的子树维护的异或和。

具体维护结点的代码如下所示。

void maintain(int o){w[o] = xorv[o] = 0;if (ch[o][0]){w[o] += w[ch[o][0]];xorv[o] ^= xorv[ch[o][0]] << 1;}if (ch[o][1]){w[o] += w[ch[o][1]];xorv[o] ^= (xorv[ch[o][1]] << 1) | (w[ch[o][1]] & 1);}// w[o] = w[o] & 1;// 只需知道奇偶性即可,不需要具体的值。当然这句话删掉也可以,因为上文就只利用了他的奇偶性。
}

插入和删除的代码非常相似。

需要注意的地方就是:

  • 这里的 MAXH 指 trie 的深度,也就是强制让每一个叶子节点到根的距离为 MAXH。对于一些比较小的值,可能有时候不需要建立这么深(例如:如果插入数字 4,分解成二进制后为 100,从根开始插入 001 这三位即可),但是我们强制插入 MAXH 位。这样做的目的是为了便于全局 +1 时处理进位。例如:如果原数字是 311),递增之后变成 4100),如果当初插入 3 时只插入了 2 位,那这里的进位就没了。

  • 插入和删除,只需要修改叶子节点的 w[] 即可,在回溯的过程中一路维护即可。

namespace trie{const int MAXH = 21;int ch[_ * (MAXH + 1)][2], w[_ * (MAXH + 1)], xorv[_ * (MAXH + 1)];int tot = 0;int mknode(){++tot;ch[tot][1] = ch[tot][0] = w[tot] = xorv[tot] = 0;return tot;}void maintain(int o){w[o] = xorv[o] = 0;if (ch[o][0]){w[o] += w[ch[o][0]];xorv[o] ^= xorv[ch[o][0]] << 1;}if (ch[o][1]){w[o] += w[ch[o][1]];xorv[o] ^= (xorv[ch[o][1]] << 1) | (w[ch[o][1]] & 1);}w[o] = w[o] & 1;}void insert(int &o, int x, int dp){if (!o) o = mknode();if (dp > MAXH) return (void)(w[o]++);insert(ch[o][x & 1], x >> 1, dp + 1);maintain(o);}void erase(int o, int x, int dp){if (dp > 20) return (void)(w[o]--);erase(ch[o][x & 1], x >> 1, dp + 1);maintain(o);}
} 

2).全局加一

所谓全局加一就是指,让这棵 trie 中所有的数值 +1

形式化的讲,设 trie 中维护的数值有 V 1 , V 2 , V 3 … V n V_1, V_2, V_3 \dots V_n V1,V2,V3Vn, 全局加一后 其中维护的值应该变成 V 1 + 1 , V 2 + 1 , V 3 + 1 … V n + 1 V_1+1, V_2+1, V_3+1 \dots V_n+1 V1+1,V2+1,V3+1Vn+1

void addall(int o) {swap(ch[o][0], ch[o][1]);if (ch[o][0]) addall(ch[o][0]);maintain(o);
}

我们思考一下二进制意义下 +1 是如何操作的。

我们只需要从低位到高位开始找第一个出现的 0,把它变成 1,然后这个位置后面的 1 都变成 0 即可。

下面给出几个例子感受一下:(括号内的数字表示其对应的十进制数字)

1000(10)  + 1 = 1001(11)  ;
10011(19) + 1 = 10100(20) ;
11111(31) + 1 = 100000(32);
10101(21) + 1 = 10110(22) ;
100000000111111(16447) + 1 = 100000001000000(16448);

对应 trie 的操作,其实就是交换其左右儿子,顺着 交换后0 边往下递归操作即可。

回顾一下 w[o] 的定义:w[o] 指节点 o 到其父亲节点这条边上数值的数量(权值)。

有没有感觉这个定义有点怪呢?如果在父亲结点存储到两个儿子的这条边的边权也许会更接近于习惯。但是在这里,在交换左右儿子的时候,在儿子结点存储到父亲这条边的距离,显然更加方便。

3).01-trie 合并

指的是将上述的两个 01-trie 进行合并,同时合并维护的信息。

可能关于合并 trie 的文章比较少,其实合并 trie 和合并线段树的思路非常相似,可以搜索“合并线段树”来学习如何合并 trie。

其实合并 trie 非常简单,就是考虑一下我们有一个 int merge(int a, int b) 函数,这个函数传入两个 trie 树位于同一相对位置的结点编号,然后合并完成后返回合并完成的结点编号。

考虑怎么实现?
分三种情况:

  • 如果 a 没有这个位置上的结点,新合并的结点就是 b

  • 如果 b 没有这个位置上的结点,新合并的结点就是 a

  • 如果 a,b 都存在,那就把 b 的信息合并到 a 上,新合并的结点就是 a,然后递归操作处理 a 的左右儿子。

    提示:如果需要的合并是将 a,b 合并到一棵新树上,这里可以新建结点,然后合并到这个新结点上,这里的代码实现仅仅是将 b 的信息合并到 a 上。

int merge(int a, int b) {if (!a) return b;  // 如果 a 没有这个位置上的结点,返回 bif (!b) return a;  // 如果 b 没有这个位置上的结点,返回 a/*如果 `a`, `b` 都存在,那就把 `b` 的信息合并到 `a` 上。*/w[a] = w[a] + w[b];xorv[a] ^= xorv[b];/* 不要使用 maintain(),maintain() 是合并a的两个儿子的信息而这里需要 a b 两个节点进行信息合并*/ch[a][0] = merge(ch[a][0], ch[b][0]);ch[a][1] = merge(ch[a][1], ch[b][1]);return a;
}

其实 trie 都可以合并,换句话说,trie 合并不仅仅限于 01-trie。

4.AC自动机

⚠ 此部分转移到单独的新博客

5.可持久化字典树

⚠ 此部分转移到单独的新博客

http://chatgpt.dhexx.cn/article/r1jnmEM9.shtml

相关文章

Web前端面试题汇总(持续更新...)

H5 的新特性有哪些&#xff1f;C3 的新特性有哪些&#xff1f; H5 新特性 拖拽释放(Drap and drop) API ondrop自定义属性 data-id语义化更好的内容标签(header,nav,footer ,aside, article, section)音频 ,视频(audio, video) 如果浏览器不支持自动播放怎么办?在属性中添加…

Web前端面试题(全锦集)

1 第一部分&#xff1a; 聪明的猴子都在看右下角目录 点击查看更多资源 前端基础(HTML CSS JS基础) 1. 怎么让一个不定宽高的 DIV&#xff0c;垂直水平居中&#xff1f; 答&#xff1a;1.使用 CSS 方法&#xff1a; 父盒子设置&#xff1a;display&#xff1a;table…

web前端开发面试题(一)

一、html部分 1.1 link和import 区别如下&#xff1a; 1.1.1从属关系区别 import是 CSS 提供的语法规则&#xff0c;只有导入样式表的作用&#xff1b;link是HTML提供的标签&#xff0c;不仅可以加载 CSS 文件&#xff0c;还可以定义 RSS、rel 连接属性等。 2.加载顺序区别…

Web常见前端面试题及答案

前端技术导航大全 1、盒子模型 盒子模型包括四部分&#xff1a;内容&#xff08;content&#xff09;、填充&#xff08;padding&#xff09;、边框&#xff08;border&#xff09;、边界&#xff08;margin&#xff09; 盒子模型可以分为两种&#xff1a;IE盒子模型和W3C标准…

web前端开发面试题(七)

前端面试题第七天 一、HTML 部分 1.1 iframe框架都有哪些优缺点 在讲iframe框架之前 先聊聊iframe吧 定义&#xff1a;iframe是HTML标签&#xff0c;作用是文档中的文档&#xff0c;或者浮动的框架(FRAME)。iframe元素会创建包含另外一个文档的内联框架&#xff08;即行内框…

2020 web前端面试题及答案大全

css相关 1. 万能居中 1.margin: 0 auto;水平 2.text-align: center;水平 3.行高&#xff0c;垂直 4.表格&#xff0c;center,middle&#xff1b;水平垂直 5.display:table-cell&#xff1b;模拟表格&#xff0c;all 6.绝对定位&#xff0c;50%减自身宽高 7.绝对定位&#xff…

初级web前端面试题

文章目录 一、JS1、js基本类型和引用类型2、如何判断js数据类型3、js 拷贝4、事件处理机制5、原型和原型链6、什么是闭包7、事件循环机制&#xff08;event loop&#xff09;8、前端模块化9、es6新增特性1.let代替var关键字&#xff1b;2.const3.箭头函数4.字符串模板 &#xf…

【面试】web前端经典面试题试题及答案(持续更新)-html/css

html/ css css盒模型、BFC、css浮动、css经典布局、css兼容、css hack、html/ css基础 css盒模型BFCcss浮动css经典布局自适应css兼容css hackhtml/ css基础css3 transformcss实战图片 #mermaid-svg-vRGce0x6PUoXllsG .label{font-family:trebuchet ms, verdana, arial;font-fa…

前端面试题2021及答案

身为三本的我就是凭借这些前端面试题拿到百度京东offer的&#xff0c;前端面试题2021及答案... 此文转载自&#xff1a;https://blog.csdn.net/qq_33277654/article/details/112758362#commentBox 点进来之后你的噩梦就要来了&#xff0c;接下来你要面对上百道面试题&#xff…

web前端面试题(全)

近来看到网上格式各样的web前端求职的面试题&#xff0c;接下来我用我的经验总结了一套在面试过程中高频率问到的面试题&#xff0c;希望能帮助各位求职者在求职的过程中顺利通过&#xff0c;废话不多说&#xff0c;直接说题。。。 一、HTML5部分 1.说一下对css盒模型的理解 …

2023最新Web前端经典面试试题及答案-史上最全前端面试题(含答案)

近期总结一一些面试题 都是企业的面试题笔记题 感觉薪资10k-15k的常见面试题 个人录制的最新Vue项目学习视频&#xff1a;B站 Vue2-第二版-后台管理系统项目实战/vueelement-ui/vue经典全套系统案例讲解_哔哩哔哩_bilibili 前端面试题视频讲解&#xff1a; 2023前端高频…

Web前端面试:这40个经典Web前端面试题面试者必看!

想成功就业Web前端工程师&#xff0c;想要能高薪就业&#xff0c;那么除了好的Web前端技能以外&#xff0c;还得有好的面试技巧&#xff0c;如果提前就了解更多企业的面试要求及面试题目&#xff0c;那么可以让我们的面试成功的几率大大的提高。今天千锋武汉Web前端培训小编为大…

最新Web前端面试题精选大全及答案

目录 HTML、CSS相关 Javascript相关 三者的异同 Vue相关 55.Vue路由懒加载&#xff08;按需加载路由&#xff09; React相关 react 生命周期函数 ******为什么虚拟 dom 会提高性能?(必考) (组件的)状态(state)和属性(props)之间有何不同 shouldComponentUpdate 是做…

50道web前端工程师面试题及答案解析,你学会了吗

简介&#xff1a;本文包含了50个实用的前端面试题及答案解析&#xff0c;涵盖了HTML、CSS、JavaScript、DOM、Ajax、MVC、模块化、ES6、SPA、Webpack、Babel、Virtual DOM、响应式设计、移动优先设计、响应式图片、CSS 预处理器、后处理器、模块化、布局、盒模型、浮动、定位、…

web前端面试题(必背面试题)

必背面试题-手写题 前端面试&#xff08;手写题&#xff09;_Z_Xshan的博客-CSDN博客 css系列 面试官&#xff1a;说说你对盒子模型的理解 一、是什么 所有元素都可以有像盒子一样的平面空间和外形 一个盒子由四部分组成&#xff1a;context ,padding,margin,border con…

指针数组与数组指针的区别

a、指针数组&#xff1a;是指一个数组里面装着指针&#xff0c;也即指针数组是一个数组&#xff1b; 定义形式:int *a[10]&#xff1b; 如图所示: b、数组指针:是指一个指向数组的指针&#xff0c;它其实还是一个指针&#xff0c;只不过是指向数组而已&#xff1b; 定义形式…

指针数组和数组指针的简单理解

图解 指针数组&#xff0c;重点在数组 数组指针&#xff0c;重点在指针 例子&#xff1a; include <iostream>using namespace std;int main() { int c[2][4]{1,2,3,4,5,6,7,8}; int *a[4]; //指针数组 int (*b)[4]; //数组指针 bc; //将数组c中元素赋给数组a for(int …

C指针数组和数组指针

一、指针数组和数组指针的内存布局 初学者总是分不出指针数组与数组指针的区别。其实很好理解&#xff1a; 指针数组&#xff1a;首先它是一个数组&#xff0c;数组的元素都是指针&#xff0c;数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。 数组指针&#…

c++数组指针和指针数组详解

指针数组&#xff1a; 指针数组可以说成是”指针的数组”&#xff0c;首先这个变量是一个数组&#xff0c;其次&#xff0c;”指针”修饰这个数组&#xff0c;意思是说这个数组的所有元素都是指针类型&#xff0c;在32位系统中&#xff0c;指针占四个字节。 数组指针&#xf…

C语言指针数组

一、指针数组概念回顾&#xff1a; 一个数组&#xff0c;其元素均为指针类型数据&#xff0c;称为指针数组。即&#xff1a;指针数组中每一个元素都是指针变量。 指针数组的定义格式: 类型标识符 *数组名(数字长度说明); 如&#xff1a; int *p[4]; //每个数组元素都可以看…