LeetCode·每日一题·1851. 包含每个查询的最小区间·优先队列(小顶堆)

题目

 

示例

 

思路

  • 离线查询:
    •  输入的结果数组queries[]是无序的。如果我们按照输入的queries[]本身的顺序逐个查看,时间复杂度会比较高。 于是,我们将queries[]数组按照数值大小,由小到大逐个查询,这种方法称之为离线查询。
  • 位运算:
    •  离线查询的时候,queries[]可以按照数值大小逐个查看,但最终返回的结果数组,是需要按照queries[]相同的顺序输出的,也就是说,queries[]数组的下标信息不能被丢失。 此时,我们可以考虑,将每一个queries[x]放在一个long int数值的高32位,将下标x放在这个long int数值的低32位。 由此,我们可以看出,这些新的long int数值,它们之间的相互大小关系,取决于高32位的数值,只有在高32位相等的时候,才需要比较低32位。如此,我们在比较大小时,优先看高32位,然后想要获取其下标信息时,直接取低32位。 类似的,我们在处理输入的intervals[]数组时也类似处理。将每一个intervals[x]的左边缘放在一个long int数值的高32位,右边缘放在这个long int数值的低32位。这样,这些long int数值,它们之间的相互大小关系,就取决于左边缘的大小关系,而想要获取右边缘时,直接取低32位即可。
  • 小根堆(优先队列):
    • ​​​​​​​ 我们要由小到大处理queries[]数组和intervals[]数组,可以考虑排序,也可以考虑小根堆,这里采用小根堆。

【具体实现】

  1. 将输入的intervals[]数组,每一个intervals[x]的左边缘放在一个long int数值的高32位,右边缘放在这个long int数组的低32位,然后long int数值push入小根堆intervalsHeap。
  2. 将输入的queries[]数组,每一个queries[x]的数值放在一个long int数值的高32位,下标x放在这个long int数值的低32位,然后long int数值push入小根堆queriesHeap。
  3. 逐个从queriesHeap中pop出堆顶元素,这个堆顶元素的高32位就是上面第2步push入堆时,每一个queries[x]的数值,赋值给变量position,低32位就是下标x。
    1. 3.1 从intervalsHeap中pop出所有左边缘(高32位)小于等于position的区间。
    2. 3.2 上面3.1中pop出的每一个区间,把所有右边缘(低32位)大于等于position的区间,都push入一个有效区间小根堆validHeap中。这个validHeap中,以区间的range为高32位,右边缘为低32位,以保证堆顶的区间range始终是最小的。
    3. 3.3 如果validHeap堆顶区间的右边缘(低32位)小于当前的position,则将其弹出。
    4. 3.4 经过上面3.3的操作后,如果validHeap仍然非空,则说明存在包含这个position的区间,且validHeap的堆顶区间的range(高32位)就是题目要求的结果。

代码

/* 几个全局定义的说明。
  INVALID_VALUE:            无效值-1。小根堆中,根节点的父节点使用这个无效值。题目中结果数组里的无效值也是这个-1。
  FATHER_NODE(x):           小根堆中,每一个节点的父节点。其中,根节点的父节点是无效值。
  LEFT_NODE(x):             小根堆中,每一个节点的左子节点。当它大于等于堆size的时候无效。
  RIGHT_NODE(x):            小根堆中,每一个节点的右子节点。当它大于等于堆size的时候无效。
  MERGE_LONG(x, y):         两个int数值合并成一个long int数值,其中x占据高32位,y占据低32位。
  GET_HIGHER32(x):          获取一个long int数值的高32位值。
  GET_LOWER32(x):           获取一个long int数值的低32位值。 */
#define INVALID_VALUE       -1
#define FATHER_NODE(x)      ((0 == (x)) ? INVALID_VALUE : ((x) - 1 >> 1))
#define LEFT_NODE(x)        (((x) << 1) + 1)
#define RIGHT_NODE(x)       (((x) << 1) + 2)
#define MERGE_LONG(x, y)    ((long int)(x) << 32 | (long int)(y))
#define GET_HIGHER32(x)     ((x) >> 32)
#define GET_LOWER32(x)      ((x) & 0xFFFFFFFF)

/* 小根堆的结构定义,里面包括堆空间数组arr[],和堆size。 */
typedef struct
{
    long int *arr;
    int arrSize;
}
HeapStruct;

