【数据结构】深度讲解栈、栈的应用举例、栈和递归的实现教你全面认识栈

news2024/12/26 22:48:08

目录

  • 一.栈
    • 1.栈的概念及结构
    • 2.栈的实现
      • stack.h
      • Stack.c
  • 二.栈的应用举例
    • 1.数制转换
    • 2.有效的括号
    • 3.迷宫求解
  • 三.栈与递归的实现
    • 1.栈和递归
    • 2.迷宫问题递归实现
    • 3.汉诺塔栈实现

一.栈

入栈出栈展示:
在这里插入图片描述

1.栈的概念及结构

栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。**进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。**栈中的数据元素遵守后进先出LIFO(last in first out)的原则。
进栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶
出栈:栈的删除操作叫出栈。出数据也在栈顶
在这里插入图片描述

2.栈的实现

栈的实现一般可以使用数组或者链表实现,相对而言数组的结构实现更优一些。因为数组在尾上插入数据的代价比较小,而且栈对数组头部的操作只有在栈中数据个数为1或0时,才对数组头部进行操作,数组完全符合栈的条件,相对于链表优势更大,实现更简单。
通常我们将top指针指向栈顶的下一个位置,表示该位置为下一个数据入栈存储的位置。当top = 0时表示空栈。
另一方面,栈在使用的过程中所需要最大空间的大小很难估计,因此,一般来说,在初始化设空栈时不设置栈的最大容量,而是根据实际需求动态增加空间。
在这里插入图片描述

stack.h

头文件,包含栈所有的函数声明、结构体声明

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

typedef int STDataType;//方便存储其他类型的数据时进行修改

typedef struct Stack
{
	STDataType* st;
	int capcity;//栈的容量
	int top;//栈顶
}Stack;

void StackInit(Stack* ps);//初始化栈

void StackPush(Stack* ps, STDataType x);//入栈

void StackPop(Stack* ps);//出栈

STDataType StackTop(Stack* ps);//获取栈顶元素

int StackSize(Stack* ps);//获取栈中有效元素个数

int StackEmpty(Stack* ps);//检测栈是否为空,如果为空返回非零结果,如果不为空返回0

void StackDestroy(Stack* ps);//销毁栈

Stack.c

包含所有函数的定义
1.初始化栈

//初始化栈
void StackInit(Stack* ps)
{
	assert(ps);

	//初始化两种方式
	//方式1
	ps->top = 0;
	ps->capcity = 0;
	ps->st = NULL;

	//方式2
	// 使用该方法,下面的入栈函数需要修改
	//ps->st = (STDataType*)malloc(sizeof(STDataType) * 4);
	//if (!ps->st)
	//{
	//	perror("malloc fail");
	//	exit(-1);
	//}
	//ps->capcity = 4;
	//ps->top = 0;
}

方法1:直接将栈的容量赋值为0,对存放数据的数组赋为NULL,入栈操作时,在进行赋值
方法2:在初始化时,向内存申请空间并赋给存放数据的指针,将申请空间的大小就是栈的容量。
这两种方法都可以使用。

2.入栈

//入栈
void StackPush(Stack* ps, STDataType x)
{
	assert(ps);
	if (ps->capcity == ps->top)
	{
		int size = (ps->top == 0) ? 4 : ps->top * 2;
		STDataType* tmp = (STDataType*)realloc(ps->st, sizeof(STDataType) * size);
		if (!tmp)
		{
			perror("realloc fail!");
			exit(-1);
		}
		ps->capcity = size;
		ps->st = tmp;
	}
	ps->st[ps->top] = x;
	ps->top++;
}

首先,判断栈剩余的的空间是否足够,若是不够,进行扩容操作,向内存申请更多的空间,并修改栈的容量。
其次,将首元素存入栈顶,在将栈顶加1,
因为初始时,栈顶的位置为0,存放1个元素时,栈顶的位置为1,存放第二个元素时,栈顶的位置为2,每增加一个元素,栈顶移动一位。

