数据结构:顺序表

news2024/10/7 18:27:16

朋友们、伙计们,我们又见面了,本期来给大家解读一下数据结构方面有关顺序表的相关知识点,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C语言专栏:C语言:从入门到精通

数据结构专栏:数据结构

个人主页:stackY、

1.线性表 

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


线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储
 

2.顺序表

2.1概念及结构 

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表一般可以分为:

    1.静态顺序表:使用定长数组储存元素

//静态顺序表
#define N 5
typedef  int SLDataType;
typedef struct SeqList
{
	SLDataType a[N]; //定长数组 
	int Size;        //顺序表中的有效元素的个数
}SL;

   2. 动态顺序表:使用动态开辟的数组存储

//动态顺序表
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;  //指向动态开辟的数组
	int Size;       //有效元素的个数
	int Capactiy;	//容量
}SL;


 

2.2接口实现 

 静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。在动态顺序表中我们主要实现增删查改的功能,我们可以分板块来写,类似于通讯录的样式,这样方便使用,当然大家也可以在一个源文件中写,我们这里创建两个源文件:Test.cSeqList.c,再创建一个头文件:SeqList.h,这里的文件命名不做要求,表示的有意义就行。同样的我们在Test.c中实现基本逻辑,在SeqList.c中实现顺序表的细节,在SeqList.h中进行函数声明和头文件包含等,话不多说,我们直接开始:

2.2.1初始化顺序表 

在初始化通讯录里面我们需要为顺序表动态开辟一块空间,然后初始给顺序表的容量为4,默认有效元素个数是0。

头文件:SeqList.h

#include <stdio.h>
#include <stdlib.h>

//静态顺序表
//#define N 5
//typedef  int SLDataType;
//typedef struct SeqList
//{
//	SLDataType a[N]; //定长数组 
//	int Size;        //顺序表中的有效元素的个数
//}SL;

//动态顺序表
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;  //指向动态开辟的数组
	int Size;       //有效元素的个数
	int Capactiy;	//容量
}SL;

//初始化
void SLInit(SL* ps);

源文件:Test.c

#include "SeqList.h"
//   数据结构————顺序表

int main()
{
	SL s;
	//初始化
	SLInit(&s);
    return 0;
}

源文件:SeqList.c

#include "SeqList.h"

//初始化
void SLInit(SL* ps)
{
    assert(ps);
	ps->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);  //由于类型重新命名了,我们直接用即可
	if (ps->a == NULL)
	{
		perror("malloc fail:");
		return;
	}
	ps->Size = 0;
	ps->Capactiy = 4;  //默认是4个容量
}

2.2.2释放顺序表 

 释放顺序表指的就是把为顺序表动态开辟的空间释放掉,将有效的元素个数和顺序表的容量都置为0。

头文件:SeqList.h

//释放
void SLDestroy(SL* ps);

源文件:Test.c

#include "SeqList.h"
//   数据结构————顺序表

int main()
{
	SL s;
	//初始化
	SLInit(&s);
	
	//释放
	SLDestroy(&s);
	return 0;
}

源文件:SeqList.c

//释放
void SLDestroy(SL* ps)
{
    assert(ps);
	free(ps->a);   //释放
	ps->a = NULL;  //释放之后一定要置为NULL
	ps->Size = 0;  //有效元素个数置为0
	ps->Capactiy = 0; //容量置为0
}

2.2.3容量检查

容量检查就是要检查顺序表中有效元素的个数是否达到了顺序表的最大容量,如果等于最大容量,那么就表示满了、放不下了,就需要进行扩容,那怎么扩容呢?

由于我们的顺序表是由malloc开辟来的,所以可以通过realloc 来进行扩容,我们这里就规定每次扩容至原空间的2倍:

头文件:SeqList.h

//容量检测
void SLCheckCapactiy(SL* ps);

源文件:SeqList.c

//检测容量
void SLCheckCapactiy(SL* ps)
{
    assert(ps);

	if (ps->Size == ps->Capactiy) //有效元素个数等于顺序表的容量之后
	{
		SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->Capactiy * 2);
		if (tmp == NULL)
		{
			perror("realloc fail:");
			return;
		}
		ps->a = tmp;  //将扩充之后的空间再赋给原空间
		ps->Capactiy *= 2;  //容量扩容值2倍
	}
}

