数据结构线性表——顺序表

news2024/7/6 17:48:42

前言:小伙伴们好久不见,从这篇文章开始,我们就要正式进入数据结构的学习啦。

学习的难度也将逐步上升,希望小伙伴们能够和博主一起坚持,一起加油!!!


目录

一.什么是线性表 

二.什么是顺序表

三.顺序表实现

1.顺序表的定义

2.顺序表初始化

3.顺序表的销毁

四.顺序表的操作

1.顺序表的扩容

2.顺序表的尾插

3.顺序表的打印

 4.顺序表的尾删

 4.顺序表的头插

5.顺序表的头删

6.顺序表的任意插

7.顺序表的任意删

8.顺序表的查找

9.顺序表的修改

五.代码展示

1.Sqlist.h

2.Sqlist.c

3.test.c

六.总结


一.什么是线性表 

线性表是具有n个相同特性的数据元素的有限序列,是一种在实际应用中广泛使用的数据结构,常见的线性表有:顺序表、链表、栈、队列、字符串等等。

从逻辑上说,线性表是一个连续的直线。但是从实际物理结构上来说,线性表不一定是连续的,那么根据线性表的连续性,将其分为连续的顺序表和不连续的链表

今天我们就先来学习连续的线性表——顺序表

如果小伙伴们已经跟博主一起学习过了通讯录,那么这一块就是小菜一碟哦!


二.什么是顺序表

顺序表是通过使用一段物理地址连续的存储单元依次存储数据元素的线性结构

那么我们如何理解顺序二字呢???

实际上,这里的顺序并不是说我们存放的数据要按升序或者降序的方式存储,毕竟我们存的数据不一定都是数字

顺序的含义其实是我们在存数据时要一个一个紧挨着的存储,不能间隔存储

说到这里,小伙伴们应该都能联想到数组,没错,实际上顺序表就是采用数组来存储数据的

在了解过顺序表的基本结构之后,我们就开始正式的来讲解顺序表该如何实现


三.顺序表实现

1.顺序表的定义

想定义一个顺序表,就需要先来想想顺序表需要哪些结构元素,首先是一个数组,数组的大小,还要有一个数据来统计我们当前的数据个数

我们学习过动态内存管理,所以这里我们直接定义一个动态顺序表

typedef int SLDataType;
//动态顺序表
typedef struct SqList
{
	SLDataType* data;
	int size;//数据个数
	int capacity;//表长
}SL;

定义动态数组,我们就只需要一个头指针

要注意的是,我们用SLDataType来定义顺序表所存储的数据的类型,方便以后得维护和修改。


2.顺序表初始化

那么在定义好顺序表的基本结构之后,我们要先对其进行初始化,通过封装一个函数:

//初始化顺序表
void SqListInit(SL* ps)
{
	ps->data = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

将头指针置空,并将size和capacity置零

我们前边学习过,动态开辟的空间使用后要及时释放,所以我们需要封装一个函数来销毁顺序表。


3.顺序表的销毁

//销毁
void SqListDestroy(SL* ps)
{
	free(ps->data);
	ps->data = NULL;
	ps->capacity = ps->size = 0;
}

通过这样三个步骤,我们才算是真正意义上的造出了一个顺序表。

紧接着,我们来讲解顺序表的各种操作。


四.顺序表的操作

对于我们的数据结构这块内容的基本操作,自然是增、删、查、改

那么我们要特别注意的是对于顺序表的头、尾两个位置的操作。

1.顺序表的扩容

在对顺序表进行各种操作之前,最重要的一点就是要判断这个表当前是否已经占满,如果占满就必须得扩容

而且小伙伴们似乎发现,我们刚才初始化的时候把顺序表的长度初始化为0,其实就是为了将初始化长度和扩容相结合

//扩容
void SqlistDilatation(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType tmp = (SLDataType*)realloc(ps->data, newcapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("SqListPushBack->realloc");
			exit(-1);
		}
		ps->data = tmp;
		ps->capacity = newcapacity;
	}
}

首先进行一个判断,如果当前的数据个数等于顺序表的大小,那么证明已经满了,需要扩容,这时候定义一个新的顺序表长度,注意看这一行:

int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;

这里我们用一个三目运算符,它的含义是什么呢???

当我们顺序表的长度为0时,也就是刚初始化完还没有大小,那么我们就将它扩容为4个空间,以后没次扩容都是原大小的二倍。 

有了扩容之后,我们就可以安心的进行各种操作了。


2.顺序表的尾插

尾插,顾名思义就是在线性表的数据末尾插入一个数据。

注意是数据的末尾,而不是整个表的末尾

那么我们该怎么尾插呢???

其实很简单,毕竟顺序表本质上就是一个数组,而且我们也有数据的个数size这个变量初始时size是0,每填入一个数据,size都会指向下一个空白空间而这个空间,不就是数据的末尾吗

所以尾插的实现如下:

//尾插
void SqListPushBack(SL* ps, SLDataType x)
{
    //判断扩容
	SqlistDilatation(ps);	
	ps->data[ps->size] = x;	
	ps->size++;
}

记得要先判断是否需要扩容!!!

那么我们的尾插到底能否实现我们想要的结果呢???

想要判断正确与否,我们还得封装一个打印顺序表函数,这样我们才能看到结果。


3.顺序表的打印

打印就很简单了,只需要通过循环来实现即可:

//打印
void SqListPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->data[i]);
	}
}

接下来我们就来测试尾插是否能够成功:

void Test(SL* s)
{
	SqListInit(s);
	SqListPushBack(s, 1); 
	SqListPrint(s);
	printf("\n");
	SqListPushBack(s, 2);
	SqListPrint(s);
	printf("\n");
	SqListPushBack(s, 3); 
	SqListPrint(s);
	printf("\n");
	SqListPushBack(s, 4);
	SqListPrint(s);
    SqListDestroy(s);
}

 我们封装一个测试函数,先进行初始化,然后每尾插一次,就打印一次并换行,得到结果如下:

确实是从末尾在一个一个的插入,所以我们的尾插并没有错误。

那么既然有插入,就会有删除,接下来我们来讲解尾删


 4.顺序表的尾删

该怎么尾删呢,既然有了size,那么只需要size--就可以找到末尾的数据,那么该怎么把它删除

实际上,我们不可能实现真正意义上的删除因为是数组,我们不可能把整个数组的大小减一

所以所谓的删除,不过是把原本的数据置0或者置为其他的规定的数字

那么你有没有想过,我们就非得将数据改变吗???

如果我们的size--,我们在遍历数组的时候,是不是就不会遍历到末尾的数据了虽然这个数据还存在,但是我们下次尾插时,是不是就把它给覆盖了。所以尾删其实特别简单:

//尾删
void SqListPopBack(SL* ps)
{
	assert(ps->size > 0);
	ps->size--;
}

唯一要注意的就是判断size是否为0,没数据还删啥呀。

通过测试:

void Test(SL* s)
{
	SqListInit(s);
	SqListPushBack(s, 1); 
	SqListPrint(s);
	printf("\n");
	SqListPushBack(s, 2);
    SqListPrint(s);
	printf("\n");
	SqListPushBack(s, 3); 
	SqListPrint(s);
	printf("\n");
	SqListPopBack(s);
	SqListPrint(s);
    SqListDestroy(s);
}

确实实现了末尾数据的删除。 

 

那么讲完尾部的操作之后,就该轮到头部了,那么头部又该如何操作呢?


 4.顺序表的头插

那么怎么向头部插入数据呢?在数组的开头开一个空间?肯定是不可能的,那么对于我们的顺序表,只有一种操作:那就是整体数据向后挪一位,再将新数据头插进来

既然要将数据后挪,就肯定还要先判断是否需要扩容。

具体操作如下:

//头插
void SqListPushFront(SL* ps, SLDataType x)
{
	//判断扩容
	SqlistDilatation(ps);
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->data[end + 1] = ps->data[end];
        end--;
	}
	ps->data[0] = x;
	ps->size++;
}

首先要先找到末尾元素的下标end,然后循环后移,最后头插。

来看测试:

void Test(SL* s)
{
	SqListInit(s);
	SqListPushBack(s, 1); 
	SqListPrint(s);
	printf("\n");
	SqListPushBack(s, 2);
	SqListPrint(s);
	printf("\n");
	SqListPushFront(s, 4);
	SqListPrint(s);
	printf("\n");
	SqListPushFront(s, 5);
	SqListPrint(s);
    SqListDestroy(s);
}

结果如下:


5.顺序表的头删

头删和头插可以说是反着来的,头插是数据往后挪动,头删则是数据依次往前覆盖

要注意的是,头删也要判断数据是否为空,没数据还删什么?

//头删
void SqListPopFront(SL* ps)
{
	assert(ps->size > 0);
	int begin = 0;
	while (begin < ps->size - 1)
	{
		ps->data[begin] = ps->data[begin + 1];
		begin++;
	}
	ps->size--;
}

至于最后一个数据,我们类似于尾删,不用管它,下一次尾插或者头插自然会将其覆盖

测试如下:

void Test(SL* s)
{
	SqListInit(s);
	SqListPushBack(s, 1); 
	SqListPrint(s);
	printf("\n");
	SqListPushBack(s, 2);
	SqListPrint(s);
	printf("\n");
	SqListPushFront(s, 4);
	SqListPrint(s);
	printf("\n");
	SqListPopFront(s);
	SqListPrint(s);
	printf("\n");
	SqListPopFront(s);
	SqListPrint(s);
	SqListDestroy(s);
}

结果如下:

那么我们前边讲的都是顺序表固定位置的操作,那么接下来我们就来讲解顺序表任意位置的操作。


6.顺序表的任意插

虽然说是任意位置的插入,但是还是要满足顺序表的条件,只能在0~size的范围内插入

插入之前,同样需要先判断是否需要扩容

任意位置的插入其实和头插差不多,头插是把全部的数据向后挪,而任意插只需要挪动我们要插入的位置的后边的数据

//任意插
void SqListInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	//判断扩容
	SqlistDilatation(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->data[end + 1] = ps->data[end];
		end--;
	}
	ps->data[pos] = x;
	ps->size++;
}

测试如下:

void Test(SL* s)
{
	SqListInit(s);
	SqListPushBack(s, 1); 
	SqListPushBack(s, 2);
	SqListPushBack(s, 4);
	SqListPrint(s);
	printf("\n");
	SqListInsert(s, 2, 3);
	SqListPrint(s);
	SqListDestroy(s);
}

结果如下:


7.顺序表的任意删

任意删的形式则和头删差不多,需要从指定的位置开始,后边的数据依次往前覆盖。

值得注意的是,任意位置插入时,可以在size位置插入,但是任意位置删除时,size处是没有没数据可删的。 

实现如下:

//任意删
void SqListErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	int begin = pos;
	while (begin < ps->size - 1)
	{
		ps->data[begin] = ps->data[begin + 1];
		begin++;
	}
	ps->size--;
}

测试如下:

void Test(SL* s)
{
	SqListInit(s);
	SqListPushBack(s, 1); 
	SqListPushBack(s, 2);
	SqListPushBack(s, 5);
	SqListPushBack(s, 3);
	SqListPrint(s);
	printf("\n");
	SqListErase(s, 2);
	SqListPrint(s);
	SqListDestroy(s);
}

结果如下:


8.顺序表的查找

查找有两种方式,一个是按数据查,一个是按数据的位置下标查

但是一般情况下我们都是按照数据来查它是否存在,存在则打印其下标位置

查找其实就是在遍历的基础上找到某个数据,还有一种特殊情况是这个数据有多个,那么我们就同时打印多个下标

实现如下:

//查找
void SqListFind(SL* ps, SLDataType x)
{
	assert(ps);
	int count = 0;
	for(int i = 0; i < ps->size; i++)
	{
		if (x == ps->data[i])
		{
			count = 1;
			printf("下标为:%d\n", i);
		}
	}
	if (count == 0)
	{
		printf("该数据不存在");
	}
}

这里我们用count来做一个标志,用于判断要找的数据是否存在。

测试如下:

void Test(SL* s)
{
	SqListInit(s);
	SqListPushBack(s, 1); 
	SqListPushBack(s, 2);
	SqListPushBack(s, 5);
	SqListPushBack(s, 3);
	SqListPushBack(s, 3);
	SqListFind(s, 3);
	SqListFind(s, 4);
	SqListDestroy(s);
}

结果如下:


9.顺序表的修改

修改数据一般采用通过数组下标修改的方式,那么前提就是你选择的下标在0~size之内

实现如下:

//修改
void SqListAmend(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	ps->data[pos] = x;
}

修改就比较简单了,直接找到数组的下标位置修改就行。

测试如下:

void Test(SL* s)
{
	SqListInit(s);
	SqListPushBack(s, 1); 
	SqListPushBack(s, 2);
	SqListPushBack(s, 5);
	SqListPushBack(s, 3);
	SqListPrint(s);
	printf("\n");
	SqListAmend(s, 2, 3);
	SqListAmend(s, 3, 4);
	SqListPrint(s);
	SqListDestroy(s);
}

结果如下:


五.代码展示

1.Sqlist.h

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int SLDataType;

//动态顺序表
typedef struct SqList
{
	SLDataType* data;
	int size;
	int capacity;
}SL;

//接口函数
//初始化
void SqListInit(SL* ps);
//销毁
void SqListDestroy(SL* ps);
//打印
void SqListPrint(SL* ps);
//扩容
void SqlistDilatation(SL* ps);
//尾插
void SqListPushBack(SL* ps, SLDataType x);
//尾删
void SqListPopBack(SL* ps);
//头插
void SqListPushFront(SL* ps, SLDataType x);
//头删
void SqListPopFront(SL* ps);
//任意插
void SqListInsert(SL* ps, int pos, SLDataType x);
//任意删
void SqListErase(SL* ps, int pos);
//查找
void SqListFind(SL* ps, SLDataType x);
//修改
void SqListAmend(SL* ps, int pos, SLDataType x);

2.Sqlist.c

#include "Sqlist.h"
//初始化
void SqListInit(SL* ps)
{
	assert(ps);
	ps->data = NULL;
	ps->size = 0;
	ps->capacity = 0;
}
//扩容
void SqlistDilatation(SL* ps)
{
	assert(ps);
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->data, newcapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("SqListPushBack->realloc");
			exit(-1);
		}
		ps->data = tmp;
		ps->capacity = newcapacity;
	}
}
//销毁
void SqListDestroy(SL* ps)
{
	assert(ps);
	free(ps->data);
	ps->data = NULL;
	ps->capacity = ps->size = 0;
}
//打印
void SqListPrint(SL* ps)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->data[i]);
	}
}
//尾插
void SqListPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	//判断扩容
	SqlistDilatation(ps);	
	ps->data[ps->size] = x;	
	ps->size++;
}
//尾删
void SqListPopBack(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);
	ps->size--;
}
//头插
void SqListPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	//判断扩容
	SqlistDilatation(ps);
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->data[end + 1] = ps->data[end];
		end--;
	}
	ps->data[0] = x;
	ps->size++;
}
//头删
void SqListPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);
	int begin = 0;
	while (begin < ps->size - 1)
	{
		ps->data[begin] = ps->data[begin + 1];
		begin++;
	}
	ps->size--;
}
//任意插
void SqListInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	//判断扩容
	SqlistDilatation(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->data[end + 1] = ps->data[end];
		end--;
	}
	ps->data[pos] = x;
	ps->size++;
}
//任意删
void SqListErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	int begin = pos;
	while (begin < ps->size - 1)
	{
		ps->data[begin] = ps->data[begin + 1];
		begin++;
	}
	ps->size--;
}
//查找
void SqListFind(SL* ps, SLDataType x)
{
	assert(ps);
	int count = 0;
	for(int i = 0; i < ps->size; i++)
	{
		if (x == ps->data[i])
		{
			count = 1;
			printf("下标为:%d\n", i);
		}
	}
	if (count == 0)
	{
		printf("该数据不存在");
	}
}
//修改
void SqListAmend(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->size);
	ps->data[pos] = x;
}

3.test.c

#include "Sqlist.h"
void Test(SL* s)
{
	SqListInit(s);
	SqListDestroy(s);
}
int main()
{
	SL s;
	Test(&s);
	return 0;
}