3.出栈

//出栈
void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);//判断栈中是否还有数据

	ps->top--;
}

出栈操作十分简单,先使用断言栈中是否还有数据,若是有进行出战操作,否则提示出错(无法对空的栈进行出栈操作)。
最后,将栈顶减1即可,因为每次入栈和从栈中取数据都是依据栈顶来实现的,直接将对栈顶进行操作,当下次入栈时就会覆盖掉之前的数据,从栈中取数据也是取减1后栈顶的数据,这样写是合适的。

4.获取栈顶元素

//获取栈顶元素
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);

	return ps->st[ps->top-1];
}

同时,判断栈中是否还有数据。
因为栈顶是要比存入栈的最后一个数据的下标高一位,返回下标为栈顶-1的数据即可

5.获取栈中有效元素个数

//获取栈中有效元素个数
int StackSize(Stack* ps)
{
	assert(ps);
	assert(ps->top >= 0);

	return ps->top;
}

返回栈顶的即可,栈顶是多少,栈中就有多少个数据。

6.检测栈是否为空

//检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);

	return ps->top == 0;
}

如果栈顶为0,表示栈为空,返回1,否则为假,返回0

7.销毁栈

//销毁栈
void StackDestroy(Stack* ps)
{
	assert(ps);

	free(ps->st);
	ps->st = NULL;
	ps->capcity = 0;
	ps->top = 0;
}

栈中存储数据的空间是向内存申请的需要释放,否则会造成内存泄漏。

Stack.c代码

#define _CRT_SECURE_NO_WARNINGS 1
#include "Stack.h"

//初始化栈
void StackInit(Stack* ps)
{
	assert(ps);

	//初始化两种方式
	//方式1
	ps->top = 0;
	ps->capcity = 0;
	ps->st = NULL;

	//方式2
	// 使用该方法,下面的入栈函数需要修改
	//ps->st = (STDataType*)malloc(sizeof(STDataType) * 4);
	//if (!ps->st)
	//{
	//	perror("malloc fail");
	//	exit(-1);
	//}
	//ps->capcity = 4;
	//ps->top = 0;
}

//入栈
void StackPush(Stack* ps, STDataType x)
{
	assert(ps);
	if (ps->capcity == ps->top)
	{
		int size = (ps->top == 0) ? 4 : ps->top * 2;
		STDataType* tmp = (STDataType*)realloc(ps->st, sizeof(STDataType) * size);
		if (!tmp)
		{
			perror("realloc fail!");
			exit(-1);
		}
		ps->capcity = size;
		ps->st = tmp;
	}
	ps->st[ps->top] = x;
	ps->top++;
}

//出栈
void StackPop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);

	ps->top--;
}

//获取栈顶元素
STDataType StackTop(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);

	return ps->st[ps->top-1];
}

//获取栈中有效元素个数
int StackSize(Stack* ps)
{
	assert(ps);
	assert(ps->top >= 0);

	return ps->top;
}

//检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{
	assert(ps);
	assert(ps->top > 0);

	return ps->top == 0;
}

//销毁栈
void StackDestroy(Stack* ps)
{
	assert(ps);

	free(ps->st);
	ps->st = NULL;
	ps->capcity = 0;
	ps->top = 0;
}

二.栈的应用举例

由于栈结构具有后进先出的固有特性,使得栈成为程序设计中的有用工具。接下来我们将讨论几个栈应用中典型的例子。

1.数制转换

十进制数N和其他d进制数的转换是计算机实现计算的基本问题,其解决方法很多,其中一个简单算法基于下列原理:
在这里插入图片描述
例如:(1348)10 = (2504)8 其运算过程如下:
在这里插入图片描述
这道题使用栈来实现是非常简单的,将N和8求余的结果放入栈中,最终依次出栈即为最终结果。也许,有人就会有疑问:用数组直接实现不也很简单?对的,用数组直接实现也不难,但栈的引用简化了程序设计的问题,使思考范围缩小。使用数组真正要去想的不是解出这道题的思路,而是去考虑数组下标增减等细节问题。

