栈与队列的对决:用队列实现栈的2种思路

news2025/1/11 6:51:32

在这里插入图片描述

本篇博客会讲解力扣“225. 用队列实现栈”的解题思路,这是题目链接。

先来审题:
在这里插入图片描述
在这里插入图片描述

以下是输出示例:
在这里插入图片描述
以下是提示和进阶:
在这里插入图片描述
这道题有2种思路,分别使用2个和1个队列来实现栈。

准备工作

先来实现队列。由于本篇博客的重点不是队列应该如何实现,所以这里直接上代码:

// 链式结构:表示队列
typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

// 队列的结构
typedef struct Queue
{
	QNode* front; // 队头
	QNode* tail;  // 队尾
	int size;     // 有效数据个数
}Queue;

// 初始化队列
void QueueInit(Queue* pq);

// 队尾入队列
void QueuePush(Queue* pq, QDataType data);

// 队头出队列
void QueuePop(Queue* pq);

// 获取队列头部元素
QDataType QueueFront(Queue* pq);

// 获取队列队尾元素
QDataType QueueBack(Queue* pq);

// 获取队列中有效元素个数
int QueueSize(Queue* pq);

// 检测队列是否为空
bool QueueEmpty(Queue* pq);

// 销毁队列
void QueueDestroy(Queue* pq);

void QueueInit(Queue* pq)
{
	assert(pq);

	// 定义哨兵位
	QNode* head = (QNode*)malloc(sizeof(QNode));
	if (head == NULL)
	{
		perror("malloc fail");
		return;
	}
	head->data = 0;
	head->next = NULL;

	pq->front = head;
	pq->tail = head;
	pq->size = 0;
}

void QueuePush(Queue* pq, QDataType data)
{
	assert(pq);

	// 创建新结点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = data;
	newnode->next = NULL;

	// 链接在尾部
	pq->tail->next = newnode;
	pq->tail = newnode;

	pq->size++;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	// 判断非空
	assert(!QueueEmpty(pq));

	if (pq->size == 1)
	{
		// 删除数据
		free(pq->tail);
		pq->front->next = NULL;
		pq->tail = pq->front;
	}
	else
	{
		// 至少2个数据
		QNode* first = pq->front->next;
		QNode* second = first->next;
		// 删除数据
		free(first);
		first = NULL;
		// 链接
		pq->front->next = second;
	}

	pq->size--;
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	// 判断非空
	assert(!QueueEmpty(pq));

	return pq->front->next->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	// 判断非空
	assert(!QueueEmpty(pq));

	return pq->tail->data;
}

int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}

void QueueDestroy(Queue* pq)
{
	QNode* del = pq->front;
	while (del)
	{
		QNode* next = del->next;
		free(del);
		del = next;
	}

	pq->front = NULL;
	pq->tail = NULL;
	pq->size = 0;
}

思路1

我们可以使用2个队列来实现栈。

首先定义结构:

typedef struct {
	Queue q1;
	Queue q2;
} MyStack;

初始化时,分别对2个队列初始化即可:

MyStack* myStackCreate() {
	MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
	// 初始化2个队列
	QueueInit(&obj->q1);
	QueueInit(&obj->q2);

	return obj;
}

假设我们让一个队列存数据,另一个队列空着,入数据时,应该往有数据的队列插入数据。

void myStackPush(MyStack* obj, int x) {
	// 往有数据的队列中入数据,若2个队列都为空,则随便
	if (QueueEmpty(&obj->q1))
		QueuePush(&obj->q2, x);
	else
		QueuePush(&obj->q1, x);
}

问题来了,如何删除数据呢?

首先栈非空才能删除数据,所以需要断言一下。

假设栈非空,如果我们每次都是往有数据的队列插入数据,一定有1个队列为空,另一个队列非空,我们先使用判断语句,锁定队列中哪个为空,则另一个非空。

