数据结构从入门到精通——顺序表

news2024/11/16 10:14:38

顺序表

  • 前言
  • 一、线性表
  • 二、顺序表
    • 2.1概念及结构
    • 2.2 接口实现
    • 2.3 数组相关面试题
    • 2.4 顺序表的问题及思考
  • 三、顺序表具体实现代码
    • 顺序表的初始化
    • 顺序表的销毁
    • 顺序表的打印
    • 顺序表的增容
    • 顺序表的头部/尾部插入
    • 顺序表的头部/尾部删除
    • 指定位置之前插入数据和删除指定位置数据
    • 顺序表元素的查找
  • 四、顺序表完整代码
    • text.h
    • text.c
    • main.c


前言

顺序表是一种常见的线性数据结构,它使用一段连续的存储单元依次存储数据元素。这种数据结构的特点是逻辑上相邻的元素在物理存储位置上也相邻,因此可以快速地访问表中的任意元素。

顺序表的实现通常依赖于数组,数组是一种静态的数据结构,一旦创建,其大小就是固定的。这意味着在顺序表中插入或删除元素可能会导致空间的浪费或不足。例如,如果在一个已经满了的顺序表中插入一个新元素,就需要重新分配更大的数组空间,并将原有元素复制到新数组中,这是一个相对耗时的操作。

然而,顺序表在访问元素时具有很高的效率。由于元素在内存中是连续存储的,计算机可以直接通过计算偏移量来访问任意位置的元素,这种访问方式的时间复杂度为O(1)。相比之下,链表等动态数据结构在访问元素时可能需要遍历多个节点,效率较低。

顺序表还支持快速的元素查找。通过索引,我们可以在常数时间内找到表中的任意元素。这种特性使得顺序表在处理需要频繁查找操作的场景时表现出色。

总的来说,顺序表是一种高效、简单的数据结构,适用于需要快速访问和查找元素的场景。然而,它的固定大小特性也限制了其在需要频繁插入和删除操作的场景中的应用。在实际应用中,我们需要根据具体的需求和场景来选择合适的数据结构。


一、线性表

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

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

在这里插入图片描述

二、顺序表

2.1概念及结构

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

顺序表一般可以分为:

  1. 静态顺序表:使用定长数组存储元素。
    在这里插入图片描述
  2. 动态顺序表:使用动态开辟的数组存储。
    在这里插入图片描述

2.2 接口实现

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间大小,所以下面我们实现动态顺序表。

typedef int SLDataType;
// 顺序表的动态存储
typedef struct SeqList
{
  	SLDataType* array;  // 指向动态开辟的数组
  	size_t size ;       // 有效数据个数
  	size_t capicity ;   // 容量空间的大小
}SeqList;
// 基本增删查改接口
// 顺序表初始化
void SeqListInit(SeqList* psl);
// 检查空间,如果满了,进行增容
void CheckCapacity(SeqList* psl);
// 顺序表尾插
void SeqListPushBack(SeqList* psl, SLDataType x);
// 顺序表尾删
void SeqListPopBack(SeqList* psl);
// 顺序表头插
void SeqListPushFront(SeqList* psl, SLDataType x);
// 顺序表头删
void SeqListPopFront(SeqList* psl);
// 顺序表查找
int SeqListFind(SeqList* psl, SLDataType x); 
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* psl, size_t pos, SLDataType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* psl, size_t pos);
// 顺序表销毁
void SeqListDestory(SeqList* psl);
// 顺序表打印
void SeqListPrint(SeqList* psl);

2.3 数组相关面试题

  1. 原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)
  2. 删除排序数组中的重复项
  3. 合并两个有序数组

2.4 顺序表的问题及思考

问题:

  1. 中间/头部的插入删除,时间复杂度为O(N)
  2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
  3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容到
    200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空间。

思考:如何解决以上问题呢?

三、顺序表具体实现代码