2.2.4从尾部插数据

 从尾部插入数据就比较简单了,首先要进行容量检查,然后就是将要插入的放在顺序表中下标为Size的位置上,放完之后需要对Size进行加一。

头文件:SeqList.h

//尾插
void SLPushBack(SL* ps, SLDataType num);

源文件:SeqList.c

//尾插
void SLPushBack(SL* ps, SLDataType num)
{
	SLCheckCapactiy(ps);
	ps->a[ps->Size] = num;  //将要插入的数据放在最后面
	ps->Size += 1;
}

2.2.5从头部插入数据 

从头部插入数据首先还是要进行容量检查,然后就是将数据放在顺序表中下标为0的位置上, 然后将数据依次向后面挪动一个位置,在这里的挪动就有说法了,是从前面的开始挪还是从后面的开始挪呢?答案是从后面的开始挪,如果你从前面开始挪,那你把你挪动的数据放在下一个位置上面是不是把它覆盖掉了,所以我们需要从后面的开始挪动:

头文件:SeqList.h

//头插
void SLPushFront(SL* ps, SLDataType num);

源文件:SeqList.c

//头插
void SLPushFront(SL* ps, SLDataType num)
{
	SLCheckCapactiy(ps);
	int end = ps->Size - 1;  //注意这里的判断条件不唯一,是根据end的值来进行判断
                             //可以进行画图来区分
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[0] = num;   //将要插入的数据放在顺序表的头部
	ps->Size++;
}

2.2.6从尾部删除数据 

 从尾部删除数据就更加简单了,首先我们得检查我们的顺序表中是否有元素,有元素才可以进行删除,没有元素删不了,所以可以将顺序表的有效元素个数进行断言,删除尾部元素最简单的方法就是直接对有效元素的个数进行减1,因为顺序表就相当于数组嘛,我们是通过数组的下标来进行访问数组,所以直接将顺序表的有效元素减一之后就访问不到最后一个元素了,这不也就达到了我们所想要的效果了嘛,当然有的老铁会想到将最后一个元素改为0,但是如果最后一个元素本来就是0呢?因此最简单有效的方法就是我们直接对顺序表的有效元素的个数减一就可以达到我们从尾部删除数据的效果。

头文件:SeqList.h

//尾删
void SLPopBack(SL* ps);

源文件:SeqList.c

//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->Size > 0);  //判断是否存在数据
	ps->Size--;
}

2.2.7从头部删除数据 

从头部删除数据就是将顺序表中下标为0的数据删掉,那我们可以直接进行覆盖,这里的覆盖也有门道了,我们是从前面开始挪呢?还是从后面开始挪呢?答案是从前面开始依次向前挪动一个位置,这里要注意,如果从后面依次开始挪动,那么最后面的数据挪到前面会将前一个数据覆盖掉 ,这样就达不到我们预期的效果了,当然了,在删除之前我们也需要进行判断顺序表中是否有数据,删除成功之后需要将我们的有效元素个数减一。

头文件:SeqList.h

//头删
void SLPopFront(SL* ps);

源文件:SeqList.c

//头删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->Size > 0);  //判断是否存在数据
	int start = 0;
	while (start < ps->Size - 1)  //判断条件还是需要进行画图来观察,切记不要越界访问
	{
		ps->a[start] = ps->a[start + 1];
		start++;
	}
	ps->Size--;
}

2.2.8 指定位置插入数据

将数据插入顺序表中下标为pos的位置上,首先呢我们需要进行容量检查,然后再判断pos的合法性,是否在0~Size之间,如果是0那么就代表头插,如果是Size就代表尾插,所以在这里我们还是需要进行挪动数据,腾出地方来让我们插入数据,所以呢,就需要将pos位置后面的数据依次挪动一个位置,这里注意需要从后面开始挪动,以防数据被覆盖掉,挪动之后将数据放在pos的位置上,然后顺序表的有效元素个数加1。

 头文件:SeqList.h

//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType num);

源文件:SeqList.c