假设队列中依次插入了[1 2 3 4 5],如果要出队列,根据队列“先进先出”的特性,只能出1,但是作为栈,应该要出5,应该如何操作呢?
在这里插入图片描述

我们先出1,插入到空队列中,相当于把1“转移”了。
在这里插入图片描述

接着转移2。
在这里插入图片描述

继续转移,直到上面的队列只剩1个元素。
在这里插入图片描述
删除并返回5即可。
在这里插入图片描述
此时,“栈顶”元素5就被删除了。

int myStackPop(MyStack* obj) {
	// 非空才能删除
	assert(!myStackEmpty(obj));

	// 判断队列是否为空
	Queue* emptyQueue = &obj->q1;
	Queue* nonEmptyQueue = &obj->q2;
	if (QueueEmpty(&obj->q2))
	{
		emptyQueue = &obj->q2;
		nonEmptyQueue = &obj->q1;
	}

	// 把非空队列中的数据插入到空队列中
	while (QueueSize(nonEmptyQueue) > 1)
	{
		// 转移数据
		QueuePush(emptyQueue, QueueFront(nonEmptyQueue));
		QueuePop(nonEmptyQueue);
	}

	// 取非空队列最后一个数据并删除
	int ret = QueueFront(nonEmptyQueue);
	QueuePop(nonEmptyQueue);
	return ret;
}

有数据的队列中,队尾的元素就是“栈顶”元素。注意取“栈顶”元素之前要判断非空。

int myStackTop(MyStack* obj) {
	// 非空才能取数据
	assert(!myStackEmpty(obj));

	// 取非空队列的队尾
	if (QueueEmpty(&obj->q1))
		return QueueBack(&obj->q2);
	else
		return QueueBack(&obj->q1);
}

2个队列都为空,则栈为空。

bool myStackEmpty(MyStack* obj) {
	// 2个队列都为空,则栈为空
	return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

最后free即可。

void myStackFree(MyStack* obj) {
	// 销毁2个队列
	QueueDestroy(&obj->q1);
	QueueDestroy(&obj->q2);

	free(obj);
	obj = NULL;
}

完整代码如下:

// 链式结构:表示队列
typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

// 队列的结构
typedef struct Queue
{
	QNode* front; // 队头
	QNode* tail;  // 队尾
	int size;     // 有效数据个数
}Queue;

// 初始化队列
void QueueInit(Queue* pq);

// 队尾入队列
void QueuePush(Queue* pq, QDataType data);

// 队头出队列
void QueuePop(Queue* pq);

// 获取队列头部元素
QDataType QueueFront(Queue* pq);

// 获取队列队尾元素
QDataType QueueBack(Queue* pq);

// 获取队列中有效元素个数
int QueueSize(Queue* pq);

// 检测队列是否为空
bool QueueEmpty(Queue* pq);

// 销毁队列
void QueueDestroy(Queue* pq);

void QueueInit(Queue* pq)
{
	assert(pq);

	// 定义哨兵位
	QNode* head = (QNode*)malloc(sizeof(QNode));
	if (head == NULL)
	{
		perror("malloc fail");
		return;
	}
	head->data = 0;
	head->next = NULL;

	pq->front = head;
	pq->tail = head;
	pq->size = 0;
}

void QueuePush(Queue* pq, QDataType data)
{
	assert(pq);

	// 创建新结点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = data;
	newnode->next = NULL;

	// 链接在尾部
	pq->tail->next = newnode;
	pq->tail = newnode;

	pq->size++;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	// 判断非空
	assert(!QueueEmpty(pq));

	if (pq->size == 1)
	{
		// 删除数据
		free(pq->tail);
		pq->front->next = NULL;
		pq->tail = pq->front;
	}
	else
	{
		// 至少2个数据
		QNode* first = pq->front->next;
		QNode* second = first->next;
		// 删除数据
		free(first);
		first = NULL;
		// 链接
		pq->front->next = second;
	}

	pq->size--;
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	// 判断非空
	assert(!QueueEmpty(pq));

	return pq->front->next->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	// 判断非空
	assert(!QueueEmpty(pq));

	return pq->tail->data;
}

int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}