顺序表的初始化

void SLInit(SL* p);//顺序表的初始化
void SLInit(SL* p)
{
	p->arr = NULL;//动态开辟数组的地址置为空指针
	p->capacity = 0;//数组容量置为0
	p->size = 0;//数组下标置为0或-1,具体看你需要哪一种下标
}

顺序表的初始化是数据结构学习中不可或缺的一步,它指的是为一个预先分配了固定大小内存空间的线性表分配存储空间,并设置其初始状态。在顺序表中,数据元素在内存中是连续存放的,这种存储方式使得我们可以通过下标直接访问任何位置的元素,从而实现了快速的元素访问。

顺序表的初始化通常包括分配内存空间、设定初始容量、以及可能的初始化所有元素为某一默认值。初始化操作的正确与否直接关系到后续数据操作的效率与正确性。

以C语言为例,顺序表可以用数组来实现。在初始化时,我们通常会定义一个结构体来表示顺序表,这个结构体包含数组本身、数组的大小(即当前已存储的元素数量)以及数组的最大容量(即预先分配的内存空间大小)。

顺序表的销毁

void SLDestroy(SL* p);//顺序表的销毁
void SLDestroy(SL* p)
{
	assert(p);
	int i = 0; 
	free(p->arr);
	SLInit(p);
}

在数据结构和算法的世界里,顺序表作为一种基本的线性表结构,承载着数据的存储和访问功能。然而,正如任何资源的生命周期一样,顺序表的使用也需要在结束时进行适当的销毁和清理,以确保内存的有效利用和系统的稳定运行。

顺序表的销毁,主要涉及到内存的释放。当一个顺序表不再需要使用时,我们必须将其所占用的内存空间归还给操作系统,避免内存泄漏。内存泄漏是程序运行过程中未能正确释放不再使用的内存空间,长时间累积下来会导致可用内存空间减少,最终可能导致程序运行缓慢甚至崩溃。

销毁顺序表的过程通常包括以下步骤:

  1. 遍历顺序表,释放其中存储的每一个数据元素。如果数据元素是指针类型,需要确保这些指针指向的内存也被正确释放。

  2. 释放顺序表本身所占用的内存空间。这通常通过调用相应的内存释放函数(如C语言中的free()函数)来实现。

  3. 将顺序表的头指针或引用设置为nullnullptr,表示该顺序表已经不再有效,防止后续代码错误地访问或操作已销毁的顺序表。

  4. 在某些情况下,可能还需要进行额外的清理工作,如关闭与顺序表相关的文件、释放其他相关资源等。

通过以上步骤,我们可以确保顺序表在不再使用时能够被正确地销毁,从而保持程序的内存安全和稳定运行。在实际编程中,我们应该始终遵循资源管理的最佳实践,确保在适当的时候释放不再需要的资源,避免内存泄漏和其他潜在问题。

顺序表的打印

void SLprint(SL p);//顺序表的打印
void SLprint(SL p)
{
	for (int i = 0; i < p.size; i++)
	{
		printf("%5d", p.arr[i]);
	}
	printf("\n");
}

顺序表的打印通常指的是将顺序表中的所有元素按照一定的格式输出到控制台或其他输出设备上。这个过程通常涉及到遍历顺序表中的所有元素,并将它们转换为人类可读的格式。在打印顺序表时,我们通常会选择一种易于阅读和理解的方式,如按照元素在表中的顺序依次打印,或者使用特定的分隔符将不同的元素分隔开。

顺序表的增容

在这里插入图片描述

void SLCheckCapacity(SL* ps);//顺序表的增容
void SLCheckCapacity(SL* p)
{
	SL* a;
	assert(p);
	int Datacapaity = 0;
	if (p->size == p->capacity)
	{
		Datacapaity = 0 ? 4 : 2 * p->capacity;//三目运算符
		a = (DataType*)realloc(p->arr, Datacapaity * sizeof(DataType));//使得扩容按照2倍增长
		assert(a);
		p->arr = a;
	}
	p->capacity = Datacapaity;
}