//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType num)
{
	SLCheckCapactiy(ps); //容量检测
	assert(pos >= 0 && pos <= ps->Size);  //判断pos位置的合法性
	//挪动
	int end = ps->Size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[pos] = num;  //插入数据
	ps->Size++;
}

2.2.9 指定位置删除数据

在指定位置删除数据就直接挪动覆盖就好了,所以呢我们需要先进行pos的合法性检查,是否在0~Size之间,如果是0那么就代表头删,如果是Size就代表尾删,在这里的挪动也是需要先从前面的开始挪动,在删除成功之后就将顺序表的有效元素个数减1。

 头文件:SeqList.h

//在pos位置删除数据
void SLErase(SL* ps, int pos);

源文件:SeqList.c

//在pos位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->Size);  //判断pos位置的合法性
	int start = pos;
	while (start < ps->Size)
	{
		ps->a[start] = ps->a[start + 1];
		start++;
	}
	ps->Size--;
}

 2.2.10优化插入、删除数据 

 当我们写出了从指定位置插入或删除数据的时候,我们可以发现,当我们指定的位置是0和Size的时候就表示从顺序表的头部和尾部进行插入或删除,因此我们可以直接在头插、头删、尾插、尾删中套用SLInsert和SLErase这两个函数,在传参的时候直接传递0或Siez即可。

源文件:SeqList.c

//尾插
void SLPushBack(SL* ps, SLDataType num)
{
	
	SLInsert(ps, ps->Size - 1, num);

}


//头插
void SLPushFront(SL* ps, SLDataType num)
{
	
	SLInsert(ps, 0, num);
}

//尾删
void SLPopBack(SL* ps)
{
	
	SLErase(ps, ps->Size - 1);

}

//头删
void SLPopFront(SL* ps)
{
	
	SLErase(ps, 0);
}

//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType num)
{
	SLCheckCapactiy(ps); //容量检测
	assert(pos >= 0 && pos <= ps->Size);  //判断pos位置的合法性
	//挪动
	int end = ps->Size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[pos] = num;  //插入数据
	ps->Size++;
}

//在pos位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->Size);  //判断pos位置的合法性
	int start = pos;
	while (start < ps->Size)
	{
		ps->a[start] = ps->a[start + 1];
		start++;
	}
	ps->Size--;
}

2.2.11查找数据 

 查找顺序表中的某一个数据,如果查找成功就返回它的下标,如果查找失败就返回-1,所以我们需要遍历一下顺序表进行查找。

 头文件:SeqList.h

//查找数据
int SLFind(SL* ps, SLDataType num);

源文件:SeqList.c

//查找数据
int SLFind(SL* ps, SLDataType num)
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->Size; i++)
	{
		if (ps->a[i] == num)
		{
			return i;
		}
	}
	return -1;
}

2.2.12修改数据 

修改数据就更加简单了,直接将pos位置的数据进行赋值你所要改的值,所以先要进行对pos的合法性检查,然后再修改。

 头文件:SeqList.h

//修改数据
void SLModify(SL* ps, int pos, SLDataType num);

源文件:SeqList.c

//修改数据
void SLModify(SL* ps, int pos, SLDataType num)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->Size);
	ps->a[pos] = num;
}

2.2.13打印顺序表 

打印顺序表和打印数组一模一样,通过有效元素个数来控制打印次数。

头文件:SeqList.h

//打印
void SLPrint(SL* ps);

源文件:SeqList.c

//打印
void SLPrint(SL* ps)
{
	assert(ps);

	int i = 0;
	for (i = 0; i < ps->Size; i++)
	{
		printf("%d ", *(ps->a + i));
	}
	printf("\n");
}

2.2.14未优化的完整代码

这里指的是没有优化插入和删除数据的代码:

源文件:Test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "SeqList.h"
//   数据结构————顺序表

int main()
{
	SL s;
	//初始化
	SLInit(&s);
	
	//在这里可以加上增添、查找、删除、修改等函数的调用
	//...
	//...
	//...
	
	//打印
	SLPrint(&s);
	//释放
	SLDestroy(&s);
	return 0;
}

 头文件:SeqList.h

#pragma once


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


//静态顺序表
//#define N 5
//typedef  int SLDataType;
//typedef struct SeqList
//{
//	SLDataType a[N]; //定长数组 
//	int Size;        //顺序表中的有效元素的个数
//}SL;