static void heapPush(HeapStruct *heap, long int t);
static void heapPop(HeapStruct *heap);

/* 主函数的几个变量说明:
  x:                循环下标。
  position:         queries数组中的数值。因为这个数组会带着下标一起入堆,在出堆时,把它的实际数值取出到这个position。
  range:            题目中定义的一个左闭右闭区间的大小,等于“右边缘 - 左边缘 + 1”。
  t:                long int临时数值。
  arr1,arr2,arr3:   几个小根堆中要用到的堆空间数组。
  intervalsHeap:    将输入的intervals数组加入到这个小根堆中,左边缘放高32位,右边缘放低32位。
  queriesHeap:      将输入的queries数组加入到这个小根堆中,数值放高32位,下标放低32位。
  validHeap:        包含着当前position的有效区间的小根堆,区间range放高32位,右边缘放低32位。 */
int *minInterval(int **intervals, int intervalsSize, int *intervalsColSize, int *queries, int queriesSize, int *returnSize)
{
    int x = 0, position = 0, range = 0;
    long int t = 0;
    long int arr1[intervalsSize], arr2[queriesSize], arr3[intervalsSize];
    HeapStruct intervalsHeap, queriesHeap, validHeap;
    int *result = NULL;
    /* 申请结果数组的内存空间。 */
    result = (int *)malloc(sizeof(int) * queriesSize);
    if(NULL == result)
    {
        *returnSize = 0;
        return result;
    }
    /* 堆空间定义,初始化一开始堆都为空。 */
    intervalsHeap.arr = arr1;
    intervalsHeap.arrSize = 0;
    queriesHeap.arr = arr2;
    queriesHeap.arrSize = 0;
    validHeap.arr = arr3;
    validHeap.arrSize = 0;
    /* 把所有的区间放入堆中,左边缘为高优先级,右边缘为低优先级。 */
    while(intervalsSize > x)
    {
        t = MERGE_LONG(intervals[x][0], intervals[x][1]);
        heapPush(&intervalsHeap, t);
        x++;
    }
    /* 把所有的查询放入堆中,位置为高优先级,下标为低优先级。 */
    x = 0;
    while(queriesSize > x)
    {
        t = MERGE_LONG(queries[x], x);
        heapPush(&queriesHeap, t);
        x++;
    }
    /* 一个个查询逐个出堆。 */
    while(0 < queriesHeap.arrSize)
    {
        /* 把下标和位置列出,然后出堆。 */
        x = GET_LOWER32(queriesHeap.arr[0]);
        position = GET_HIGHER32(queriesHeap.arr[0]);
        heapPop(&queriesHeap);
        /* 把所有左边缘小于等于position的区间弹出。 */
        while(0 < intervalsHeap.arrSize && position >= GET_HIGHER32(intervalsHeap.arr[0]))
        {
            /* 右边缘大于等于position的区间放到有效堆中(右边缘小于position的不可能包含当前position)。
            这个有效堆以range为第一优先级,确保堆顶的区间是range最小的。 */
            if(position <= GET_LOWER32(intervalsHeap.arr[0]))
            {
                range = GET_LOWER32(intervalsHeap.arr[0]) - GET_HIGHER32(intervalsHeap.arr[0]) + 1;
                t = MERGE_LONG(range, GET_LOWER32(intervalsHeap.arr[0]));
                heapPush(&validHeap, t);
            }
            heapPop(&intervalsHeap);
        }
        /* 有效堆中,如果堆顶的右边缘小于position,则弹出。这里用到了延迟删除的思想。因为最小堆的pop操作只能针对堆顶。
        上面对validHeap进行push操作之后,堆顶的区间右边缘值可能已经小于当前的位置了,这种情况必须pop出去。 */
        while(0 < validHeap.arrSize && position > GET_LOWER32(validHeap.arr[0]))
        {
            heapPop(&validHeap);
        }
        /* 如果此时的有效堆非空,则当前查询的取值就是堆顶的区间大小,否则就是题目要求的-1。 */
        result[x] = (0 < validHeap.arrSize) ? GET_HIGHER32(validHeap.arr[0]) : INVALID_VALUE;
    }
    /* 返回数组长度等于queriesSize。 */
    *returnSize = queriesSize;
    return result;
}