顺序表是一种线性数据结构,它使用一段连续的存储空间来存储元素。当顺序表中的元素数量达到其当前容量上限时,就需要进行增容操作,以确保可以继续添加新的元素。

增容操作的核心思想是为顺序表分配更多的连续存储空间,并将原有的元素复制到新的存储空间中。这通常涉及到两个主要步骤:分配新的存储空间和元素迁移。

分配新的存储空间是增容操作的第一步。顺序表会根据其当前容量和扩容因子来计算出新的容量。扩容因子是一个大于1的常数,用于确定每次增容时应该增加多少存储空间。例如,如果扩容因子为2,那么每次增容时新的容量将是原容量的两倍。

在分配了新的存储空间之后,接下来就是元素迁移的步骤。这一步将顺序表中原有的元素从旧的存储空间复制到新的存储空间中。为了保证数据的完整性和正确性,复制过程必须小心谨慎地进行。通常,复制过程会从顺序表的第一个元素开始,逐个复制到新的存储空间的相应位置,直到所有元素都被复制完毕。

完成元素迁移后,顺序表就可以继续使用新的存储空间来存储新的元素了。同时,顺序表的容量上限也得到了提升,可以容纳更多的元素。

需要注意的是,增容操作虽然可以扩展顺序表的容量,但也会带来一定的性能开销。因为每次增容都需要分配新的存储空间并进行元素迁移,这些操作都需要消耗一定的时间和资源。因此,在设计和实现顺序表时,需要权衡扩容的时机和频率,以避免频繁的增容操作对性能产生负面影响。

为了优化性能,可以在顺序表的使用过程中采取一些策略来减少增容的次数。例如,可以在添加元素之前先检查顺序表的容量是否足够,如果不足够则提前进行增容操作,以避免在添加元素时触发频繁的增容。此外,还可以根据实际应用场景和数据特点来选择合适的扩容因子,以平衡存储空间的利用率和增容操作的开销。

顺序表的头部/尾部插入

在这里插入图片描述

//顺序表的头部/尾部插入
void SLPushBack(SL* p, DataType x);
void SLPushFront(SL* p, DataType x);
void SLPushBack(SL* p, DataType x)//顺序表的尾插
{
	assert(p);
	SLCheckCapacity(p);//增容
	p->arr[p->size++] = x;
}

void SLPushFront(SL* p, DataType x)//顺序表的头插
{
	assert(p);
	SLCheckCapacity(p);//增容
	for (int i = p->size; i > 0; i--)
	{
		p->arr[i] = p->arr[i - 1];
	}
	p->arr[0] = x;
	p->size++;
}

顺序表的头部/尾部插入是顺序表操作中的基本功能,它们直接影响了顺序表的数据存储和访问效率。顺序表,又称数组列表,是一种线性表的数据结构,其特点是元素在内存中是连续存储的。这种存储方式使得顺序表在访问元素时具有很高的效率,因为可以通过下标直接定位到元素的位置。然而,在插入或删除元素时,顺序表的表现就不如链表等其他数据结构了。

在顺序表的头部插入元素时,需要将所有已存在的元素向后移动一位,以腾出空间给新插入的元素。这种操作的时间复杂度是O(n),其中n是顺序表的长度。因此,对于频繁在头部插入元素的场景,顺序表并不是最优的数据结构选择。

相比之下,在顺序表的尾部插入元素则相对高效。因为尾部是顺序表的一个“开放”端,新元素可以直接添加到这个位置,而不需要移动其他元素。这种操作的时间复杂度是O(1),即常数时间复杂度,意味着无论顺序表有多大,尾部插入的效率都是相同的。