//对于输入的任意一个非零的十进制整数,打印出与它等值的八进制数
void conversion()
{
	Stack s;
	StackInit(&s);//创建空栈
	int N = 0;
	printf("十进制:");
	scanf("%d", &N);

	while (N)
	{
		StackPush(&s, N % 8);
		N = N / 8;
	}
	printf("八进制:");
	while (!StackEmpty(&s))
	{
		printf("%d", StackTop(&s));
		StackPop(&s);
	}
}

在这里插入图片描述

2.有效的括号

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。

示例 1:

输入:s = “()”
输出:true

示例 2:

输入:s = “()[]{}”
输出:true

示例 3:

输入:s = “(]”
输出:false

(这是Leetcode上的一道题,大家可以直接点击标题进入网站答题。)

解题思想:
我们将“ [ { ( ”这三种开括号放入栈中,如果出现另外三种闭括号,将该闭括号与栈顶的元素做对比,这样简单的入栈出栈操作即可确定括号是否匹配。
在这里插入图片描述

bool isValid(char* s) {
	Stack st;
	StackInit(&st);
	
	while (*s)
	{
		if (*s == '[' || *s == '{' || *s == '(')
		{
			StackPush(&st, *s);//将出现的开括号全都放入栈中
			s++;
		}
		else//当出现闭括号,将其与栈顶数据做对比
		{
			if (StackEmpty(&st))//如果此时栈为空,匹配失败
				return false;

			char tmp = StackTop(&st);
			StackPop(&st);
			if (*s == ']' && tmp == '[')
			{
				s++;
				continue;
			}
			else if (*s == '}' && tmp == '{')
			{
				s++;
				continue;
			}
			else if (*s == ')' && tmp == '(')
			{
				s++;
				continue;
			}
			return false;
		}
	}
	if (!StackEmpty(&st))//如果此时栈不为空,表示括号不匹配
		return false;
	return true;
}

3.迷宫求解

求迷宫中从入口到出口的所有路径使一个经典的程序设计问题。由于计算机解迷宫时,通常用的是“穷举求解”的方法,即从入口出发,顺某一方向向前探索,若能走通,则继续往前走;否则沿原路退回,换一个方向继续探索,甚至所有可能的通路都探索到为止。为了保证在任何位置上都沿原路退回,显然需要一个后进先出的结构保存从入口到当前位置的路径。因此,在求迷宫通路的算法中应用“栈”也就是自然而然的事。
在这里插入图片描述
思想

  • 白方块:通道
  • 阴影线方块:墙

假设当前位置为“在搜索过程中某一时刻所在图中某个方块位置”,则求迷宫中一条路径的算法基本思想为:
当前路径可通,入栈,继续探索下一个位置,若当前路径不可通,则出栈,出栈后判断此时栈顶位置其他的下一个位置;若该通道块四周的4个方块均不可通,则继续出栈,如此重复直到到达出口。
“当前位置”的四个方块为相邻的上、下、左、右上相邻的方块。
在这里插入图片描述
注意:所谓的可通指的是未曾走到过的通道块,即要求该方块位置不仅是通道块,而且即不在当前路径上,也不是曾经纳入过的路径的通道块,否则只能进入死循环。

在这里插入图片描述

  • 如图我们采用这样8*8的图形来模拟迷宫
    在这里插入图片描述
    代码
typedef struct location
{
	int x;  //当前位置的横坐标
	int y;  //当前位置的纵坐标
	int next; //指向的下一个位置
}local;//栈的元素类型

int n = 8;

char Maze[8][8] =
{ {'O','X','X','X','X','X','X','X'},
{'O','O','O','O','O','X','X','X'},
{'X','O','X','X','O','O','O','X'},
{'X','O','X','X','O','X','X','O'},
{'X','O','X','X','X','X','X','X'},
{'X','O','X','X','O','O','O','X'},
{'X','O','O','O','O','X','O','O'},
{'X','X','X','X','X','X','X','O'}
};

int H[4] = { -1,0,1,0 };
int V[4] = { 0,1,0,-1 };

void MazePrint()
{
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			printf("%c ", Maze[i][j]);
		}
		printf("\n");
	}
}

void DFS(Stack* st)
{
	StackInit(st);
	local start = { 0,0,0 };
	StackPush(st, start);
	Maze[start.x][start.y] = ' '; //将入栈的位置变为空字符方便区分

	while (!StackEmpty(st))
	{
		int top = StackSize(st) - 1;  //取栈顶元素的下标
		while (st->st[top].next < 4)//每个当前位置共用四个方位可以选择,每个方向遍历一遍
		{
			if (st->st[top].x == n - 1 && st->st[top].y == n - 1)//判断是否到达出口
			{
				MazePrint();
				return;
			}
			if (Maze[st->st[top].x + H[st->st[top].next]][st->st[top].y + V[st->st[top].next]] == 'O')//判断该位置是否入栈
			{
				Maze[st->st[top].x + H[st->st[top].next]][st->st[top].y + V[st->st[top].next]] = ' ';//将入栈位置变为空字符
				local cur = { st->st[top].x + H[st->st[top].next], st->st[top].y + V[st->st[top].next], 0 };//创建新的当前位置结点,方便入栈
				st->st[top].next++;//修改当前位置的四个方向标记,表示此方向已经遍历,无论是否出栈,将不会在遍历
				StackPush(st, cur);//入栈
				top = StackSize(st) - 1;
				continue;
			}
			st->st[top].next++;//修改当前位置的四个方向标记,表示此方向已经遍历,无论是否出栈,将不会在遍历
		}
		Maze[st->st[top].x][st->st[top].y] = 'O';//出栈前先将当前位置代表的字符回复,表示此路不通
		StackPop(st);//出栈
	}
}

在这里插入图片描述

三.栈与递归的实现

栈还有一个重要应用就是在程序设计语言中实现递归。一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称为递归函数。递归是算法中最常见的手段之一,它通常可以将一个复杂的大型的问题简化的简洁而清晰,很多递归算法常常要比非递归算法更容易设计,当问题本身涉及的数据结构是递归定义的时候,使用递归方法就更合适。

既然递归这么好,那我们为什么还要学着用栈去实现递归?
第一:在很多面试或笔试当中要求我们有这样的能力。比如快排的非递归写法就时常被拿来考察面试者。
第二:递归算法虽好,但它还是有很大的局限性,想要拥有一些东西,就必须要放弃一些。比如二叉树用递归遍历和非递归遍历就各有各的优势,我们可以用这两种方法达到不同的目的。

1.栈和递归

栈和递归的相互转换,采用相同的思想:

将第一个问题中的数据入栈或进入下一层递归,判断下一个问题的可执行性,继续保存当前数据继续入栈或进入下一次递归。
以此达到保存之前数据去解决新的问题的目的。

  • 这里我们用之前讲的迷宫问题的递归实现和汉诺塔的栈实现来讲解栈和递归的联系。

2.迷宫问题递归实现

栈操作是将迷宫的下一个位置入栈,当无路可走时,做出栈操作,查找新的可以入栈的位置。
递归采用相同的思想:

代码

int n = 8;

char Maze[8][8] =
{ {'O','X','X','X','X','X','X','X'},
{'O','O','O','O','O','X','X','X'},
{'X','O','X','X','O','O','O','X'},
{'X','O','X','X','O','X','X','O'},
{'X','O','X','X','X','X','X','X'},
{'X','O','X','X','O','O','O','X'},
{'X','O','O','O','O','X','O','O'},
{'X','X','X','X','X','X','X','O'}
};

int H[4] = { -1,0,1,0 };
int V[4] = { 0,1,0,-1 };

void MazePrint()
{
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			printf("%c ", Maze[i][j]);
		}
		printf("\n");
	}
}