有小伙伴们可能会疑惑,为什么顺序表不像通讯录一样搞一个菜单呢???

我们实现通讯录,是奔着它的功能去的,所以我们要制造菜单来使用。

但是顺序表我们只需要实现它的各个功能就行,所以只需要掌握每个操作的写法即可。


六.总结

顺序表的讲解到这里就结束啦,当然博主所分享的这个顺序表的操作都是最基本的,更多有用的操作就靠小伙伴们来自己开发啦。

最后不要忘记一键三连哦!!!

我们下期再见!!!

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

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

相关文章

便利店超市怎么做一个实用的微信小程序?

近年来&#xff0c;微信小程序商城越来越受到商家的青睐&#xff0c;因为它不仅提供了便捷的在线购物体验&#xff0c;而且不需要安装额外的应用。对于零编程经验的初学者&#xff0c;制作一个小程序商城可能会感到有些困难&#xff0c;但不用担心&#xff0c;本文将引导你一步…

Netty复习:(2)IdleStateHandler的用法

一、handler定义&#xff1a; package handler;import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter;public class MyChatServerHandler3 extends ChannelInboundHandlerAdapter {Overridepublic void userEventTriggered(…

【实用教程】MySQL内置函数

1 背景 在MySQL查询等操作过程中&#xff0c;我们需要根据实际情况&#xff0c;使用其提供的内置函数。今天我们就来一起来学习下这些函数&#xff0c;在之后的使用过程中更加得心应手。 2 MySQL函数 2.1 字符串函数 常用的函数如下&#xff1a; concat(s1,s2,…sn)字符串…

Ansible的安装和部署

目录 1.Ansible的安装 2.构建Ansible清单 直接书写受管主机名或ip 设定受管主机的组[组名称] 主机规格的范围化操作 指定其他清单文件 ansible命令指定清单的正则表达式 3.Ansible配置文件参数详解 配置文件的分类与优先级 常用配置参数 4.构建用户级Ansible操作环…

女神联盟2攻略,女神联盟2-GM红将全免

女神联盟2攻略&#xff1a;前期过渡阵容建议&#xff1a; 在女神联盟2-GM红将全免的初期玩法中&#xff0c;资源的获取是有限的&#xff0c;因此玩家需要合理运用搭配&#xff0c;避免浪费。同时在有限的资源中与敌人竞争资源需要进行头脑对决。首先&#xff0c;游戏中的硬通货…

Linux学习第27天:Platform设备驱动开发: 专注与分散

Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 专注与分散是我在题目中着重说明的一个内容。这是今天我们要学习分离与分层概念的延伸。专注是说我们要专注某层驱动的开发&#xff0c;而对于其他层则是芯片厂商…

【QT】鼠标常用事件

新建项目 加标签控件 当鼠标进去&#xff0c;显示【鼠标进入】&#xff0c;离开时显示【鼠标离开】 将QLable提升成自己的控件&#xff0c;然后再去捕获 添加文件 改继承的类名 提升类 同一个父类&#xff0c;可以提升 效果 现在代码就和Qlabel对应起来了。 在.h中声明&…

1992-2021年全国各地级市经过矫正的夜间灯光数据(GNLD、VIIRS)

1992-2021年全国各地级市经过矫正的夜间灯光数据&#xff08;GNLD、VIIRS&#xff09; 1、时间&#xff1a;1992-2021年3月&#xff0c;其中1992-2013年为年度数据&#xff0c;2013-2021年3月为月度数据 2、来源&#xff1a;DMSP、VIIRS 3、范围&#xff1a;分区域汇总&…