void QueueDestroy(Queue* pq)
{
	QNode* del = pq->front;
	while (del)
	{
		QNode* next = del->next;
		free(del);
		del = next;
	}

	pq->front = NULL;
	pq->tail = NULL;
	pq->size = 0;
}

typedef struct {
	Queue q1;
	Queue q2;
} MyStack;

MyStack* myStackCreate() {
	MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
	// 初始化2个队列
	QueueInit(&obj->q1);
	QueueInit(&obj->q2);

	return obj;
}

void myStackPush(MyStack* obj, int x) {
	// 往有数据的队列中入数据,若2个队列都为空,则随便
	if (QueueEmpty(&obj->q1))
		QueuePush(&obj->q2, x);
	else
		QueuePush(&obj->q1, x);
}

bool myStackEmpty(MyStack* obj);

int myStackPop(MyStack* obj) {
	// 非空才能删除
	assert(!myStackEmpty(obj));

	// 判断队列是否为空
	Queue* emptyQueue = &obj->q1;
	Queue* nonEmptyQueue = &obj->q2;
	if (QueueEmpty(&obj->q2))
	{
		emptyQueue = &obj->q2;
		nonEmptyQueue = &obj->q1;
	}

	// 把非空队列中的数据插入到空队列中
	while (QueueSize(nonEmptyQueue) > 1)
	{
		// 转移数据
		QueuePush(emptyQueue, QueueFront(nonEmptyQueue));
		QueuePop(nonEmptyQueue);
	}

	// 取非空队列最后一个数据并删除
	int ret = QueueFront(nonEmptyQueue);
	QueuePop(nonEmptyQueue);
	return ret;
}

int myStackTop(MyStack* obj) {
	// 非空才能取数据
	assert(!myStackEmpty(obj));

	// 取非空队列的队尾
	if (QueueEmpty(&obj->q1))
		return QueueBack(&obj->q2);
	else
		return QueueBack(&obj->q1);
}

bool myStackEmpty(MyStack* obj) {
	// 2个队列都为空,则栈为空
	return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) {
	// 销毁2个队列
	QueueDestroy(&obj->q1);
	QueueDestroy(&obj->q2);

	free(obj);
	obj = NULL;
}

在这里插入图片描述
通过喽。

思路2

其实,要实现栈,1个队列足矣。

结构的定义如下:

typedef struct {
	Queue q;
} MyStack;

初始化:

MyStack* myStackCreate() {
	MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
	QueueInit(&obj->q);
	return obj;
}

直接入数据:

void myStackPush(MyStack* obj, int x) {
	QueuePush(&obj->q, x);
}

重点来了,怎么删除数据呢?前面2个队列实现栈的思路中,采取的是2个队列的数据相互转移的思路。如果只有1个队列,就自己给自己转移数据即可。比如:依次插入[1 2 3 4 5],我们要删除“栈顶”的5,就把前n-1个数据从队头转移到队尾,原来队尾的5就会跑到队头,删除它即可。

在这里插入图片描述

出1,再插入1(即转移1):
在这里插入图片描述
在这里插入图片描述
再转移2:
在这里插入图片描述
在这里插入图片描述

同理转移3、4:
在这里插入图片描述
最后删除并返回5:
在这里插入图片描述
代码如下:

int myStackPop(MyStack* obj) {
	// 检查非空
	assert(!myStackEmpty(obj));

	// 把队头的size个数据转移到队尾
	int count = QueueSize(&obj->q) - 1;
	while (count--)
	{
		QueuePush(&obj->q, QueueFront(&obj->q));
		QueuePop(&obj->q);
	}

	// 此时原来在队尾的数据跑到了队头,删除即可
	int ret = QueueFront(&obj->q);
	QueuePop(&obj->q);
	return ret;
}

“栈顶”元素就是队尾元素:

int myStackTop(MyStack* obj) {
	// 检查非空
	assert(!myStackEmpty(obj));

	return QueueBack(&obj->q);
}

队列为空,则栈为空:

bool myStackEmpty(MyStack* obj) {
	return QueueEmpty(&obj->q);
}

最后释放空间:

void myStackFree(MyStack* obj) {
	QueueDestroy(&obj->q);
	free(obj);
	obj = NULL;
}

完整的代码如下:

// 链式结构:表示队列
typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* next;
	QDataType data;
}QNode;

// 队列的结构
typedef struct Queue
{
	QNode* front; // 队头
	QNode* tail;  // 队尾
	int size;     // 有效数据个数
}Queue;

// 初始化队列
void QueueInit(Queue* pq);

// 队尾入队列
void QueuePush(Queue* pq, QDataType data);

// 队头出队列
void QueuePop(Queue* pq);

// 获取队列头部元素
QDataType QueueFront(Queue* pq);

// 获取队列队尾元素
QDataType QueueBack(Queue* pq);

// 获取队列中有效元素个数
int QueueSize(Queue* pq);

// 检测队列是否为空
bool QueueEmpty(Queue* pq);

// 销毁队列
void QueueDestroy(Queue* pq);

void QueueInit(Queue* pq)
{
	assert(pq);

	// 定义哨兵位
	QNode* head = (QNode*)malloc(sizeof(QNode));
	if (head == NULL)
	{
		perror("malloc fail");
		return;
	}
	head->data = 0;
	head->next = NULL;

	pq->front = head;
	pq->tail = head;
	pq->size = 0;
}

void QueuePush(Queue* pq, QDataType data)
{
	assert(pq);

	// 创建新结点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = data;
	newnode->next = NULL;

	// 链接在尾部
	pq->tail->next = newnode;
	pq->tail = newnode;

	pq->size++;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	// 判断非空
	assert(!QueueEmpty(pq));

	if (pq->size == 1)
	{
		// 删除数据
		free(pq->tail);
		pq->front->next = NULL;
		pq->tail = pq->front;
	}
	else
	{
		// 至少2个数据
		QNode* first = pq->front->next;
		QNode* second = first->next;
		// 删除数据
		free(first);
		first = NULL;
		// 链接
		pq->front->next = second;
	}

	pq->size--;
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	// 判断非空
	assert(!QueueEmpty(pq));

	return pq->front->next->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	// 判断非空
	assert(!QueueEmpty(pq));

	return pq->tail->data;
}

int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}

void QueueDestroy(Queue* pq)
{
	QNode* del = pq->front;
	while (del)
	{
		QNode* next = del->next;
		free(del);
		del = next;
	}

	pq->front = NULL;
	pq->tail = NULL;
	pq->size = 0;
}

typedef struct {
	Queue q;
} MyStack;

MyStack* myStackCreate() {
	MyStack* obj = (MyStack*)malloc(sizeof(MyStack));
	QueueInit(&obj->q);
	return obj;
}

void myStackPush(MyStack* obj, int x) {
	QueuePush(&obj->q, x);
}

bool myStackEmpty(MyStack* obj);

int myStackPop(MyStack* obj) {
	// 检查非空
	assert(!myStackEmpty(obj));

	// 把队头的size个数据转移到队尾
	int count = QueueSize(&obj->q) - 1;
	while (count--)
	{
		QueuePush(&obj->q, QueueFront(&obj->q));
		QueuePop(&obj->q);
	}

	// 此时原来在队尾的数据跑到了队头,删除即可
	int ret = QueueFront(&obj->q);
	QueuePop(&obj->q);
	return ret;
}

int myStackTop(MyStack* obj) {
	// 检查非空
	assert(!myStackEmpty(obj));

	return QueueBack(&obj->q);
}

bool myStackEmpty(MyStack* obj) {
	return QueueEmpty(&obj->q);
}

void myStackFree(MyStack* obj) {
	QueueDestroy(&obj->q);
	free(obj);
	obj = NULL;
}