void DFS(int x, int y)
{
	if (x == n - 1 && y == n - 1)//判断是否道道出口
	{
		Maze[x][y] = ' ';
		MazePrint();
		return;
	}
	else
	{
		for (int i = 0; i < 4; i++)//将当前位置的周围四个位置依次遍历
		{
			if (x>0 && x<n && y>0 && y<n && Maze[x][y] == 'O')//判断当前位置是否是通道是否越界(我使用的VS2019不判断越界也可得到答案)
			{
				Maze[x][y] = ' ';//置为空字符,表示可行通道
				DFS(x + H[i], y + V[i]);
				Maze[x][y] = 'O';//该位置不可行,返回原值
			}
		}
	}
}

int main()
{
	int x = 0, y = 0;

	DFS(x, y);

	return 0;
}

在这里插入图片描述

  • 从思想上看这和使用栈实现相同,但代码量明显减少。
  • 在实际代码运行中,找到最终的出口就会输出,但是如上图的返回操作是递归到最后一层后依次退回之前递归的函数操作。

3.汉诺塔栈实现

汉诺塔我相信接触过递归的人都应该知道,如果对此了解不深的可以看这篇博客青蛙跳台阶和汉诺塔问题。

思路:

  1. 这里我们要使用栈去解决,栈的元素是什么?——结构体,汉诺塔需要定位三个位置和一个数量,如果一个个入栈在出栈,不方便