//动态顺序表
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;  //指向动态开辟的数组
	int Size;       //有效元素的个数
	int Capactiy;	//容量
}SL;

//初始化
void SLInit(SL* ps);
//释放
void SLDestroy(SL* ps);
//容量检测
void SLCheckCapactiy(SL* ps);


//尾插
void SLPushBack(SL* ps, SLDataType num);
//头插
void SLPushFront(SL* ps, SLDataType num);
//尾删
void SLPopBack(SL* ps);
//头删
void SLPopFront(SL* ps);

//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType num);

//在pos位置删除数据
void SLErase(SL* ps, int pos);

//查找数据
int SLFind(SL* ps, SLDataType num);

//修改数据
void SLModify(SL* ps, int pos, SLDataType num);

//打印
void SLPrint(SL* ps);

源文件:SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"

//初始化
void SLInit(SL* ps)
{
	assert(ps);

	ps->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);  //由于类型重新命名了,我们直接用即可
	if (ps->a == NULL)
	{
		perror("malloc fail:");
		return;
	}
	ps->Size = 0;
	ps->Capactiy = 4;  //默认是4个容量
}

//释放
void SLDestroy(SL* ps)
{
	assert(ps);

	free(ps->a);   //释放
	ps->a = NULL;  //释放之后一定要置为NULL
	ps->Size = 0;  //有效元素个数置为0
	ps->Capactiy = 0; //容量置为0
}


//检测容量
void SLCheckCapactiy(SL* ps)
{
	assert(ps);
	if (ps->Size == ps->Capactiy) //有效元素个数等于顺序表的容量之后
	{
		SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->Capactiy * 2);
		if (tmp == NULL)
		{
			perror("realloc fail:");
			return;
		}
		ps->a = tmp;  //将扩充之后的空间再赋给原空间
		ps->Capactiy *= 2;  //容量扩容值2倍
	}
}

//尾插
void SLPushBack(SL* ps, SLDataType num)
{
	SLCheckCapactiy(ps);
	ps->a[ps->Size] = num;
	ps->Size += 1;

}


//头插
void SLPushFront(SL* ps, SLDataType num)
{
	SLCheckCapactiy(ps);
	int end = ps->Size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[0] = num;
	ps->Size++;
}

//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->Size > 0);  //判断是否存在数据
	ps->Size--;

}

//头删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->Size > 0);  //判断是否存在数据
	int start = 0;
	while (start < ps->Size - 1)
	{
		ps->a[start] = ps->a[start + 1];
		start++;
	}
	ps->Size--;
}

//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType num)
{
	SLCheckCapactiy(ps); //容量检测
	assert(pos >= 0 && pos <= ps->Size);  //判断pos位置的合法性
	//挪动
	int end = ps->Size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[pos] = num;  //插入数据
	ps->Size++;
}

//在pos位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->Size);  //判断pos位置的合法性
	int start = pos;
	while (start < ps->Size)
	{
		ps->a[start] = ps->a[start + 1];
		start++;
	}
	ps->Size--;
}

//查找数据
int SLFind(SL* ps, SLDataType num)
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->Size; i++)
	{
		if (ps->a[i] == num)
		{
			return i;
		}
	}
	return -1;
}

//修改数据
void SLModify(SL* ps, int pos, SLDataType num)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->Size);
	ps->a[pos] = num;
}

//打印
void SLPrint(SL* ps)
{
	assert(ps);

	int i = 0;
	for (i = 0; i < ps->Size; i++)
	{
		printf("%d ", *(ps->a + i));
	}
	printf("\n");
}

2.2.15优化过后的完整代码 

 这里是优化过插入和删除数据的代码:

源文件:Test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "SeqList.h"
//   数据结构————顺序表

int main()
{
	SL s;
	//初始化
	SLInit(&s);
	
	//在这里可以加上增添、查找、删除、修改等函数的调用
	//...
	//...
	//...
	
	//打印
	SLPrint(&s);
	//释放
	SLDestroy(&s);
	return 0;
}

 头文件:SeqList.h

#pragma once


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