在实际应用中,顺序表的头部/尾部插入操作常常用于实现各种算法和数据结构。例如,在某些需要动态维护数据集合的场景中,我们可以使用顺序表来存储数据,并根据需要在头部或尾部进行插入操作。此外,顺序表还可以用于实现栈(Stack)和队列(Queue)等数据结构,其中栈通常使用顺序表的头部进行插入和删除操作,而队列则使用头部进行删除操作,使用尾部进行插入操作。

需要注意的是,虽然顺序表在访问元素时具有很高的效率,但在插入和删除元素时可能会遇到性能瓶颈。因此,在选择数据结构时,我们需要根据具体的应用场景和需求来权衡各种因素,以选择最适合的数据结构。

顺序表的头部/尾部删除

在这里插入图片描述

在这里插入图片描述

//顺序表的头部/尾部删除
void SLPopBack(SL* p);
void SLPopFront(SL* p);
void SLPopBack(SL* p)
{
	assert(p);
	assert(p->size);
	p->size--;
}
void SLPopFront(SL* p)
{
	assert(p);
	assert(p->size);
	for (int i = 0; i < p->size - 1; i++)
	{
		p->arr[i] = p->arr[i + 1];
	}
	p->size--;
}

顺序表的头部/尾部删除是数据结构中常见的操作,它们分别涉及到对顺序表的首个元素和最后一个元素的移除。这两种操作在多种应用场景中都具有重要的意义,比如缓存管理、消息队列、数据库索引等。

在顺序表(数组)中实现头部删除操作,通常需要将数组中的所有元素向前移动一位,以填补被删除元素的位置。这种操作的时间复杂度为O(n),其中n为数组的长度。如果顺序表是动态分配的,并且有足够的空间,也可以采用标记删除的方式,即简单地将头部元素标记为已删除,而不实际移动其他元素。但这种方法需要额外的逻辑来跟踪哪些元素是已删除的,因此可能会增加空间和时间上的开销。

相比之下,尾部删除操作通常更为简单和高效。在顺序表中,尾部元素总是位于数组的最后一个位置,因此删除它不需要移动其他元素。只需将数组的最后一个元素的位置标记为未使用,或者如果使用的是动态数组,可以减少其容量以释放未使用的空间。这种操作的时间复杂度通常为O(1)。

然而,需要注意的是,虽然尾部删除在单个操作上可能更快,但在频繁进行头部删除的情况下,顺序表可能不是最优的数据结构选择。在这种情况下,使用链表可能更为合适,因为链表在头部和尾部删除操作上的时间复杂度都是O(1)。

指定位置之前插入数据和删除指定位置数据

//指定位置之前插入数据
//删除指定位置数据
void SLInsert(SL* p, int pos, DataType x);
void SLErase(SL* p, int pos);
void SLInsert(SL* p, int pos, DataType x)
{
	assert(p);
	assert(pos>=0 && pos <= p->size);
	SLCheckCapacity(p);
	for (int i = p->size; i >= pos; i--)
	{
		p->arr[i] = p->arr[i - 1];
	}
	p->arr[pos] = x;
	p->size++;
}
void SLErase(SL* p, int pos)
{
	assert(p);
	assert(pos >= 0 && pos <= p->size);
	for (int i = pos - 1; i < p->size; i++)
	{
		p->arr[i] = p->arr[i + 1];
	}
	p->size--;
}

指定位置之前插入数据和删除指定位置数据是编程中常见的操作,尤其在处理列表、数组、字符串等数据结构时。这些操作不仅对于数据的管理和维护至关重要,而且在很多算法和程序设计中也扮演着重要角色。

在插入数据时,我们通常需要考虑以下几个因素:插入的位置、要插入的数据以及插入后数据结构的完整性。

在删除指定位置的数据时,我们需要确保删除操作不会破坏数据结构的其余部分。
需要注意的是,在进行插入和删除操作时,我们需要确保索引的有效性。如果索引超出数据结构的范围,那么程序会抛出异常。因此,在实际编程中,我们通常需要在进行这些操作之前先检查索引的有效性。

