NzN的数据结构--二叉树part2

        上一章我们介绍了二叉树入门的一些内容,本章我们就要正式开始学习二叉树的实现方法,先三连后看是好习惯!!!

目录

一、二叉树的顺序结构及实现

1. 二叉树的顺序结构

2. 堆的概念及结构

3. 堆的实现

3.1 堆的创建

3.2 堆的插入

3.3 向下调整算法

3.4 堆的删除

3.5 总体代码实现

4. 堆操作的时间复杂度

二、堆的应用

1. 堆排序

2. TOP-K问题

三、二叉树链式结构的实现

1. 前置说明

2. 二叉树的遍历

2.1 前/中/后序遍历

2.2 层序遍历 

2.3 节点个数以及深度等

3. 二叉树的销毁


一、二叉树的顺序结构及实现

1. 二叉树的顺序结构

        普通的二叉树不适合用数组来存储,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储。

2. 堆的概念及结构

        如果有一个关键码的集合  ={, ,…,},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足: ),则称为小堆(或大堆)。

        大堆(大根堆):根节点最大;所有父亲都≥孩子

        小堆(小根堆):根节点最小;所有父亲都≤孩子

        堆的性质:

  • 堆中某个节点的值总是≥(或≤)其父节点的值,成为大堆(或小堆)
  • 堆总是一棵完全二叉树

注意:堆在数组里不是有序的,小()堆可以帮我们找到最小()值,即找到根节点。

3. 堆的实现

3.1 堆的创建

        我们给出一个数组,这个数组逻辑上可以看做一颗完全二叉树,但是还不是一个堆,现在我们通过算法,把它构建成一个堆。根节点左右子树不是堆,我们怎么调整呢?这里我们从倒数的第一个非叶子节点的子树开始调整,一直调整到根节点的树,就可以调整成堆。

3.2 堆的插入

        先插入一个10到数组的尾上,再进行向上调整算法,直到满足堆。

3.3 向下调整算法

        我们给出一个数组,逻辑上看做一棵完全二叉树。我们通过从根节点开始的向下调整算法可以把它调整成一个小堆。向下调整算法有一个前提:左右子树必须是一个堆,才能调整。

3.4 堆的删除

        删除堆是删除堆顶的数据,将堆顶的数据根最后一个数据一换,然后删除数组最后一个数据,再进行向下调整算法。

3.5 总体代码实现

        先在头文件中对相关功能接口进行声明:

typedef int HPDataType;
//数组实现
typedef struct Heap
{HPDataType* a;//底层就是一个数组int size;int capacity;
}HP;
//堆的初始化和销毁
void HeapInit(HP* php);
void HeapInitArray(HP* php, HPDataType* a, int n);
void HeapDestroy(HP* php);
//向上调整算法
void AdjustUp(HPDataType* a, int child);
//往堆中插入数据
void HeapPush(HP* php, HPDataType x);
//向下调整算法
void AdjustDown(HPDataType* a, int size, int parent);
//删除堆顶(根节点)
void HeapPop(HP* php);
//输出根节点
HPDataType HeapTop(HP* php);
//计算堆的大小
size_t HeapSize(HP* php);
//判断堆是否为空
bool HeapEmpty(HP* php);
//堆排序
void HeapSort(int* a, int n);
//TOP-K问题
void PrintTopK(int* a, int n, int k);

        下面是接口的具体实现:

//堆的初始化和销毁
void HeapInit(HP* php)
{assert(php);php->a = NULL;php->size = 0;php->capacity = 0;
}void HeapDestroy(HP* php)
{assert(php);free(php->a);php->a = NULL;php->size = php->capacity = 0;
}//将交换功能封装成Swap函数,以便复用
void Swap(HPDataType* p1, HPDataType* p2)
{HPDataType tmp = *p1;*p1 = *p2;*p2 = tmp;
}//以小堆为例
void AdjustUp(HPDataType* a, int child)
{int parent = (child - 1) / 2;//确保不会移动根节点(因为根节点的索引为0,没有父节点)while (child > 0){//孩子 < 父亲,就要交换if (a[child] < a[parent]){Swap(&a[child], &a[parent]);child = parent;//把父亲的位置给孩子parent = (child - 1) / 2;//再算新的父亲}else{break;}}
}//往堆中插入数据
void HeapPush(HP* php, HPDataType x)
{assert(php);//空间不够需要扩容if (php->size == php->capacity){//初始化时capacity=0,所以要先给他4个空间int newCapacity = php->capacity == 0 ? 4 : php->capacity * 2;HPDataType* tmp = (HPDataType*)realloc(php->a, newCapacity * sizeof(HPDataType));if (tmp == NULL){perror("realloc");return;}//把原来的数组和容量更新成扩容之后的php->a = tmp;php->capacity = newCapacity;}php->a[php->size] = x;//在尾部插入数据php->size++;//插入一个数据后,size要+1//插入之后可能就不是堆了,通过向上调整算法把它变成堆AdjustUp(php->a, php->size - 1);
}//向下调整算法
void AdjustDown(HPDataType* a, int size, int parent)
{int child = parent * 2 + 1;while (child < size){//默认左孩子小,假设错误就更新一下if (child + 1 < size && a[child + 1] < a[child])//右孩子存在&&右孩子小于左孩子{++child;//child是左孩子,+1就是右孩子}if (a[child] < a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}//删除堆顶(根节点)
void HeapPop(HP* php)
{assert(php);assert(php->size > 0);//至少要有一个节点//把堆顶的数据跟数组的尾元素交换位置//此时删除堆顶就变成了删除数组的尾元素Swap(&php->a[0], &php->a[php->size - 1]);php->size--;//删除操作就是控制size来完成的//其实我们并不是把这个数据删除,而是把他反复在最后就不用管它了//size-1后,想要访问这个数据就变成了数组越界访问AdjustDown(php->a, php->size, 0);
}//输出根节点
HPDataType HeapTop(HP* php)
{assert(php);assert(php->size > 0);return php->a[0];
}//计算堆的大小
size_t HeapSize(HP* php)
{assert(php);return php->size;
}//判断堆是否为空
bool HeapEmpty(HP* php)
{assert(php);return php->size == 0;
}

4. 堆操作的时间复杂度

        因为堆是完全二叉树,而满二叉树也是完全二叉树,此处为了简化使用满二叉树来证明(时间复杂度本来就是近似值,多几个节点不影响最终结果):

       插入数据时间消耗主要在向上调整部分,最好情况就是不进行调整,最差情况就是调整树的高度次,因此插入数据的时间复杂度为

       删除数据时间消耗主要在向下调整部分,最好情况就是不进行调整,最差情况就是调整树的高度次,因此删除数据的时间复杂度为

//按照数组来初始化
void HeapInitArray(HP* php, HPDataType* a, int n)
{assert(php);php->a = (HPDataType*)malloc(sizeof(HPDataType) * n);if (php->a == NULL){perror("realloc");return;}memcpy(php->a, a, sizeof(HPDataType) * n);//把数据放到堆上php->size = n;php->capacity = n;//建堆//模拟向上调整,理解为数据插入,时间复杂度O(N*logN)//for (int i = 1; i < php->size; i++)//{//	AdjustUp(php->a, i);//}//第二层最多向上调整1次,第三层最多向上调整2次……,最后一层最多向上调整h-1次//假设累计向上调整F(h)次,F(h)=2^1*1+2^2*2+…+2^(h-1)*(h-1)=2^h*(h-2)+2//把F(h)转换成F(N),h=log(N+1),因此F(N)=(N+1)*(log(N+1)-2)+2≈N*logN//模拟向下调整,时间复杂度O(N)//从倒数的第一个非叶子结点开始调整(最后一个节点的父亲),倒着往上调//最后一个节点的下标是php->size - 1,i求他的父亲然后开始倒着调整for (int i = (php->size - 1 - 1) / 2; i >= 0; i--){AdjustDown(php->a, php->size, i);}//假设累计向下调整F(h)次,F(h)=2^(h-2)*1+2^(h-3)*2+…+2^0*(h-1)//F(N)=N-log(N+1)≈N
}

        对于向上调整,满二叉树最后一层结点数占整个树的一半,结点数量多的层,调整次数也多。而向下调整正好相反,结点数量多的调整次数少。因此两种方法时间复杂度相差较大,我们优先选择向下调整建堆。

二、堆的应用

1. 堆排序

void HeapSort(int* a, int n) 
{HP hp;HeapInitArray(&hp, a, n);int i = 0;while (!HeapEmpty(&hp)){a[i++] = HeapTop(&hp);//将堆顶赋给a[0]HeapPop(&hp);//删除堆顶元素,这样堆中第二大/小的元素就会被移动到堆顶}HeapDestroy(&hp);
}

        上面这种写法有两大缺陷,首先就是进行堆排序之前需要先创建堆的各种相关算法,其次就是空间复杂度为

        堆排序即利用堆的思想来进行排序,总共分为两个步骤:

        [1] 建堆

  •         升序:建大堆
  •         降序:建小堆

       [2]  利用堆删除思想来进行排序

        建堆和堆删除中都用到了向下调整,因此通过向下调整,就可以完成堆排序。

//大堆的向下调整算法
void AdjustDown(HPDataType* a, int size, int parent)
{int child = parent * 2 + 1;while (child < size){//默认左孩子大,假设错误就更新一下  if (child + 1 < size && a[child] < a[child + 1]) //右孩子存在且右孩子大于左孩子  {++child; // child原本是左孩子,+1变成右孩子  }//如果子节点大于父节点则交换 if (a[child] > a[parent]){Swap(&a[child], &a[parent]);parent = child;child = parent * 2 + 1;}else{break;}}
}//升序建大堆,降序建小堆
//建小堆如果想要升序,堆顶元素在对应位置,剩余元素重新建小堆,时间复杂度大大增加
void HeapSort(int* a, int n)
{//对数组直接建堆for (int i = (n - 1 - 1) / 2; i >= 0; --i){AdjustDown(a, n, i);}// O(N*logN)int end = n - 1;while (end > 0){Swap(&a[0], &a[end]);//上面建的大堆,根节点最大,把大的数据往后放AdjustDown(a, end, 0);//新的根节点可能会破坏大堆性质,需要向下调整end--;}
}

 注意:我们需要根据升序或降序的要求,对向下调整算法进行修改。

2. TOP-K问题

        TOP-K问题:即求数据结合中前K个最大的元素或者最小的元素。

        对于Top-K问题,最容易想到的方法就是排序,但是如果数据量非常大,排序就不可取了(可能数据不能一口气全部加载到内存中)。

        最佳的方式就是用堆来解决,基本思路如下:

        [1] 用数据集合中前K个元素来建堆

  •         求前k个最大的元素,则建小堆
  •         求前k个最小的元素,则建大堆

        [2] 用剩余的N-K个元素依次与堆顶比较,不满足则替换堆顶元素

        将剩余N-K个元素依次与堆顶元素比较之后,堆中剩余的K个元素就是所求的前K个最小或者最大的元素。

        这种方法的时间复杂度是

//以求前K个最大的数据为例,则需要建小堆
void PrintTopK(int* a, int n, int k)
{HP php;HeapInit(&php);//数组中前k个元素来建堆for (int i = 0; i < k; ++i) {HeapPush(&php, a[i]);}//用剩余的n-k个元素依次与堆顶比较for (int i = k; i < n; ++i) {if (a[i] > HeapTop(&php)) //当前元素>堆顶,就要让他进堆{ HeapPop(&php); //移除当前堆顶HeapPush(&php, a[i]); //将新的元素插入堆中}}for (int i = 0; i < k; ++i) {printf("%d ", php.a[i]);}printf("\n");HeapDestroy(&php);
}

三、二叉树链式结构的实现

1. 前置说明

        在学习二叉树的基本操作前,需先创建一棵二叉树,然后才能学习其相关操作。由于现在对二叉树结构掌握还不够深入,因此在此处手动快速创建一棵简单的二叉树,快速学习二叉树操作,等后面再来研究二叉树真正的创建方式。

        回顾下二叉树的概念,二叉树是:

  • 空树
  • 非空:根节点,根节点的左子树、根节点的右子树组成的

        从概念中可以看出,二叉树是递归定义的,因此后序基本操作中基本都是按照该概念实现的。

2. 二叉树的遍历

2.1 前/中/后序遍历

        学习二叉树结构,最简单的方式就是遍历。所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。

        按照规则,二叉树的遍历有:前序/中序/后序的递归结构遍历:

  • 前序遍历(先序遍历):根 左子树 右子树。
  • 中序遍历:左子树 根 右子树。
  • 后序遍历:左子树 右子树 根。
typedef struct BinTreeNode 
{struct BinTreeNode* left;struct BinTreeNode* right;int val;
}BTNode;//前序遍历
void PreOrder(BTNode* root)
{//树为空if (root == NULL){printf("N ");return;}//树不为空,按照 根 左子树 右子树 的顺序递归打印printf("%d ", root->val);PreOrder(root->left);PreOrder(root->right);
}//中序遍历
void InOrder(BTNode* root)
{//树为空if (root == NULL){printf("N ");return;}//树不为空,按照 左子树 根 右子树 的顺序递归打印InOrder(root->left);printf("%d ", root->val);InOrder(root->right);
}//后序遍历
void PostOrder(BTNode* root)
{//树为空if (root == NULL){printf("N ");return;}//树不为空,按照 左子树 右子树 根 的顺序递归打印PostOrder(root->left);PostOrder(root->right);printf("%d ", root->val);
}

        前序遍历递归图解:

2.2 层序遍历 

        自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

       层序遍历的实现需要借助队列(先进先出),实现思想是上一层带下一层。

void LevelOrder(BTNode* root)
{Queue q;QueueInit(&q);//先入根节点if (root)QueuePush(&q, root);while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);QueuePop(&q);printf("%d ", front->val);//带入下一层if (front->left)QueuePush(&q, front->left);if (front->right)QueuePush(&q, front->right);}printf("\n");QueueDestroy(&q);
}

2.3 节点个数以及深度等

//求树的结点个数
static int size = 0;//定义成全局变量,每次调用前初始化为0
int TreeSize(BTNode* root)
{if (root == NULL)return 0;else++size;TreeSize(root->left);TreeSize(root->right);return size;
}

       但是这种写法会出现线程安全的风险,可以改成给函数传size的指针或者依靠分治递归思想(左子树返回的结点个数+右子树返回的结点个数+1,通过后序遍历)。

//求树的结点个数
int TreeSize(BTNode* root)
{return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
//求树的深度
int TreeDepth(BTNode* root)
{if (root == NULL)return 0;//左子树和右子树分别计算深度,取最大的深度+1(根节点)int leftDepth = TreeDepth(root->left);int rightDepth = TreeDepth(root->right);return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}
int TreeDepth(BTNode* root)
{if (root == NULL)return 0;return TreeDepth(root->left) > TreeDepth(root->right) ? TreeDepth(root->left) + 1 : TreeDepth(root->right) + 1;//这种写法看似与上面的写法相差不大,但实际上这个写法的时间复杂度大打折扣//通过leftDepth和rightDepth会记录左右子树的深度,return时不需要再次计算,时间复杂度为O(N)//而这种写法每次使用TreeDepth(root->left)或TreeDepth(root->right)都需要重新计算一次//时间复杂度是一个等比数列和,根节点的找次其实是第二层节点找2次,是第三层找4次……//最后算到时间复杂度为O(2^N)
}
//求第k层的节点个数
//当前树的第k层节点个数=左子树的第k-1层节点个数+右子树的第k-1层节点个数
int TreeKLevel(BTNode* root, int k)
{assert(k > 0);if (root == NULL)return 0;//只有根节点if (k == 1)return 1;//k>1,说明第k层在子树里return TreeKLevel(root->left, k - 1) + TreeKLevel(root->right, k - 1);
}
//查找x所在的节点(找到1个就返回)
BTNode* TreeFind(BTNode* root, int x)
{if (root == NULL)return NULL;if (root->val == x)return root;//先在左子树找,没找到再去右子树找//如果要找两次,推荐用变量来接收BTNode* ret1 = TreeFind(root->left, x);if (ret1)return ret1;BTNode* ret2 = TreeFind(root->right, x);if (ret2)return ret2;return NULL;
}

3. 二叉树的销毁

void TreeDestroy(BTNode* root)
{if (root == NULL)return;TreeDestroy(root->left);TreeDestroy(root->right);free(root);//root = NULL;//这里置空对形参修改,实参不改变//但是想要修改root就需要二级指针,非常麻烦//因此可以调用完TreeDestroy函数后,手动将root置为NULL
}

        以上就是本文的全部内容,这个地方非常难理解,大家要注意看每段代码的注释部分,后面需要勤加练习,熟练掌握这些算法!!

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

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

相关文章

Idea 通过 Tomcat 启动项目时出现“错误:找不到或无法加载主类 ecoding”

问题描述 在Idea中通过Tomcat启动项目时&#xff0c;出现 “错误&#xff1a;找不到或无法加载主类 ecoding” 原因 在Tomcat - Eidt Configurations....中配置VM options时出现了错误&#xff0c;可以查看下该配置是否填写正确&#xff1b;

2024-04-08 NO.5 Quest3 手势追踪进行 UI 交互

文章目录 1 玩家配置2 物体配置3 添加视觉效果4 添加文字5 其他操作5.1 双面渲染5.2 替换图片 ​ 在开始操作前&#xff0c;我们导入先前配置好的预制体 MyOVRCameraRig&#xff0c;相关介绍在 《2024-04-03 NO.4 Quest3 手势追踪抓取物体-CSDN博客》 文章中。 1 玩家配置 &a…

全自动ai生成视频MoneyPrinterTurbo源码

功能介绍 完整的 MVC架构&#xff0c;代码 结构清晰&#xff0c;易于维护&#xff0c;支持 API 和 Web界面 支持视频文案 AI自动生成&#xff0c;也可以自定义文案支持多种 高清视频 尺寸 竖屏 9:16&#xff0c;1080x1920 横屏 16:9&#xff0c;1920x1080 支持 批量视频生成&am…

PHP基础

搭建环境 网站基本概念 服务器概念 服务器是为电脑提供服务的电脑&#xff0c;本地电脑如果有公网IP&#xff0c;那也能当作服务器工作服务器是计算机的一种&#xff0c;它比普通计算机运行更快&#xff0c;负载更高、价格更贵。 服务器在网络中为其它客户机&#xff08;如P…

借助 Aspose.Words,在 C# 中将图片转换为 Word

Microsoft Word 提供了多种用于生成具有增强的格式化功能的文本文档的工具。除了文本格式之外&#xff0c;我们还可以将各种图形元素和图像合并到Word文档中。在某些情况下&#xff0c;我们可能需要将图片或照片插入DOC或DOCX格式的Word文档中。在本文中&#xff0c;我们将学习…

DevOps已死?2024年的DevOps将如何发展

随着我们进入2024年&#xff0c;DevOps也发生了变化。新兴的技术、变化的需求和发展的方法正在重新定义有效实施DevOps实践。 IDC预测显示&#xff0c;未来五年&#xff0c;支持DevOps实践的产品市场继续保持健康且快速增长&#xff0c;2022年-2027年的复合年增长率&#xff0…

【神经网络】卷积神经网络CNN

卷积神经网络 欢迎访问Blog全部目录&#xff01; 文章目录 卷积神经网络1. 神经网络概览2.CNN&#xff08;Convolutional Neunal Network&#xff09;2.1.学习链接2.2.CNN结构2.2.1.基本结构2.2.1.1输入层2.2.1.2.卷积层|Convolution Layers2.2.1.3.池化层|Pooling layers2.3…

k8s部署efk

环境简介&#xff1a; kubernetes: v1.22.2 helm&#xff1a; v3.12.0 elasticsearch&#xff1a; 8.8.0 chart包&#xff1a;19.10.0 fluentd: 1.16.2 chart包&#xff1a; 5.9.4 kibana: 8.2.2 chart包&#xff1a;10.1.9 整体架构图&#xff1a; 一、Elasticsearch安装…

归一化技术比较研究:Batch Norm, Layer Norm, Group Norm

归一化层是深度神经网络体系结构中的关键&#xff0c;在训练过程中确保各层的输入分布一致&#xff0c;这对于高效和稳定的学习至关重要。归一化技术的选择&#xff08;Batch, Layer, GroupNormalization&#xff09;会显著影响训练动态和最终的模型性能。每种技术的相对优势并…

Codeforces Round 938 (Div. 3) A - F 题解

A. Yogurt Sale 题意&#xff1a;要购买n个酸奶&#xff0c;有两种买法&#xff0c;一种是一次买一个&#xff0c;价格a。一种是一次买两个&#xff0c;价格b&#xff0c;问买n个酸奶的最小价格。 题解&#xff1a;很容易想到用2a和b比较&#xff0c;判断输出即可。 代码&am…

麻雀优化算法(Sparrow Search Algorithm)

注意&#xff1a;本文引用自专业人工智能社区Venus AI 更多AI知识请参考原站 &#xff08;[www.aideeplearning.cn]&#xff09; 算法背景 麻雀算法&#xff08;Sparrow Search Algorithm, SSA&#xff09;是一种受自然界麻雀群体行为启发的优化算法。想象一下&#xff0c;一…

【MacOs】proxychains配置使用

一、开始 1. 安装proxychains 使用brew进行安装 brew install proxychains-ng没有homebrew的&#xff0c;可以使用该命令安装 /usr/bin/ruby -e "$(curl -fsSL https://cdn.jsdelivr.net/gh/ineo6/homebrew-install/install)"2. 配置代理配置文件 cd /opt/homeb…

day5 nest商业项目初探·一(java转ts全栈/3R教室)

背景&#xff1a;从头一点点学起太慢了&#xff0c;直接看几个商业项目吧&#xff0c;看看根据Java的经验&#xff0c;自己能看懂多少&#xff0c;然后再系统学的话也会更有针对性。先看3R教室公开的 kuromi 移民机构官方网站吧 【加拿大 | 1.5w】Nextjs&#xff1a;kuromi 移民…

专业140+总410+国防科技大学831信号与系统考研经验国防科大电子信息与通信,真题,大纲,参考书。

应群里同学要求&#xff0c;总结一下我自己的复习经历&#xff0c;希望对大家有所借鉴&#xff0c;报考国防科技大学&#xff0c;专业课831信号与系统140&#xff0c;总分410&#xff0c;大家以前一直认为国防科技大学时军校&#xff0c;从而很少关注这所军中清华&#xff0c;现…

Java 基于微信小程序的助农扶贫小程序

博主介绍&#xff1a;✌Java徐师兄、7年大厂程序员经历。全网粉丝13w、csdn博客专家、掘金/华为云等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不…

React - 你知道props和state之间深层次的区别吗

难度级别:初级及以上 提问概率:60% 如果把React组件看做一个函数的话,props更像是外部传入的参数,而state更像是函数内部定义的变量。那么他们还有哪些更深层次的区别呢,我们来看一下。 首先说props,他是组件外部传入的参数,我们知道…

鸿蒙实战开发-相机和媒体库、如何在eTS中调用相机拍照和录像

介绍 此Demo展示如何在eTS中调用相机拍照和录像&#xff0c;以及使用媒体库接口将图片和视频保存的操作。实现效果如下&#xff1a; 效果预览 使用说明 1.启动应用&#xff0c;在权限弹窗中授权后返回应用&#xff0c;进入相机界面。 2.相机界面默认是拍照模式&#xff0c;…

【第二十九篇】BurpSuite杂项综合

文章目录 Intruder模块URL编码Grep检索提取logger日志模块Intruder模块URL编码 假设我们需要对GET请求包中的URL目录进行爆破FUZZ: example.com/xxxx(文件名)Intruder模块会自动对我们的文件名字典进行URL编码 例如payload为1.txt时,burp对其进行URL编码并连接到example.c…

性能优化 - 你知道dns-prefetch有什么用吗

难度级别:中级及以上 提问概率:50% 我们在HTML文档里写一个script标签,为src属性指定Javascript文件网络地址,这是一件再平凡不过的事情。当浏览器加载HTML文档,加载到这个script标签的时候,就会去下载Javascript文件。而在下载之前,就…

携程旅行 abtest

声明: 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01;wx a15018601872 本文章…