//静态顺序表
//#define N 5
//typedef  int SLDataType;
//typedef struct SeqList
//{
//	SLDataType a[N]; //定长数组 
//	int Size;        //顺序表中的有效元素的个数
//}SL;

//动态顺序表
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;  //指向动态开辟的数组
	int Size;       //有效元素的个数
	int Capactiy;	//容量
}SL;

//初始化
void SLInit(SL* ps);
//释放
void SLDestroy(SL* ps);
//容量检测
void SLCheckCapactiy(SL* ps);


//尾插
void SLPushBack(SL* ps, SLDataType num);
//头插
void SLPushFront(SL* ps, SLDataType num);
//尾删
void SLPopBack(SL* ps);
//头删
void SLPopFront(SL* ps);

//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType num);

//在pos位置删除数据
void SLErase(SL* ps, int pos);

//查找数据
int SLFind(SL* ps, SLDataType num);

//修改数据
void SLModify(SL* ps, int pos, SLDataType num);

//打印
void SLPrint(SL* ps);

源文件:SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"

//初始化
void SLInit(SL* ps)
{
	assert(ps);

	ps->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);  //由于类型重新命名了,我们直接用即可
	if (ps->a == NULL)
	{
		perror("malloc fail:");
		return;
	}
	ps->Size = 0;
	ps->Capactiy = 4;  //默认是4个容量
}

//释放
void SLDestroy(SL* ps)
{
	assert(ps);

	free(ps->a);   //释放
	ps->a = NULL;  //释放之后一定要置为NULL
	ps->Size = 0;  //有效元素个数置为0
	ps->Capactiy = 0; //容量置为0
}


//检测容量
void SLCheckCapactiy(SL* ps)
{
	assert(ps);
	if (ps->Size == ps->Capactiy) //有效元素个数等于顺序表的容量之后
	{
		SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->Capactiy * 2);
		if (tmp == NULL)
		{
			perror("realloc fail:");
			return;
		}
		ps->a = tmp;  //将扩充之后的空间再赋给原空间
		ps->Capactiy *= 2;  //容量扩容值2倍
	}
}

//尾插
void SLPushBack(SL* ps, SLDataType num)
{
	SLInsert(ps, ps->Size - 1, num);
}


//头插
void SLPushFront(SL* ps, SLDataType num)
{
	SLInsert(ps, 0, num);
}

//尾删
void SLPopBack(SL* ps)
{
	SLErase(ps, ps->Size - 1);
}

//头删
void SLPopFront(SL* ps)
{
	SLErase(ps, 0);
}

//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType num)
{
	SLCheckCapactiy(ps); //容量检测
	assert(pos >= 0 && pos <= ps->Size);  //判断pos位置的合法性
	//挪动
	int end = ps->Size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	ps->a[pos] = num;  //插入数据
	ps->Size++;
}

//在pos位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0 && pos <= ps->Size);  //判断pos位置的合法性
	int start = pos;
	while (start < ps->Size)
	{
		ps->a[start] = ps->a[start + 1];
		start++;
	}
	ps->Size--;
}

//查找数据
int SLFind(SL* ps, SLDataType num)
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->Size; i++)
	{
		if (ps->a[i] == num)
		{
			return i;
		}
	}
	return -1;
}

//修改数据
void SLModify(SL* ps, int pos, SLDataType num)
{
	assert(ps);
	assert(pos >= 0 && pos < ps->Size);
	ps->a[pos] = num;
}

//打印
void SLPrint(SL* ps)
{
	assert(ps);

	int i = 0;
	for (i = 0; i < ps->Size; i++)
	{
		printf("%d ", *(ps->a + i));
	}
	printf("\n");
}

注意:

1.这些函数在这里就不使用案例来测试了,大家有兴趣可以自己动手来敲一敲,熟能生巧!

2.在顺序表的一些操作中是需要通过画图来判断它的限制条件,一定要画图!一定要画图!一定要画图! 

2.3顺序表的问题及思考

问题:
1. 中间/头部的插入删除,时间复杂度为O(N)。


2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。


3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

思考:

如何解决这些问题呢?这时就需要用到我们的链表啦,关于链表的知识我们下期再聊。

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!

 

 

 

 

 

 

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

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

相关文章