此外,插入和删除操作的时间复杂度也是我们需要考虑的因素。在大多数数据结构中,插入和删除操作的时间复杂度都是O(n),其中n是数据结构的长度。这意味着,随着数据结构的增长,这些操作所需的时间也会增加。因此,在选择数据结构时,我们需要根据实际需求来权衡各种因素,包括插入和删除操作的频率、数据结构的长度以及所需的空间等。

总的来说,指定位置之前插入数据和删除指定位置数据是编程中常见的操作,我们需要熟练掌握这些操作的方法和技巧,以便在实际编程中能够灵活应用。同时,我们还需要注意操作的有效性和时间复杂度等问题,以确保程序的正确性和效率。

顺序表元素的查找

//顺序表元素的查找
int SLFind(SL p, DataType x);
int SLFind(SL* p, DataType x)
{
	assert(p);
	for (int i = 0; i < p->size; i++)
	{
		if (p->arr[i] == x)return i + 1;
	}
	return -1;
}

顺序表元素的查找是计算机科学中的一个基本问题,它涉及到在一个有序或无序的列表中查找特定的元素。在各种实际应用中,如数据库管理、搜索引擎、编程语言中的数据结构等,顺序表元素的查找都扮演着重要的角色。

对于顺序表元素的查找,最基础的算法是线性查找。线性查找的思想是从表的第一个元素开始,逐个比较每个元素,直到找到目标元素或遍历完整个表。这种查找方法的时间复杂度为O(n),其中n为表的长度。虽然线性查找简单易懂,但在处理大数据量时,其效率往往不能满足要求。

为了提高查找效率,人们设计了更为高效的查找算法,如二分查找。二分查找只适用于有序的顺序表,其基本思想是将表分为两半,通过比较目标元素与中间元素的大小,决定在哪一半继续查找,如此反复,直到找到目标元素或确定目标元素不存在于表中。二分查找的时间复杂度为O(log n),在数据量较大时,其效率远高于线性查找。

除了二分查找,还有一些其他的查找算法,如插值查找和斐波那契查找等。插值查找是对二分查找的一种改进,它根据目标元素在表中的大致位置,选择一个更接近于目标元素的中间元素进行比较,从而减少比较次数。斐波那契查找则是利用斐波那契数列的性质,通过计算斐波那契数来确定查找范围,进一步提高查找效率。

在实际应用中,选择哪种查找算法取决于具体的需求和场景。对于有序的顺序表,二分查找、插值查找和斐波那契查找等高效算法是更好的选择;而对于无序的顺序表,线性查找可能是唯一可行的选择。此外,还可以根据表的大小、元素的分布等因素来选择合适的查找算法。

四、顺序表完整代码

text.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef int DataType;

静态顺序表
//typedef struct Seqlist
//{
//	DataType data[10];
//	int size;
//}Seqlist;

//动态顺序表
typedef struct Seqlist
{
	DataType* arr;
	int size;
	int capacity;
}SL;

void SLCheckCapacity(SL* ps);//顺序表的增容

void SLInit(SL* p);//顺序表的初始化
void SLDestroy(SL* p);//顺序表的销毁
void SLprint(SL p);//顺序表的打印

//顺序表的头部/尾部插入
void SLPushBack(SL* p, DataType x);
void SLPushFront(SL* p, DataType x);

//顺序表的头部/尾部删除
void SLPopBack(SL* p);
void SLPopFront(SL* p);

//指定位置之前插入数据
//删除指定位置数据
void SLInsert(SL* p, int pos, DataType x);
void SLErase(SL* p, int pos);
int SLFind(SL p, DataType x);

text.c