在这里插入图片描述
通过!

总结

由于队列“先进先出”的性质,出自己的数据再插入,数据就从队头跑到了队尾,所以可以只使用1个队列实现栈。但是如果使用栈实现队列,就必须使用2个栈才行,因为栈“后进先出”的性质,如果出自己的数据再插入,数据还是在栈顶。

感谢大家的阅读!

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

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

相关文章

一步一步详解LSTM网络【从RNN到LSTM到GRU等,直至attention】

一步一步详解LSTM网络【从RNN到LSTM到GRU等,直至attention】 0、前言1、Recurrent Neural Networks循环神经网络2、The Problem of Long-Term Dependencies长期依赖的问题3、LSTM Networks4、The Core Idea Behind LSTMs5、Step-by-Step LSTM Walk Through6、Varian…

Linux--进程(一篇博客让你理解操作系统的底层)

Linux–进程 本篇博客讲开始进入进程模块,我们先从冯诺依曼的体系结构和操作系统讲起,由浅入深,先梳理一个Linux相关知识体系框架~ 进程目录 Linux--进程一、冯诺依曼体系结构1.1外围设备1.2中央处理器(CPU)1.3存储器(内存)1.3.1什么是内存&a…

【LeetCode20】有效的括号——图解

​ 你好,欢迎来到我的博客!作为一名程序员,我经常刷LeetCode题目来提升自己的编程能力。在我的博客里,我会分享一些我自己做过的题目和解题思路,希望能够帮助到大家。今天,我想和大家分享一道挑战性较高的题…

【Java校招面试】实战面经(三)

目录 前言一、简历中项目的难点及解决方案二、讲讲分布式锁的实现三、AQS(Abstract Queued Synchronizer)的原理四、ConcurrentHashMap的原理五、MySQL InnoDB存储引擎中的MVCC解决了什么问题,MVCC的实现原理六、平时怎么创建线程?为什么用线程池&#x…

Red Hat Enterprise Linux (RHEL) 8.8 正式版发布

红帽企业 Linux 8.8 发布 Red Hat Enterprise Linux (RHEL) 8.8 (x86_64, aarch64) Release 请访问原文链接:https://sysin.org/blog/rhel-8/,查看最新版。原创作品,转载请保留出处。 作者主页:sysin.org 2023年 5月 16日&…

Java学习路线(5)——数组