typedef struct node
{
	int n;        //传递盘数
	char begin;   //起始
	char mid;     //中转
	char end;     //放置位置
}Node;
  1. 循环的实现,和递归相同。
    当传递盘数为1时,begin->end;
    当传递盘数不为1时,进行分化:
    1. 将n-1个盘子从begin移动到mid
    2. 将最后一个盘子从begin移动到end
    3. 将n-1个盘子从mid移动到end

因为是在栈上操作,每次进行循环时,都是对栈顶操作,如果按照123的顺序入栈,那就是在1还没有进行,n-1个盘子还没有移动到mid就继续操作,这明显是不对的。

所以我们按照321的顺序入栈,依次继续操作。

代码:

typedef struct node
{
	int n;        //传递盘数
	char begin;   //起始
	char mid;     //中转
	char end;     //放置位置
}Node;

void hanoi(Stack* st)
{
	while (!StackEmpty(st))
	{
		Node cur = StackTop(st);
		StackPop(st);
		if (cur.n == 1)
			printf("%c->%c\n", cur.begin, cur.end);
		else
		{
			Node tmp1 = { cur.n - 1,cur.mid,cur.begin,cur.end };
			StackPush(st, tmp1);
			Node tmp2 = { 1,cur.begin,cur.mid,cur.end };
			StackPush(st, tmp2);
			Node tmp3 = { cur.n - 1,cur.begin,cur.end,cur.mid };
			StackPush(st, tmp3);
		}
	}
}

int main()
{
	Stack st;
	StackInit(&st);
	Node nd = { 3,'A','B','C' };
	StackPush(&st,nd);
	hanoi(&st);

	return 0;
}

在这里插入图片描述

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

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

相关文章

【Web开发】Python实现Web服务器(Ubuntu下安装Flask)

&#x1f37a;基于Python的Web服务器系列相关文章编写如下&#x1f37a;&#xff1a; &#x1f388;【Web开发】Python实现Web服务器&#xff08;Flask快速入门&#xff09;&#x1f388;&#x1f388;【Web开发】Python实现Web服务器&#xff08;Flask案例测试&#xff09;&a…

【ESP32+freeRTOS学习笔记-(三)任务】