#include"text.h"
void SLCheckCapacity(SL* p)
{
	SL* a;
	assert(p);
	int Datacapaity = 0;
	if (p->size == p->capacity)
	{
		Datacapaity = 0 ? 4 : 2 * p->capacity;
		a = (DataType*)realloc(p->arr, Datacapaity * sizeof(DataType));
		assert(a);
		p->arr = a;
	}
	p->capacity = Datacapaity;
}

void SLInit(SL* p)
{
	p->arr = NULL;
	p->capacity = 0;
	p->size = 0;
}
void SLDestroy(SL* p)
{
	int i = 0; 
	for (i = 0; i < p->size; i++)
	{
		free(p->arr[i]);
	}
	SLInit(p);
}
void SLprint(SL p)
{
	for (int i = 0; i < p.size; i++)
	{
		printf("%5d", p.arr[i]);
	}
	printf("\n");
}

void SLPushBack(SL* p, DataType x)
{
	assert(p);
	SLCheckCapacity(p);
	p->arr[p->size++] = x;
}

void SLPushFront(SL* p, DataType x)
{
	assert(p);
	SLCheckCapacity(p);
	for (int i = p->size; i > 0; i--)
	{
		p->arr[i] = p->arr[i - 1];
	}
	p->arr[0] = x;
	p->size++;
}

void SLPopBack(SL* p)
{
	assert(p);
	assert(p->size);
	p->size--;
}
void SLPopFront(SL* p)
{
	assert(p);
	assert(p->size);
	for (int i = 0; i < p->size - 1; i++)
	{
		p->arr[i] = p->arr[i + 1];
	}
	p->size--;
}

void SLInsert(SL* p, int pos, DataType x)
{
	assert(p);
	assert(pos>=0 && pos <= p->size);
	SLCheckCapacity(p);
	for (int i = p->size; i >= pos; i--)
	{
		p->arr[i] = p->arr[i - 1];
	}
	p->arr[pos] = x;
	p->size++;
}
void SLErase(SL* p, int pos)
{
	assert(p);
	assert(pos >= 0 && pos <= p->size);
	for (int i = pos - 1; i < p->size; i++)
	{
		p->arr[i] = p->arr[i + 1];
	}
	p->size--;
}

int SLFind(SL* p, DataType x)
{
	assert(p);
	for (int i = 0; i < p->size; i++)
	{
		if (p->arr[i] == x)return i + 1;
	}
	return -1;
}

main.c

#include"text.h"

void slTest01() {
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLprint(sl);      //1 2 3 4
	SLPushBack(&sl, 5);
	SLprint(sl);
	
	SLPushFront(&sl, 5);
	SLPushFront(&sl, 6);
	SLPushFront(&sl, 7);
	SLprint(sl);       //7 6 5 1 2 3 4
	
	SLPopBack(&sl);
	SLPopBack(&sl);
	SLPopBack(&sl);
	SLPopBack(&sl);
	SLprint(sl);   //1 2
	SLPopFront(&sl);
	SLPopFront(&sl);
	SLPopFront(&sl);
	//SLPopFront(&sl);
	SLprint(sl);   //3 4
	//SLInsert(&sl, 0, 100);
	//SLprint(sl); //100 1 2 3 4
	//SLInsert(&sl, sl.size, 200);
	//SLprint(sl); //100 1 2 3 4 200
	//SLInsert(&sl, 100, 300);
	//SLprint(sl);
	//SLErase(&sl, 0);
	//SLprint(sl);  //2 3 4
	//SLErase(&sl, sl.size - 1);
	//SLErase(&sl, 1);
	//SLprint(sl);//1 3 4
}

int main()
{
	slTest01();
	return 0;
}

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

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

相关文章

MySQL 主从读写分离入门——基本原理以及ProxySQL的简单使用

一、读写分离工作原理 读写分离的工作原理&#xff1a;在大型网站业务中&#xff0c;当单台数据库无法满足并发需求时&#xff0c;通过主从同步方式同步数据。设置一台主服务器负责增、删、改&#xff0c;多台从服务器负责查询&#xff0c;从服务器从主服务器同步数据以保持一…