/* 小根堆的push操作。
为确保其保持为一棵完整二叉树,新push入的数值初始放到堆尾,然后,根据父子节点的大小关系调整位置。 */
static void heapPush(HeapStruct *heap, long int t)
{
    int son = heap->arrSize, father = FATHER_NODE(son);
    heap->arrSize++;
    while(INVALID_VALUE != father && heap->arr[father] > t)
    {
        heap->arr[son] = heap->arr[father];
        son = father;
        father = FATHER_NODE(son);
    }
    heap->arr[son] = t;
    return;
}

/* 小根堆的pop操作。
堆顶弹出后,堆顶空缺,为确保其保持为一棵完整二叉树,将堆尾的数值补充到堆顶,然后,根据父子节点的大小关系调整位置。 */
static void heapPop(HeapStruct *heap)
{
    int father = 0, left = LEFT_NODE(father), right = RIGHT_NODE(father), son = 0;
    long int t = heap->arr[heap->arrSize - 1];
    heap->arrSize--;
    while((heap->arrSize > left && heap->arr[left] < t)
        || (heap->arrSize > right && heap->arr[right] < t))
    {
        son = (heap->arrSize > right && heap->arr[right] < heap->arr[left]) ? right : left;
        heap->arr[father] = heap->arr[son];
        father = son;
        left = LEFT_NODE(father);
        right = RIGHT_NODE(father);
    }
    heap->arr[father] = t;
    return;
}

作者:恒等式
链接:https://leetcode.cn/problems/minimum-interval-to-include-each-query/solutions/2026632/bao-han-mei-ge-cha-xun-de-zui-xiao-qu-ji-lxkj/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。/* 几个全局定义的说明。
  INVALID_VALUE:            无效值-1。小根堆中,根节点的父节点使用这个无效值。题目中结果数组里的无效值也是这个-1。
  FATHER_NODE(x):           小根堆中,每一个节点的父节点。其中,根节点的父节点是无效值。
  LEFT_NODE(x):             小根堆中,每一个节点的左子节点。当它大于等于堆size的时候无效。
  RIGHT_NODE(x):            小根堆中,每一个节点的右子节点。当它大于等于堆size的时候无效。
  MERGE_LONG(x, y):         两个int数值合并成一个long int数值,其中x占据高32位,y占据低32位。
  GET_HIGHER32(x):          获取一个long int数值的高32位值。
  GET_LOWER32(x):           获取一个long int数值的低32位值。 */
#define INVALID_VALUE       -1
#define FATHER_NODE(x)      ((0 == (x)) ? INVALID_VALUE : ((x) - 1 >> 1))
#define LEFT_NODE(x)        (((x) << 1) + 1)
#define RIGHT_NODE(x)       (((x) << 1) + 2)
#define MERGE_LONG(x, y)    ((long int)(x) << 32 | (long int)(y))
#define GET_HIGHER32(x)     ((x) >> 32)
#define GET_LOWER32(x)      ((x) & 0xFFFFFFFF)

/* 小根堆的结构定义,里面包括堆空间数组arr[],和堆size。 */
typedef struct
{
    long int *arr;
    int arrSize;
}
HeapStruct;

static void heapPush(HeapStruct *heap, long int t);
static void heapPop(HeapStruct *heap);

/* 主函数的几个变量说明:
  x:                循环下标。
  position:         queries数组中的数值。因为这个数组会带着下标一起入堆,在出堆时,把它的实际数值取出到这个position。
  range:            题目中定义的一个左闭右闭区间的大小,等于“右边缘 - 左边缘 + 1”。
  t:                long int临时数值。
  arr1,arr2,arr3:   几个小根堆中要用到的堆空间数组。
  intervalsHeap:    将输入的intervals数组加入到这个小根堆中,左边缘放高32位,右边缘放低32位。
  queriesHeap:      将输入的queries数组加入到这个小根堆中,数值放高32位,下标放低32位。
  validHeap:        包含着当前position的有效区间的小根堆,区间range放高32位,右边缘放低32位。 */