目录1、任务相关基本概念1.1 任务函数原型1.2 任务句柄TaskHandle_t 及任务控制块TCB_t1.3 任务状态1.4 优先级(Task Priorities)2 创建任务2.1 xTaskCreate2.2 xTaskCreateStatic()3 延迟函数使任务进入阻塞状态vTaskDelay()函数vTaskDelayUntil&#xff08;&#xff09;函数4…

【C++初阶】vector的模拟实现

文章目录vector的介绍vector的模拟实现成员变量Member functionsconstructor&#xff08;构造函数&#xff09;destructor&#xff08;析构函数&#xff09;operatorIteratorsbeginendCapacitysizecapacityreserveresizeElement accessoperator[]Modifierspush_backpop_backins…

手办商城系统|Springboot+vue+ElementUI手办商城系统

作者主页&#xff1a;编程指南针 作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、掘金特邀作者、多年架构师设计经验、腾讯课堂常驻讲师 主要内容&#xff1a;Java项目、毕业设计、简历模板、学习资料、面试题库、技术互助 收藏点赞不迷路 关注作者有好处 文末获取源…

C语言重点解剖预处理要点速记

1.宏定义字符串的时候一定要带上双引号。 2.程序的翻译&#xff0c;就是把文本代码翻译成二进制代码。分为4个阶段&#xff0c;即预处理&#xff0c;编译&#xff0c;汇编&#xff0c;链接。 3.预处理&#xff1a;头文件展开&#xff0c;去注释&#xff0c;宏替换&#xff0c…

基于Spring cloud + vue 的前后端分离的社团管理系统,数据库采用MYSQL,前端使用 VUE + element

背景 因学校没有成熟的社团管理系统&#xff0c;一为满足学校需求&#xff0c; 其次开源的社团管理项目&#xff0c;多为mvc ssm框架 很少有前后端分离的微服务框架&#xff0c;为了科技的发展&#xff0c; 更好学习解决高并发问题&#xff0c;也为了更好地掌握锤炼新技术&…

Web前端:广州蓝景实训

0基础也能学&#xff0c;5个月的课程时间&#xff0c;从小白到就业课程内容紧贴市场就业要求。广州蓝景前端实训课程&#xff0c;6年时间培养出过千名工程师&#xff0c;成功就业。今天就跟大家分享&#xff0c;蓝景学员的学习体验流程&#xff1a; 一、前端技术学习 1、授课…

3.Kafka

1.定义 1.1传统定义 Kafka是一个分布式的基于发布订阅模式的消息队列&#xff0c;主要应用于大数据的实时处理领域发布订阅&#xff1a;消息发布者不会直接将消息发送给订阅者&#xff0c;而是将发布的消息分为不同的类别&#xff0c;订阅者只接收感兴趣的消息 1.2最新定义 …

计算机网络期末重点考点总复习

第三章数据链路层 CRC检验问题 知识点 例题 要发送的数据为1101011011。采用CRC的生成多项式是 P(X)X^4 X 1试求应添加在数据后面的余数。数据在传输过程中最后一个1变成了0&#xff0c;问接收端能否发现&#xff1f; 若数据在传输过程中最后两个1都变成了0&#xff0c;问接…

MacOS系统安装Qt教程

我以为搜索QT下载&#xff0c;安装好QT Creator就能开开心心HelloWorld&#xff0c;然而还是太年轻了&#xff0c;一个 No suitable kit found 报错告诉我&#xff0c;可能没那么简单。于是&#xff0c;我去查了查&#xff0c;大概原因就是苹果处理器和x86不太一样&#xff0c;…

借助这几个工具,轻松实现在手机压缩视频

我有一个习惯&#xff0c;就是会将生活中发生的趣事&#xff0c;或是沿途好看的风景&#xff0c;用手机录制下来&#xff0c;这样的话&#xff0c;后续再回看这些视频的时候&#xff0c;就能勾起相对应的美好回忆。 但是久而久之&#xff0c;这些视频积累太多的话&#xff0c;就…