【ArcGIS】渔网分割提取栅格图+网格化分析图绘制

ArcGIS按渔网分割提取栅格图并绘制网格化分析图 准备数据操作步骤步骤1&#xff1a;创建渔网&#xff08;Create Fishnet&#xff09;步骤2&#xff1a;栅格数据处理步骤3&#xff1a;栅格插值步骤4&#xff1a;数据关联 参考 网格化的目的是让各个数据更加标准化的进行统计。因…

FlinkSQL ChangeLog

01 Changelog相关优化规则 0101 运行upsert-kafka作业 登录sql-client&#xff0c;创建一个upsert-kafka的sql作业&#xff08;注意&#xff0c;这里发送给kafka的消息必须带key&#xff0c;普通只有value的消息无法解析&#xff0c;这里的key即是主键的值&#xff09; CREA…

linux系统Jenkins工具参数化构建

Jenkins参数化构建 web服务器jenkins服务器编写主机清单编写脚本代码 jenkins服务web页面操作 web服务器 下载nginx 下载gitcd /usr/share/nginxrm -rf htmlgit clone http://root:Qq123456192.168.188.176/ximu/test-nginx.gitmv test-nginx/ htmljenkins服务器 下载ansible…

前端Vue自定义勾选协议组件的开发与应用

摘要&#xff1a; 随着前端技术的不断发展&#xff0c;用户体验成为了软件开发中的关键要素。在登录、注册等场景中&#xff0c;勾选协议是常见的需求。本文旨在介绍一款基于 Vue.js 的自定义勾选协议组件的开发与应用&#xff0c;该组件适用于多种场景&#xff0c;并且具备良…

虚拟机部署Sentry步骤,国内地址

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

【计算机网络】TCP 如何实现可靠传输

TCP通过三次握手建立连接&#xff0c;四次挥手释放连接&#xff0c;确保连接建立和连接释放的可靠。 序列号、检验和、确认应答信号、重发机制、连接管理、窗口控制、流量控制、拥塞控制 标准回答 可靠传输就是通过TCP连接传送的数据是没有差错、不会丢失、不重复并且按序到达的…

【C++ map和set】

文章目录 map和set序列式容器和关联式容器键值对setset的主要操作 mapmap主要操作 multiset和multimap map和set 序列式容器和关联式容器 之前我们接触的vector,list,deque等&#xff0c;这些容器统称为序列式容器&#xff0c;其底层为线性序列的的数据结构&#xff0c;里面存…

Mac专用投屏工具AirServer 7.27 for Mac中文版2024最新图文教程

Mac专用投屏工具AirServer 7.27 for Mac中文版是一款适用于Mac的投屏工具&#xff0c;可以将Mac屏幕快速投影到其他设备上&#xff0c;如电视、投影仪、平板等。 Mac专用投屏工具AirServer 7.27 for Mac中文版具有优秀的兼容性&#xff0c;可以与各种设备配合使用。无论是iPhon…

[方案实操]中国电子副总陆志鹏:《数据资产化路径的思考与探索》演讲实录和解析

中国数字经济发展和治理学术年会&#xff08;2023&#xff09;上&#xff0c;中国电子党组成员、副总经理&#xff0c;50人论坛委员陆志鹏先生《数据资产化路径的思考与探索》为题进行了主旨演讲&#xff0c;提出“如果简单把资源进行评估定价&#xff0c;价值非常有限&#xf…

STM32标准库开发—实时时钟(BKP+RTC)

BKP配置结构 注意事项 BKP基本操作 时钟初始化 RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);PWR_BackupAccessCmd(ENABLE);//设置PWR_CR的DBP&#xff0c;使能对PWR以及BKP的访问读写寄存器操作 uint16_t ArrayW…