int *minInterval(int **intervals, int intervalsSize, int *intervalsColSize, int *queries, int queriesSize, int *returnSize)
{
    int x = 0, position = 0, range = 0;
    long int t = 0;
    long int arr1[intervalsSize], arr2[queriesSize], arr3[intervalsSize];
    HeapStruct intervalsHeap, queriesHeap, validHeap;
    int *result = NULL;
    /* 申请结果数组的内存空间。 */
    result = (int *)malloc(sizeof(int) * queriesSize);
    if(NULL == result)
    {
        *returnSize = 0;
        return result;
    }
    /* 堆空间定义,初始化一开始堆都为空。 */
    intervalsHeap.arr = arr1;
    intervalsHeap.arrSize = 0;
    queriesHeap.arr = arr2;
    queriesHeap.arrSize = 0;
    validHeap.arr = arr3;
    validHeap.arrSize = 0;
    /* 把所有的区间放入堆中,左边缘为高优先级,右边缘为低优先级。 */
    while(intervalsSize > x)
    {
        t = MERGE_LONG(intervals[x][0], intervals[x][1]);
        heapPush(&intervalsHeap, t);
        x++;
    }
    /* 把所有的查询放入堆中,位置为高优先级,下标为低优先级。 */
    x = 0;
    while(queriesSize > x)
    {
        t = MERGE_LONG(queries[x], x);
        heapPush(&queriesHeap, t);
        x++;
    }
    /* 一个个查询逐个出堆。 */
    while(0 < queriesHeap.arrSize)
    {
        /* 把下标和位置列出,然后出堆。 */
        x = GET_LOWER32(queriesHeap.arr[0]);
        position = GET_HIGHER32(queriesHeap.arr[0]);
        heapPop(&queriesHeap);
        /* 把所有左边缘小于等于position的区间弹出。 */
        while(0 < intervalsHeap.arrSize && position >= GET_HIGHER32(intervalsHeap.arr[0]))
        {
            /* 右边缘大于等于position的区间放到有效堆中(右边缘小于position的不可能包含当前position)。
            这个有效堆以range为第一优先级,确保堆顶的区间是range最小的。 */
            if(position <= GET_LOWER32(intervalsHeap.arr[0]))
            {
                range = GET_LOWER32(intervalsHeap.arr[0]) - GET_HIGHER32(intervalsHeap.arr[0]) + 1;
                t = MERGE_LONG(range, GET_LOWER32(intervalsHeap.arr[0]));
                heapPush(&validHeap, t);
            }
            heapPop(&intervalsHeap);
        }
        /* 有效堆中,如果堆顶的右边缘小于position,则弹出。这里用到了延迟删除的思想。因为最小堆的pop操作只能针对堆顶。
        上面对validHeap进行push操作之后,堆顶的区间右边缘值可能已经小于当前的位置了,这种情况必须pop出去。 */
        while(0 < validHeap.arrSize && position > GET_LOWER32(validHeap.arr[0]))
        {
            heapPop(&validHeap);
        }
        /* 如果此时的有效堆非空,则当前查询的取值就是堆顶的区间大小,否则就是题目要求的-1。 */
        result[x] = (0 < validHeap.arrSize) ? GET_HIGHER32(validHeap.arr[0]) : INVALID_VALUE;
    }
    /* 返回数组长度等于queriesSize。 */
    *returnSize = queriesSize;
    return result;
}

/* 小根堆的push操作。
为确保其保持为一棵完整二叉树,新push入的数值初始放到堆尾,然后,根据父子节点的大小关系调整位置。 */
static void heapPush(HeapStruct *heap, long int t)
{
    int son = heap->arrSize, father = FATHER_NODE(son);
    heap->arrSize++;
    while(INVALID_VALUE != father && heap->arr[father] > t)
    {
        heap->arr[son] = heap->arr[father];
        son = father;
        father = FATHER_NODE(son);
    }
    heap->arr[son] = t;
    return;
}