一、数组的概念 数组是用来存储一系列同类型数据的内存区域。 二、存储数据 1、静态初始化数组 作用: 定义数组时直接赋值。 格式: 【数据类型[] 变量名 new 数据类型[]{值1,值2…,值n}】 示例: double[] height {172.5,170.1,145.1,152.5,…

java常用工具之Scanner类

目录 简介一、扫描控制台输入1.1nextLine1.2nextInt1.3其他方法 二、扫描文件三、查找匹配项四、小结 简介 Java 的 Scanner 类是一个方便在控制台扫描用户输入的工具类,虽然它也可以扫描文件内容,但我们通常更喜欢它扮演前面的角色,因为扫描…

Flink第五章:处理函数

系列文章目录 Flink第一章:环境搭建 Flink第二章:基本操作. Flink第三章:基本操作(二) Flink第四章:水位线和窗口 Flink第五章:处理函数 文章目录 系列文章目录前言一、基本处理函数(ProcessFunction)二、按键分区处理函数(KeyedProcessFunction)1.处理…

【Linux进阶之路】基本权限的理解

文章目录 一.用户1.分类2.su3.su-4.sudo 二.文件1.文件分类2.文件权限3.文件权限的身份4.chmod——改写文件权限第一种方式第二种方式 5.chown——改写文件拥有者身份6.chgrp ——改写文件所属组身份7.umask ——设置权限掩码8.目录权限9.粘滞位——特殊的可执行权限 一.用户 …

关于Markdown文件的处理【笔记】

关于Markdown文件的处理【笔记】 前言推荐关于Markdown文件的处理一、md文件转word文档1 准备2 打开3 转为word文档4 导出结果5 打开 二、word文档转md文件1 准备2 导入3 打开4 显示图片5 打开 三、导入到CSDN中1 选择导入2 查看 四、导入设置1 前言2 导入设置3 修改配置 最后 …

ES6之生成器

文章目录 前言一、生成器是什么?二、生成器总结 前言 生成器 一、生成器是什么? 生成器就是一个特殊的函数,实现异步编程。格式function *名称(){...} (这个*靠近function写,靠近名称写,或者两边空格都不靠近均正确)…

[比赛简介]Parkinson‘s Freezing of Gait Prediction

比赛链接:https://www.kaggle.com/competitions/tlvmc-parkinsons-freezing-gait-prediction 比赛简介 本次比赛的目标是检测步态冻结(FOG),这是一种使人衰弱的症状,困扰着许多帕金森病患者。您将开发一个机器学习…

YOLO V3 SPP ultralytics 第三节:关于yolo 中cfg的网络配置信息和读取cfg配置文件

目录 1. 介绍 2. 关于yolo的cfg网络配置文件 2.1 关于卷积层 2.2 关于池化层 2.3 关于捷径分支shortcut 2.4 关于route 层 2.5 关于上采样层 2.6 关于yolo层 3. 解析cfg 文件 4. 代码 1. 介绍 根据 第二节 的步骤,生成了属于自己的 my_yolov3.cfg 配置…

Python 墨西哥湾流(gulf stream)可视化

背景介绍 墨西哥湾流和黑潮分别是北半球两支强大的西边界流,墨西哥湾流的流速还要强于黑潮,也是温盐环流的重要组成部分。 引入涡度的概念,将涡度分为两个部分: 1、行星涡度,记为 f f f,与地球自转有关…

【软考数据库】第十四章 数据库主流应用技术

目录 14.1 分布式数据库 14.2 Web与数据库 14.3 XML与数据库 14.4 面向对象数据库 14.5 大数据与数据库 14.6 NewSQL 前言: 笔记来自《文老师软考数据库》教材精讲,精讲视频在b站,某宝都可以找到,个人感觉通俗易懂。 14.1 …

Springcloud1---->openFeign

目录 简介快速入门导入依赖开启Feign配置Feign客户端接口Feign使用小结feign feign配置负载均衡feign配置Hystix支持 简介 Feign可以把Rest的请求进行隐藏,伪装成类似SpringMVC的Controller一样。你不用再自己拼接url,拼接参数等等操作,一切…

WebSocket 详解,以及用QWebSocket 实现服务端和客户端(含代码例子)

目录 1、WebSocket 诞生背景 2、WebSocket的特点: 3、 WebSocket 简介 4、WebSocket 优点 5、QWebSocket通讯—客户端: 6、QWebSocket通讯—服务端: 1、WebSocket 诞生背景 早期,很多网站为了实现推送技术,所用的技术都…

初始Linux发展

目录 前言 Linux概念: 一.Linux发展历史 二.Linux的发展现状 三.发行版本 四.Linux 环境的搭建方式 主要有三种 : 4.6下载方式: 五.XShell软件 前言 Linux概念: Linux,全称GNU/Linux,是一套免费使用和自由传播的…

python中的对象和变量的关系

这里写目录标题 对象简介对象的结构变量和对象 对象简介 Python是一门面向对象的编程语言! 一切皆对象! 程序运行当中,所有的数据都是存储到内存当中然后再运行的! 对象就是内存中专门用来存储指定数据的一块区域 对象实际上就是…

《计算机网络—自顶向下方法》 Wireshark实验(九):DHCP 协议分析

DHCP(Dynamic Host configuration protocol)动态主机配置协议,它可以为客户机自动分配 IP 地址、子网掩码以及缺省网关、DNS 服务器的 IP 地址等 TCP/IP 参数, 简单来说,就是在 DHCP 服务器上有一个数据库,…