阿里的Leader为什么牛逼?秘密都在“三板斧”里...

许多人都觉得&#xff0c;啊里出来的Leader&#xff0c;做事情都很有方法、有套路、有结果&#xff0c;秘密究竟在哪里&#xff1f;其实一个人的牛逼&#xff0c;首先是方法论的牛逼。本文就来聊聊&#xff0c;阿里Leader们都要学习的管理方法论&#xff0c;俗称阿里“三板斧”…

《MySQL系列-InnoDB引擎37》索引与算法-全文检索

全文检索 1 概述 对于B树的特点&#xff0c;可以通过索引字段的前缀进行查找。例如如下的查询方式是支持B树索引的,只要name字段添加了B树索引&#xff0c;就可以利用索引快速查找以XXX开头的名称。 select * from table where name like XXX%; 而如下这种情况不适合私有B索…

BUUCTF-sql注入联合查询的创建虚拟表-词频-steghide的使用

第七周第三次 目录 WEB [GXYCTF2019]BabySQli [GXYCTF2019]BabyUpload Crypto 世上无难事 old-fashion ​Misc 面具下的flag 九连环 WEB [GXYCTF2019]BabySQli 这是一道很新的题目 我们打开环境 发现登入注册界面 先看看源码有没有提示 发现有一个 php文件 进入…

Spark 对hadoopnamenode-log文件进行数据清洗并存入mysql数据库

一.查找需要清洗的文件 1.1查看hadoopnamenode-log文件位置 1.2 开启Hadoop集群和Hive元数据、Hive远程连接 具体如何开启可以看我之前的文章&#xff1a;(10条消息) SparkSQL-liunx系统Spark连接Hive_难以言喻wyy的博客-CSDN博客 1.3 将这个文件传入到hdfs中&#xff1a; hd…

OpenAI Translator | 基于ChatGPT API全局翻译润色解析及ORC上传图像翻译插件

简介 OpenAI Translator&#xff0c;一款基于 ChatGPT API 的划词翻译的浏览器插件和跨平台桌面端应用&#xff0c;使用 ChatGPT API 进行划词翻译和文本润色&#xff0c;借助了 ChatGPT 强大的翻译能力&#xff0c;帮助用户更流畅地阅读外语和编辑外语&#xff0c;允许跨 55 …

Qt音视频开发35-左右通道音量计算和音量不同范围值的转换

一、前言 视频文件一般会有两个声音通道及左右声道&#xff0c;值有时候一样有时候不一样&#xff0c;很多场景下我们需要对其分开计算不同的音量值&#xff0c;在QAudioFormat中可以获取具体有几个通道&#xff0c;如果是一个通道&#xff0c;则左右通道值设定一样&#xff0…

【时序数据库】时间序列数据和MongoDB第三部分-查询、分析和呈现时间序列数据...

在《时间序列数据和MongoDB:第1部分-简介》「时序数据库」时间序列数据与MongoDB&#xff1a;第一部分-简介中&#xff0c;我们回顾了理解数据库的查询访问模式需要询问的关键问题。在《时间序列数据和MongoDB:第2部分-模式设计最佳实践》「时序数据库」时序数据库和MongoDB第二…

Java实验课的学习笔记(二)类的简单使用

本文章就讲的是很基础的类的使用 重点大概就是类的构造函数以及一些很基础的东西。 实验内容是些老生常谈的东西&#xff0c;Complex类&#xff0c;在当初学C面向对象的时候也是这个样子展开的。 内容如以下&#xff1a; public class Complex {float real;float imag;public…

APK瘦身

先看下APK打包流程&#xff1a;APK打包流程_贺兰猪的博客-CSDN博客 知道了APK打包流程后想要瘦身&#xff0c;其实无非就是把整个APK的一些文件进行一个瘦身。 看下apk的这个文件。包括class、资源&#xff0c;资源生成arsc(资源映射表)&#xff0c;manifest清单&#xff0c;…

快排(非递归)及计数排序算法

都学了递归版的快速排序为何还要再学非递归实现&#xff1f;由于在递归过程中&#xff0c;如果数据量过大&#xff0c;那么实现时容易导致栈溢出&#xff0c;虽然代码没有问题&#xff0c;但是就是会崩&#xff0c;因此要将其改为非递归来实现 文章目录一、快速排序&#xff08…