短期经济波动:均衡国民收入决定理论(一

宏观经济学讲义 10 短期经济波动&#xff1a;均衡国民收入决定理论(一) 文章目录 10 短期经济波动&#xff1a;均衡国民收入决定理论(一)[toc]1 均衡国民收入决定1.1 均衡国民收入决定的不同理论1.2 两部门经济&#xff1a;有效需求原理和框架1.2.1 模型假设1.2.2 模型推导1.2…

正点原子嵌入式linux驱动开发——Linux 音频驱动

音频是最常用到的功能&#xff0c;音频也是linux和安卓的重点应用场合。STM32MP1带有SAI接口&#xff0c;正点原子的STM32MP1开发板通过此接口外接了一个CS42L51音频DAC芯片&#xff0c;本章就来学习一下如何使能CS42L51驱动&#xff0c;并且CS42L51通过芯片来完成音乐播放与录…

java八股文(基础篇)

面向过程和面向对象的区别 面向过程&#xff1a;在解决问题时&#xff0c;特别自定义函数编写一步一步的步骤解决问题。 面向对象&#xff1a;其特点就是 继承&#xff0c;多态&#xff0c;继承&#xff0c;在解决问题时&#xff0c;不再注重函数的编写&#xff0c;而在于注重…

EPLAN_012#自定义导航器

关键字&#xff1a;设备导航器、端子排导航器、电缆导航器、设备列表导航器&#xff0c;树形结构&#xff08;导航器&#xff09; 正常情况下&#xff0c;eplan中的设备导航器是这个模样 如何在导航器中显示更多内容或者进行规划。&#xff08;比如下图中&#xff0c;可以显示其…

买车软件有哪些,买车软件哪个好

买车软件是指为购买汽车提供便利的手机应用程序&#xff0c;可以帮助消费者找到心仪的汽车型号、比较不同车型的价格、了解车辆的详细参数和配置、预约试驾、办理贷款、购车保险等一系列服务。 买车软件可以让用户更加便捷地了解汽车信息、比较不同车型的价格和配置、预约试驾…

【STL】:vector的模拟实现

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关vector的模拟实现&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通 数…

qt 系列(一)---qt designer设计常用操作

最近转战qt, 主要用qt designer 进行GUI开发&#xff0c;记录下实战经验~ 1.前言 qt 是跨平台C图形用户界面应用程序开发框架&#xff0c;可以使用的IDE工具有 qt creator 和 vs, 这里我主要使用 Visual Studio 2017 工具进行程序开发与编写。 2. 环境配置 只写关键步骤~~ …

Java 谈谈你对OOM的认识

文章目录 前言一、基础架构二、常见OOM1、栈内存溢出java.lang.StackOverflowError2、堆内存溢出java.lang.OutOfMemoryError&#xff1a;Java heap space3、GC回收时间过长java.lang.OutOfMemoryError: GC overhead limit exceeded4、NIO程序堆外内存溢出java.lang.OutOfMemor…

MySQL使用存储过程迁移用户表数据,过滤用户名相同名称不同的用户

存储过程简介 存储过程&#xff08;Stored Procedure&#xff09;是一组为了完成特定功能的SQL语句集&#xff0c;经编译后存储在数据库中&#xff0c;用户通过指定存储过程的名字并给定参数&#xff08;如果该存储过程带有参数&#xff09;来调用执行它。它是一段预编译的SQL…

openGauss学习笔记-110 openGauss 数据库管理-管理用户及权限-Schema

文章目录 openGauss学习笔记-110 openGauss 数据库管理-管理用户及权限-Schema110.1 创建、修改和删除Schema110.2 搜索路径 openGauss学习笔记-110 openGauss 数据库管理-管理用户及权限-Schema Schema又称作模式。通过管理Schema&#xff0c;允许多个用户使用同一数据库而不…

为什么 ConcurrentHashMap 中 key 不允许为 null

ConcurrentHashMap 在ConcurrentHashMap 的源码&#xff0c;在 put 方法里面&#xff0c;可以看到这样一段代码&#xff0c;如果 key 或者 value 为空&#xff0c;则抛出空指针异常。 但是为什么 ConcurrentHashMap 不允许 key 或者 value 为空呢&#xff1f; 原因 简单来说&…

macOS M1安装wxPython报错‘tiff.h‘ file not found的解决方法

macOS12.6.6 M1安装wxPython失败&#xff1a; 报错如下&#xff1a; imagtiff.cpp:37:14: fatal error: tiff.h file not found解决办法&#xff1a; 下载源文件重新编译&#xff08;很快&#xff0c;5分钟全部搞定&#xff09;&#xff0c;分三步走&#xff1a; 第一步&…