/* 小根堆的pop操作。
堆顶弹出后,堆顶空缺,为确保其保持为一棵完整二叉树,将堆尾的数值补充到堆顶,然后,根据父子节点的大小关系调整位置。 */
static void heapPop(HeapStruct *heap)
{
    int father = 0, left = LEFT_NODE(father), right = RIGHT_NODE(father), son = 0;
    long int t = heap->arr[heap->arrSize - 1];
    heap->arrSize--;
    while((heap->arrSize > left && heap->arr[left] < t)
        || (heap->arrSize > right && heap->arr[right] < t))
    {
        son = (heap->arrSize > right && heap->arr[right] < heap->arr[left]) ? right : left;
        heap->arr[father] = heap->arr[son];
        father = son;
        left = LEFT_NODE(father);
        right = RIGHT_NODE(father);
    }
    heap->arr[father] = t;
    return;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

《微服务架构设计模式》第十二章 部署微服务应用

内容总结自《微服务架构设计模式》 部署微服务应用 一、部署模式分类二、编程语言特定的发布包格式1、概述2、利弊 三、将服务部署为虚拟机1、概览2、利弊 四、将服务部署为容器1、概述2、利弊3、K8S部署 五、Serverless部署1、概述2、利弊3、示例 六、总结 一、部署模式分类 …

视频融合平台EasyCVR级联后上级平台播放失败的问题排查与优化

EasyCVR视频融合平台基于云边端智能协同架构&#xff0c;具有强大的数据接入、处理及分发能力&#xff0c;平台可提供视频监控直播、云端录像、云存储、录像检索与回看、智能告警、平台级联、云台控制等视频能力与服务&#xff0c;可支持多协议、多类型的海量设备接入与分发。 …

7、PHP语法要点2

1、or 和 ||&#xff0c;&& 和 and 都是逻辑运算符&#xff0c;效果一样&#xff0c;但是其优先级却不一样。&&、||的优先级在赋值运算符之前&#xff0c;or和and在赋值运算符之后。 2、字符串变量及数组可以在echo输出时双引号内、双引号外均可引用&#xff…

Meta提出全新参数高效微调方案,仅需一个RNN,Transformer模型GPU使用量减少84%!

近来&#xff0c;随着ChatGPT和GPT-4模型的不断发展&#xff0c;国内外互联网大厂纷纷推出了自家的大语言模型&#xff0c;例如谷歌的PaLM系列&#xff0c;MetaAI的LLaMA系列&#xff0c;还有国内公司和高校推出的一些大模型&#xff0c;例如百度的文心一言&#xff0c;清华的C…

迅镭激光赋能工程机械,客户连续复购激光加工设备达双赢!

工程机械是装备制造业的重要组成部分&#xff0c;当前&#xff0c;我国已成为门类齐全、规模庞大、基础坚实、竞争力强的工程机械设备制造大国。 随着工程机械产业正在全面向智能化、绿色化转型&#xff0c;激光加工成为推动工程机械产业转型升级的重要工具&#xff0c;越来越多…

CS162 11-12 调度与死锁

调度 overview 1.FCFS 可以利用好cache缓存&#xff0c;减少上下文切换。 2.很直观&#xff0c;贪心&#xff0c;可以减少平均的响应时间 3 4. 5.等待调度的时间是平均的 6.优先级翻转&#xff0c;和优先级捐赠 解决 cfs中的调度 死锁 四个必要不充分条件 银行家算法&…

《深度学习推荐系统》笔记

目录 一、推荐系统是什么1.作用和意义2.推荐系统的架构2.1 逻辑架构2.2 技术架构 二、传统的推荐系统方法1. 协同过滤算法1.1 userCF&&ItemCF1.3 矩阵分解算法 2. 逻辑回归算法3. 因子分解机3.1 POLY2模型3.2 FM模型3.3 FFM模型3.4 小结 4. 组合模型4.1 GBDTLR组合模型…

数学建模-多元线性回归分析

回归分析介绍和分类 数据分类及数据的来源 线性回归 四种模型的解释、虚拟变量的设置以及交互项的解释 3个定量&#xff0c;7个定类插入&#xff0c;表格&#xff0c;包含标题&#xff0c;标题换黑色 可以右键&#xff0c;复制表格&#xff0c;excel中设置三线表 ,gen(A)是参数…

Linux 部署Vue+Spring Boot项目

部署Vue Spring Boot项目 安装redis wget http://download.redis.io/releases/redis-4.0.8.tar.gz tar -zxvf redis-4.0.8.tar.gz yum install gcc-c make make install如果出现下面的问题&#xff1a; yum install tcl make testredis-server myconifg/redis.conf输入客户端…

WordPress作为可扩展的企业级解决方案

网络商业世界就像一片汪洋大海&#xff0c;大型企业是大海中最大的鱼。然而&#xff0c;只因为你比其他人都大&#xff0c;并不意味着你不能逆流而上。相反&#xff0c;企业业务面临的挑战更大&#xff0c;对网站的技术要求更高。 多年来&#xff0c;大型公司通常依赖最昂贵的…

不用显示器,不用鼠标和键盘,让我们用主机远程访问OK3588的桌面

不用显示器&#xff0c;不用鼠标和键盘&#xff0c;让我们用主机远程访问OK3588的桌面 MobaXterm软件介绍串口终端运行命令MobaXterm访问开发板 MobaXterm软件介绍 MobaXterm是一款增强型终端软件&#xff0c;对于Windows平台上的程序员、网络管理员和开发者是一款极其优秀的工…

用 pesq 给 torchaudio 读取的音频数据打分

用torchaudio读取的音频文件&#xff0c;在输入pesq之前需要进行格式处理与转换。 import torchaudio from pesq import pesq# 读取音频文件 audio_clean, src torchaudio.load(./audio/NOIZEUS/clean/sp01.wav) audio_0dB, sr0 torchaudio.load(./audio/NOIZEUS/bable/0dB/…

基于FPGA的按键消抖

文章目录 基于FPGA的按键消抖一、按键消抖原理二、按键消抖代码三、仿真代码编写四&#xff1a;总结 基于FPGA的按键消抖 一、按键消抖原理 按键抖动&#xff1a;按键抖动通常的按键所用开关为机械弹性开关&#xff0c;当机械触点断开、闭合时&#xff0c;由于机械触点的弹性…

wampserver的mysql8.0版本在my.ini文件中加入skip_grant_tables无效等一系列问题。

背景&#xff1a;安装了新的wampserver之后&#xff0c;php版本mysql8.0.31&#xff0c;想打开phpadmin可视化管理页面&#xff0c;后来忘记密码了&#xff0c;报错&#xff1a;ERROR 1045 (28000): Access denied for user rootlocalhost (using password: No)&#xff0c;只能…

Ubuntu 更改内核启动顺序

ubuntu服务器系统中用run包安装了某卡的驱动&#xff0c;后来又安装了docker&#xff0c;重启后&#xff0c;驱动失效。 经分析 安装docker时&#xff0c;又把新的linux内核安装上了。驱动是安装在旧内核上。 然会重新安装驱动&#xff0c;失败&#xff0c;确认是因为驱动只支…

MDK版本坑error: A1167E: Invalid line start

移植threadx时对于.s的汇编文件报了大量错误&#xff0c;到文件里查看是汇编文件中的注释使用的C的注释方法&#xff0c;导致大量报错 MDK官网查到原因&#xff0c;一个是版本问题&#xff0c;一个是设置问题&#xff0c; https://developer.arm.com/documentation/ka002247…

isaac sim添加孔网格

isaac sim仿真和其它仿真实际上一样&#xff0c;对于孔的仿真&#xff0c;是没那么简单的 在此记录一下踩过的坑 1&#xff0c;首先&#xff0c;你需要在soildworks中将你的孔画出来&#xff0c;并导出stl 2&#xff0c;你可以在win10中使用3D画图查看孔的网格&#xff0c;看…

ylb-接口4投资排行榜

总览&#xff1a; 1、使用Redis存储投资信息 2、Redis常量类 在common模块constants包&#xff0c;创建一个Redis常量类&#xff08;RedisKey&#xff09;&#xff1a; package com.bjpowernode.common.constants;public class RedisKey {/*投资排行榜*/public static fin…

实现本地缓存-caffeine

目录 实现caffeine cache CacheManager Caffeine配置说明 创建自定义配置类 配置缓存管理器 编写自动提示配置文件 测试使用 创建测试配置实体类 创建测试配置类 创建注解扫描的测试实体 创建单元测试类进行测试 实现caffeine cache CacheManager SimpleCacheManag…

3.8 Bootstrap 面包屑导航(Breadcrumbs)

文章目录 Bootstrap 面包屑导航&#xff08;Breadcrumbs&#xff09; Bootstrap 面包屑导航&#xff08;Breadcrumbs&#xff09; 面包屑导航&#xff08;Breadcrumbs&#xff09;是一种基于网站层次信息的显示方式。以博客为例&#xff0c;面包屑导航可以显示发布日期、类别或…