Springboot 2.7.5 HikariCP 连接池多数据源配置

一. 引言 当前项目遇到需要连接多个数据库的场景&#xff0c;此时需要引入多数据源了. 还有一些诸如以下的场景: 与第三方对接时&#xff0c;有些合作方并不会为了你的某些需求而给你开发一个功能&#xff0c;他们可以提供给你一个可以访问数据源的只读账号&#xff0c;你需要…

Web漏洞扫描篇-Nessus使用

软件介绍 Nessus是一种漏洞扫描器&#xff0c;个人和组织广泛使用它来识别和修复计算机系统中的漏洞。Nessus可以扫描广泛的漏洞&#xff0c;包括缺少补丁、弱密码和配置错误的系统&#xff0c;它可以扫描单个系统或整个网络上的漏洞。Nessus可以在各种平台上运行&#xff0c;…

【云原生进阶之容器】第一章Docker核心技术1.10节——Docker网络模型设计

1 容器网络背景概述 1.1 Linux的namespace+cgroup 先来简要回顾一下前面的内容,namespace和cgroup是Linux 内核的两大特性,namespace的诞生据说就是为了支持容器技术,那么这俩特性到底干了啥呢? - namespace:linux支持多种类型的namespace,包括Network,IPC,PID, Mount…

基于HOG、LBP完成特征工程,基于SVM/RF/XGBOOST/GBDT/CNN/DNN完成人脸识别+表情识别

在我之前的文章中写过很多关于人脸识别和表情识别的文章&#xff0c;今天有一个项目的需求就是需要做两种或者是多种任务&#xff0c;我在开发完对应的模型之后就突然想到了之前做过的人脸识别和表情识别的项目&#xff0c;就想着是否可以基于机器学习/深度学习等方式来同时实现…

算法训练第五十七天 | LeetCode 647、516动态规划结尾

LeetCode 647回文子串 题目简析&#xff1a; 给你一个字符串 s &#xff0c;请你统计并返回这个字符串中 回文子串 的数目。具有不同开始位置或结束位置的子串&#xff0c;即使是由相同的字符组成&#xff0c;也会被视作不同的子串。 思路分析&#xff1a; 本题的难点 主要…

【Aptos与Sui智能合约】(Move长话短说) #03 - 智能合约编写与发布

前言:本篇学习如何在Aptos上发布两个自定义的智能合约 0x1 前置条件 安装好Aptos工具,MacOS、Ubuntu、Windows系统的预编译的安装包下载地址 https://github.com/aptos-labs/aptos-core/releases?q=cli&expanded=true 想自己从源码开始编译,你就下载Source Code 安装…

千万不要把Request传递到异步线程里面,有坑

前几天在网上冲浪的时候看到一篇技术文章&#xff0c;讲的是他把一个 request 请求传递到了线程池里面&#xff0c;然后遇到了一个匪夷所思的情况。 他写了这篇文章&#xff0c;把自己针对这个问题的探索过程分享了出来&#xff1a; 《springboot 中如何正确的在异步线程中使用…

艾美捷脂质过氧化检测试剂盒参数说明和文献参考

脂质过氧化的定量对于评估氧化损伤在病理生理学疾病中的作用至关重要。脂质过氧化导致饱和和不饱和脂质的高反应性和不稳定的氢过氧化物的形成。 艾美捷脂质过氧化检测试剂盒直接利用与亚铁离子的氧化还原反应测量过氧化氢&#xff0c;将脂质过氧化氢提取到氯仿中&#xff0c;提…

【python绘制地图——folium的方法和类的介绍(思维导图)】

Python使用folium制作地图并生成png图片 第一章 folium的方法和类的介绍&#xff08;思维导图&#xff09; 第二章 使用folium制作地图 第三章 folium实用功能进阶 第三章 使用Html2Image生成png图片 第四章 使用reportlab制作pdf报告 文章目录Python使用folium制作地图并生成…