如何使用Mac远程控制Windows电脑?

在你开始之前&#xff0c;设置您要远程处理的 Windows 计算机。 先安装 Microsoft Remote Desktop。 您可以在“应用程序”文件夹中检查它。 如果在个人计算机上安装&#xff0c;请转到 Apple App Store 并下载 Microsoft Remote Desktop。 如果在 TXST 计算机上安装&#xff0…

【C语言】递归解决经典题目(汉诺塔问题、青蛙跳台阶问题)

简单不先于复杂&#xff0c;而是在复杂之后。 目录 1. 汉诺塔问题 1.1 简介及思路 1.2 代码实现 2. 青蛙跳台阶问题 2.1 简介及思路 2.2 代码实现 1. 汉诺塔问题 1.1 简介及思路 汉诺塔问题是一种经典的递归问题&#xff0c;起源于印度传说中的塔 of Brahma。问题描…

手把手教你学习IEC104协议和编程实现 十 故障事件与复位进程

故障事件 目的 在IEC104普遍应用之前,据我了解多个协议,再综合自动化协议中,有这么一个概念叫“事故追忆”,意思是当变电站出现事故的时候,不但要记录事故的时间,还需记录事故前后模拟量的数据,从而能从一定程度上分析事故产生的原因,这个模拟量就是和今天讲解的故障…

silvaco 仿真BJT

本次实验为利用silvaco仿真BJT器件&#xff0c;分析不同p区厚度以及p区不同掺杂浓度研究其电流增益的变化。 一、器件要求 区域 掺杂方式 掺杂浓度或 峰值浓度&#xff08;/cm3&#xff09; 厚度&#xff08;um&#xff09; 宽度&#xff08;um&#xff09; N-漂移区 均匀…

微服务框架【笔记-Nacos注册中心】

接上篇&#xff0c;继续学习微服务框架中的Nacos注册中心。 Nacos注册中心 一、认识和安装Nacos 1.认识Nacos Nacos 是阿里巴巴的产品&#xff0c;现在是 SpringCloud 中的一个组件。相比 Eureka 功能更加丰富。 2.安装Nacos 下面给大家展示windows安装Nacos步骤&#xff1a;…

网络互联技术与实践教程(汪双硕、姚羽)——第四章 路由技术

第四章 路由技术 4.1 路由原理 路由是指通过相互连接的网络将数据从源地点转发到目标地点的过程。在路由过程中&#xff0c;数据通常会经过一个或多个中间节点&#xff0c;路由发生在网络层。路由包含两个主要的动作&#xff1a;确定最佳路径和通过网络传输信息&#xff0c;后…

刷题笔记【6】| 快速刷完67道剑指offer(Java版)

本文已收录于专栏&#x1f33b;《刷题笔记》文章目录前言&#x1f3a8; 1、包含min函数的栈题目描述思路&#xff08;双栈法&#xff09;&#x1f3a8; 2、栈的压入弹出序列题目描述思路&#xff08;辅助栈&#xff09;&#x1f3a8; 3、从上往下打印二叉树题目描述思路&#x…

chapter-4-数据库语句

以下课程来源于MOOC学习—原课程请见&#xff1a;数据库原理与应用 考研复习 概述 SQL发展 注&#xff1a;关键词是哪些功能&#xff0c;尤其第一个create alter drop是定义功能 1.SQL功能强大&#xff0c;实现了数据定义、数据操纵、数据控制等功能 2.SQL语言简洁&#xff…

【Java版oj】day26跳台阶扩展问题、快到碗里来

目录 一、跳台阶扩展问题 &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码 二、快到碗里来 &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码 一、跳台…

tomcat配置虚拟路径映射磁盘文件列表图片回显和放大功能实现springboot项目的文件虚拟路径映射

tomcat映射磁盘图片1.以E盘为例&#xff0c;在E盘创建目录testReceive2.配置tomcat虚拟路径映射e盘本地文件3.代码层面创建上传文件&#xff08;此处为图片&#xff09;工具类3.1&#xff08;校验图片格式、获取当前主机ip、上传图片至本机目的地&#xff0c;获取上传图片地址&…