springboot基于web的网上摄影工作室的开发与实现论文

网上摄影工作室 摘要 随着信息技术在管理上越来越深入而广泛的应用&#xff0c;管理信息系统的实施在技术上已逐步成熟。本文介绍了网上摄影工作室的开发全过程。通过分析网上摄影工作室管理的不足&#xff0c;创建了一个计算机管理网上摄影工作室的方案。文章介绍了网上摄影工…

详细介绍如何用windows自带Hyper-V安装虚拟机(windows11和ubuntu22)

通过系统自带的hyper-v安装windows11&#xff0c;舒服又惬意&#xff0c;相比用第三方虚拟机软件速度快很多。 硬件准备 准备 系统需要符合能安装 Hyper-V 的最低要求windows版本含Hyper-V的功能 电脑空间 电脑要有足够的空间来安装你这个虚拟机。根据自己的磁盘容量情况来规…

鸿蒙系统的开发与学习:一、安装工具与处理报错

前言&#xff1a; 鸿蒙系统的学习与记录。 1 、使用开发工具&#xff1a;deveco-studio 1&#xff09;这个是工具的安装 2&#xff09;这个是工具包&#xff0c;里面包含了 obpm&#xff0c;如果你装不上这个&#xff0c;可以使用工具包内部的 2、安装 官方安装教程&#xff…

泰迪智能科技企业数据挖掘平台使用场景

企业数据挖掘平台助力企业数据挖掘&#xff0c;数据挖掘平台也在多个领域发挥着重要的作用。 企业数据挖掘平台具有数据抓取、数据清洗、数据分析、机器学习等多项功能&#xff0c;广泛应用于企业的各个领域&#xff0c;包括&#xff1a;金融行业、医疗行业、交通领域、教育、制…

[BUUCTF]-PWN:oneshot_tjctf_2016解析(字符串输入,onegadget)

查看保护 查看ida 这道题的大致思路就是泄露libc地址&#xff0c;然后用onegadget来getshell 但是要注意&#xff0c;这里要我们输入的数据类型是long int&#xff0c;所以不能用我们常用的p64函数了。 完整exp&#xff1a; from pwn import* from LibcSearcher import* con…

Python的循环结构练习

归纳编程学习的感悟&#xff0c; 记录奋斗路上的点滴&#xff0c; 希望能帮到一样刻苦的你&#xff01; 如有不足欢迎指正&#xff01; 共同学习交流&#xff01; &#x1f30e;欢迎各位→点赞 &#x1f44d; 收藏⭐ 留言​&#x1f4dd; 生命对某些人来说是美丽的&#xff0c…

任职资格经典案例:企业任职资格体系搭建项目纪实

传统的任职资格体系主要考虑年限、经验、资历等因素&#xff0c;部分企业在任职资格体系中也引入了能力指标&#xff0c;但是&#xff0c;实际管理过程中仍然存在很多问题&#xff1a;员工“熬年头”意识严重、工作积极性差、优秀人员因得不到晋升而流失、各方面能力都不错的人…

深入理解Lambda表达式:基础概念与实战演练【第114篇—python:Lambda表达式】

深入理解Lambda表达式&#xff1a;基础概念与实战演练 在现代编程语言中&#xff0c;Lambda表达式作为一种轻量级的匿名函数形式&#xff0c;越来越受到程序员的青睐。特别是在函数式编程兴起的今天&#xff0c;Lambda表达式在简化代码、提高可读性方面发挥着重要作用。本文将…

《AI纪元:幻域探险》

游戏项目名称&#xff1a;《AI纪元&#xff1a;幻域探险》 游戏类型&#xff1a;AI驱动的角色扮演探险游戏&#xff08;RPG&#xff09; 背景设定&#xff1a; 《AI纪元&#xff1a;幻域探险》设定在一个名为“幻域”的广阔虚拟世界。这个世界由高度发达的AI技术支持&#xff0…