速通数据结构第二站 顺序表

文章目录

速通数据结构与算法系列

1 速通数据结构与算法第一站 复杂度        http://t.csdnimg.cn/sxEGF

感谢佬们支持!


目录

系列文章目录

  • 前言
  • 一、顺序表
  •       0 结构体
  •       1 接口声明
  •       2 初始化和销毁
  •       3 扩容函数
  •       4 打印和判空
  •       5 尾插
  •       6 尾删
  •       7 头插
  •       8 头删
  •       9 查找
  •      10insert (指定位置下一个插入)
  •      11erase  (删除指定位置)
  •      12完整代码
  • 二、顺序表OJ
  •       1 移除元素
  •       2 删除有序数组中的重复项
  •       3 合并两个有序数组
  • 总结

前言

           我们要接触到的第一种数据结构类型是线性表,线性表的定义是n个具有相同特性的数据元素的有限序列。常见的有:顺序表,链表,栈,队列,字符串……

线性表的逻辑结构是线性的,即一条连续的直线,但是其物理结构不一定是连续的,在物理上一般为数组和链式。

在这里我们要先区分一个概念:逻辑结构和物理结构

逻辑结构是一个抽象的结构,是我们研究某种东西时为了便于理解的一种归纳的思想

而物理结构则是真实的存储结构

举个例子来看,磁盘是什么结构?我们拿到一个磁盘会发现它是扇形的,他存储单元是一个个扇区,倘若我们把这些扇形一个个拉直,最后得到一条线,所以它的逻辑结构就是线性表

--》


一、顺序表

顺序表是用一段物理连续存储单元依次存储数据元素的线性结构,一般采用数组存储,利用数组来完成增删查改操作。

我们来浅浅实现一波,先创3个文件:test.c  sqlist.c   sqlist.h

在sqlist.h下……

为了达到C语言版的所谓泛型,模板,我们设一个宏

typedef int SLDataType;

我们先来写它的结构体

结构体


typedef struct sqlist
{SLDataType* a;//数据个数int size;//容量int capacity;
}SL;

注意:在STL的实现中,vector的结构体成员(成员变量)有3个,int* start,int* end,int* endofstorage三个指针(迭代器,此处先暂且认为是int*类型),对应关系为

start=_a;
finish=_a+_size;
endofstorage=_a+capacity;

这样设计的好处就是,当我们swap(vector1,vector2)时;成本将非常低,因为只需交换3个指针而已。


为了方便我们之后STL的学习,我们接口的命名风格都按STL的来

我们在seqlist.h中包一下需要用到的头文件,

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

接口声明

并声明如下接口

//打印函数
void SeqListPrint(SL* ps);//扩容函数
void BuyListNode(SL* ps);//初始化
void SeqListInit(SL* ps);//销毁
void SeqListDestroy(SL* ps);//判空
bool SeqListCheckCapacity(SL* ps);//尾插
void SeqListPushBack(SL* ps, SLDataType x);//尾删
void SeqListPopBack(SL* ps);//头插
void SeqListPushFront(SL* ps, SLDataType x);//头删
void SeqListPopFront(SL* ps);//查找 找到了返回下标,没找到返回-1 
int SeqListFind(SL* ps, SLDataType x);//指定pos位置后插
void SeqListInsert(SL* ps, SLDataType x, int pos);//指定pos位置删
void SeqListErase(SL* ps, int pos);

初始化和销毁

考虑到动态内存泄漏的问题,我们先来实现初始化及其销毁函数

要做的事很简单,初始化时指针置空,size和capacity给个0即可

(其中要注意判空ps,因为我们不知道不知道传入的ps是否为空)

//初始化
void SeqListInit(SL* ps)
{assert(ps);ps->a = NULL;ps->capacity = ps->size = 0;
}//销毁
void SeqListDestroy(SL* ps)
{assert(ps);free(ps->a);ps->a=NULL;ps->capacity = ps->size = 0;}

扩容函数

为了便于调用,我们封装出一个扩容函数来

由于刚开始容量为0,我们第一次给4,之后进行二倍扩容

(二倍扩容采用的是SGI STL的扩容策略)

//扩容函数
void BuyListNode(SL* ps)
{assert(ps);//0就给4,不是就给capacity2倍int newcapacity = (ps->capacity == 0) ? 4 : (ps->capacity * 2);SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));//扩容失败if (NULL == tmp){printf("realloc fail");exit(-1);}//扩容成功ps->a = tmp;ps->capacity = newcapacity;
}

我们简单地来测试一下

SL sl;SeqListInit(&sl);SeqListDestroy(&sl);

(确实挺不错的)


打印和判空

下来我们再写个简单的打印和判空

打印就简单了,遍历打印;判空的逻辑就是size是否等于0

//打印函数
void SeqListPrint(SL* ps)
{assert(ps);for (int i = 0; i < ps->size; ++i){printf("%d ", ps->a[i]);}
}
//判空
bool SeqListCheckCapacity(SL* ps)
{assert(ps);return ps->size == 0;
}

 接下来就该是顺序表核心的插入删除操作了

尾插

我们先来进行尾插,尾插简单,在数组的最后插就行了,顺便更新一下size

注意:在插之前,我们先要考虑是否需要扩容的问题

void SeqListPushBack(SL* ps, SLDataType x)
{assert(ps);//考虑扩容if (ps->capacity == ps->size){BuyListNode(ps);}//插ps->a[ps->size] = x;ps->size++;
}

另外 我们知道,此时的扩容并非在数组后面直接扩容,而是再开一块空间,并将原来的数据拷过来,并释放原空间,在STL的学习中会有所谓迭代器失效的问题,因为之前的空间没了

所以指向之前空间的迭代器就会失效,我们不能用了,但是在C语言中,这种场景则不太容易碰到。


尾删

尾删就更简单了,我们直接--size即可,但是我们需要断言一下size>0

//尾删
void SeqListPopBack(SL* ps)
{assert(ps);//断言一下size,size要大于0assert(ps->size > 0);ps->size--;}

显然,我们发现,尾插,尾删的时间复杂度都是O(1)的。


我们来测试一下尾插和尾删

SL sl;SeqListInit(&sl);SeqListPushBack(&sl,1);SeqListPushBack(&sl, 2);SeqListPushBack(&sl, 3);SeqListPushBack(&sl, 4);SeqListPrint(&sl);printf("\n");SeqListPopBack(&sl);SeqListPushBack(&sl, 5);SeqListPrint(&sl);

 

(没问题)


下来就是有点麻烦的头插和头删了

头插

头插的逻辑是这样的,我们需要讲数组往后挪一格,再在第一个位置做插入

-》

其中这个挪就有意思了,是从前往后挪还是从后往前挪呢?

当然是从后往前挪,不然会覆盖数据

//头插
void SeqListPushFront(SL* ps, SLDataType x)
{assert(ps);//考虑扩容if (ps->capacity == ps->size){BuyListNode(ps);}//插//从后往前挪数据int len = ps->size-1;while (len>=0){ps->a[len+1] = ps->a[len];--len;}ps->a[0] = x;ps->size++;}

头删

头删也需要挪动数据,不过和头插恰好相反,他需要从前往后开始挪

//头删
void SeqListPopFront(SL* ps)
{assert(ps);//挪数据int len = 0;while (len <= ps->size - 1){ps->a[len] = ps->a[len + 1];++len;}ps->size--;
}

显然,头插和头删的时间复杂度都是O(n),所以精明的STL并没有单独提供头插头删操作

我们再来简单的测试一下

SL s2;SeqListInit(&s2);SeqListPushFront(&s2, 1);SeqListPushFront(&s2, 2);SeqListPushFront(&s2, 3);SeqListPushFront(&s2, 4);SeqListPrint(&s2);printf("\n");SeqListPopFront(&s2);

(同样没有任何问题)


查找

下来我们写一下查找

查找的逻辑很简单,遍历数组,找到了返回下标,没找到返回-1

//查找 找到了返回下标,没找到返回-1 
int SeqListFind(SL* ps, SLDataType x)
{for (int i = 0; i < ps->size ; ++i){if (x == ps->a[i]){return i;}}return -1;
}

 查找是为之后的两个函数服务的:insert 和erase

因为在实际运用时,我们一般都是先找到某个元素,再插入/删除

我们写的是删除指定位置和在指定位置的下一个插入

(STL提供的是删除指定位置和指定位置前一个插入)

 写过了之前的头删,写这个insert就简单了

insert

我们依然需要从后往前插的方式,而且在之前需要判断一下pos的合法性

//指定pos位置后插
void SeqListInsert(SL* ps, SLDataType x, int pos)
{assert(pos>=0&&pos <= ps->size);//考虑扩容if (ps->capacity == ps->size){BuyListNode(ps);}//挪动数据int len = ps->size;while (len-pos+1){ps->a[len + 1] = ps->a[len];--len;}ps->a[pos + 1] = x;++ps->size;
}

erase同理

erase

//指定pos位置删
void SeqListErase(SL* ps,  int pos)
{assert(pos>=0&&pos <= ps->size);//pos+1往前挪int len = ps->size-1;while (len-pos){ps->a[len-1] = ps->a[len];len--;}--ps->size;
}

我们再来简单的测试一下

SL s3;SeqListInit(&s3);SeqListPushBack(&s3, 1);SeqListPushBack(&s3, 2);SeqListPushBack(&s3, 3);SeqListPushBack(&s3, 4);SeqListPushBack(&s3, 5);SeqListPrint(&s3);printf("\n");int pos=SeqListFind(&s3,4);printf("%d\n", pos);//SeqListInsert(&s3, 7, pos);SeqListPrint(&s3);printf("\n");SeqListInsert(&s3, 8, pos);SeqListInsert(&s3, 9, pos);SeqListErase(&s3, pos);SeqListPrint(&s3);

 


12 完整代码

 sqlist.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>typedef int SLDataType;typedef struct sqlist
{SLDataType* a;//数据个数int size;//容量int capacity;
}SL;//打印函数
void SeqListPrint(SL* ps);//扩容函数
void BuyListNode(SL* ps);//初始化
void SeqListInit(SL* ps);//销毁
void SeqListDestroy(SL* ps);//判空
bool SeqListCheckCapacity(SL* ps);//尾插
void SeqListPushBack(SL* ps, SLDataType x);//尾删
void SeqListPopBack(SL* ps);//头插
void SeqListPushFront(SL* ps, SLDataType x);//头删
void SeqListPopFront(SL* ps);//查找 找到了返回下标,没找到返回-1 
int SeqListFind(SL* ps, SLDataType x);//指定pos位置后插
void SeqListInsert(SL* ps, SLDataType x, int pos);//指定pos位置删
void SeqListErase(SL* ps, int pos);

sqlist.c

#include "seqlist.h"//初始化
void SeqListInit(SL* ps)
{assert(ps);ps->a = NULL;ps->capacity = ps->size = 0;
}//销毁
void SeqListDestroy(SL* ps)
{assert(ps);free(ps->a);ps->a = NULL;ps->capacity = ps->size = 0;}//扩容函数
void BuyListNode(SL* ps)
{assert(ps);//0就给4,不是就给capacity2倍int newcapacity = (ps->capacity == 0) ? 4 : (ps->capacity * 2);SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity * sizeof(SLDataType));//扩容失败if (NULL == tmp){printf("realloc fail");exit(-1);}//扩容成功ps->a = tmp;ps->capacity = newcapacity;
}//打印函数
void SeqListPrint(SL* ps)
{assert(ps);for (int i = 0; i < ps->size; ++i){printf("%d ", ps->a[i]);}
}void SeqListPushBack(SL* ps, SLDataType x)
{assert(ps);//考虑扩容if (ps->capacity == ps->size){BuyListNode(ps);}//插ps->a[ps->size] = x;ps->size++;
}//尾删
void SeqListPopBack(SL* ps)
{assert(ps);//断言一下size,size要大于0assert(ps->size > 0);ps->size--;}//头插
void SeqListPushFront(SL* ps, SLDataType x)
{assert(ps);//考虑扩容if (ps->capacity == ps->size){BuyListNode(ps);}//插//从后往前挪数据int len = ps->size - 1;while (len >= 0){ps->a[len + 1] = ps->a[len];--len;}ps->a[0] = x;ps->size++;}//头删
void SeqListPopFront(SL* ps)
{assert(ps);//挪数据int len = 0;while (len <= ps->size - 1){ps->a[len] = ps->a[len + 1];++len;}ps->size--;}//查找 找到了返回下标,没找到返回-1 
int SeqListFind(SL* ps, SLDataType x)
{for (int i = 0; i < ps->size; ++i){if (x == ps->a[i]){return i;}}return -1;}//指定pos位置后插
void SeqListInsert(SL* ps, SLDataType x, int pos)
{assert(pos >= 0 && pos <= ps->size);//考虑扩容if (ps->capacity == ps->size){BuyListNode(ps);}//挪动数据int len = ps->size;printf("%d\n", len);while (len - pos + 1){ps->a[len + 1] = ps->a[len];--len;}ps->a[pos + 1] = x;++ps->size;
}//指定pos位置删
void SeqListErase(SL* ps, int pos)
{assert(pos >= 0 && pos <= ps->size);//pos+1往前挪int len = ps->size - 1;while (len - pos){ps->a[len - 1] = ps->a[len];len--;}--ps->size;
}

test.c(测试代码)

#include "seqlist.h"void test1()
{SL sl;SeqListInit(&sl);SeqListPushBack(&sl,1);SeqListPushBack(&sl, 2);SeqListPushBack(&sl, 3);SeqListPushBack(&sl, 4);SeqListPrint(&sl);printf("\n");SeqListPopBack(&sl);SeqListPushBack(&sl, 5);SeqListPrint(&sl);SeqListDestroy(&sl);}void test2()
{SL s2;SeqListInit(&s2);SeqListPushFront(&s2, 1);SeqListPushFront(&s2, 2);SeqListPushFront(&s2, 3);SeqListPushFront(&s2, 4);SeqListPrint(&s2);printf("\n");SeqListPopFront(&s2);SeqListPrint(&s2);SeqListDestroy(&s2);}void test3()
{SL s3;SeqListInit(&s3);SeqListPushBack(&s3, 1);SeqListPushBack(&s3, 2);SeqListPushBack(&s3, 3);SeqListPushBack(&s3, 4);SeqListPushBack(&s3, 5);SeqListPrint(&s3);printf("\n");int pos=SeqListFind(&s3,4);SeqListInsert(&s3, 7, pos);SeqListPrint(&s3);printf("\n");SeqListInsert(&s3, 8, pos);SeqListInsert(&s3, 9, pos);SeqListErase(&s3, pos);SeqListPrint(&s3);}int main()
{test3();return 0;
}

二、顺序表OJ

学完了顺序表的实现,我们可以写一些有关顺序表的OJ题


1 移除元素

题目链接: . - 力扣(LeetCode)

这个题就是简单的双指针,我们可以定义两个变量, src用来遍历数组,dst用来存放不是val的值,并标记移除元素后的新长度

由于它要的是新长度,所以我们不用真的删除那些元素。

int removeElement(int* nums, int numsSize, int val)
{int src = 0;int dst = 0;while (src < numsSize){if (nums[src] != val){nums[dst] = nums[src];++dst;++src;}else{++src;}}return dst;
}

关于双指针这类算法,后续我们会进行系统的学习和刷题哦~


2 删除有序数组中的重复项

题目链接:  . - 力扣(LeetCode)

由于要求原地删除,所以我们不能再搞一个数组,把不重复的元素push_back进去

这道题我们依旧采用双指针来做

但是我们需要三个指针,两个cur,next用来遍历,一个dst用来记录不重复元素的个数

我们的逻辑是这样,如果cur和next指向值相同的数就让next++,指向值不相同的数时

用dst下标记录cur指向的值,再让dst++,让cur去next的位置,再让next++

如图所示

-》

-》

(此时第一个下标就记录了0这个元素)

但是还有个问题:到了要结束的时候,此时cur,next指向的元素不同,next++就跳出了循环

所以最后一个元素我们在循环结束后要再补充至dst下标上

由此可得代码如下

int removeDuplicates(int* nums, int numsSize)
{int cur = 0;int next = 1;int dst = 0;//考虑为空if (numsSize == 0){return 0;}while (next < numsSize){if (nums[next] == nums[prev]){next++;}else{nums[dst++] = nums[prev];prev = next;next++;}}nums[dst++]=nums[prev];return dst;
}

3 合并两个有序数组

题目链接 : . - 力扣(LeetCode)

显然,我们的任务是把nums2的拷入nums1中,

两个数组一个给一个指针int i=0,int j=0,遍历数组找小的;再标记一个dst指针,将较小者插入nums[dst++];

当两个数组有一个走完了另一个数组的元素再依次拷入剩下的位置即可。

啊但是!

当你画了图模拟一遍之后就会发现,如果我们从前往后开始会有覆盖的可能

如图,当nums2的2和nums1的3比较时,由于2<3,显然2要到3的位置,所以这个3就会被覆盖

那怎么办呢?

以我们刚才头插头删的经验来看,从前往后会覆盖,那我们就从后往前

所以我的指针变为int end1=m-1,int end2=n-1,int end=m+n-1。比较的时候也是比较大的

所以代码如下

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{int end1=m-1;int end2=n-1;int end=m+n-1;while (end1>=0 && end2>=0){if (nums1[end1] > nums2[end2]){nums1[end--] = nums1[end1--];}else{nums1[end--] = nums2[end2--];}}//有一个走完了while (end2>=0){nums1[end--] = nums2[end2--];}
}

总结

 做总结,这篇博客我们学习了了顺序表,顺序表的优势是由于有下标,可以随机访问尾插尾删效率高。下篇博客我们要学习可能有些些许难得链表,如果你的指针有些遗忘了,建议去复习一下指针哦~

水平有限,还请各位大佬指正。如果觉得对你有帮助的话,还请三连关注一波。希望大家都能拿到心仪的offer哦。

每日gitee侠:今天你交gitee了嘛

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

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

相关文章

基于51单片机的拔河游戏Proteus仿真

地址&#xff1a;https://pan.baidu.com/s/18LMEo-_WYcoyTOkWYJ0NUg 提取码&#xff1a;1234 仿真图&#xff1a; 1&#xff09;设计一个能进行拔河游戏的电路&#xff0c;通过按键控制游戏开始和复位。 2&#xff09;电路使用9个发光二极管排成一行&#xff0c;中间的LED为拔…

ElasticSearch之数据建模

写在前面 本文看下es数据建模相关的内容。 1&#xff1a;什么是数据建模 数据建模是对真实数据的一种抽象&#xff0c;最终映射为计算机形式的表现。其包括如下三个阶段&#xff1a; 1&#xff1a;概念模型 2&#xff1a;逻辑模型 3&#xff1a;数据模型2&#xff1a;es数据…

python爬虫基础-----运算符(第三天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

纹理压缩算法

一、什么是纹理压缩 我们知道游戏中对于3D物体表面细节的表现最重要的还是靠贴图来实现的&#xff0c;那么越是高分辨率越是真彩色的贴图自然表现力也是越强&#xff0c;但是同时带来的问题是所需占用的内存会成倍的上升&#xff0c;而节省内存这一点在目前的游戏中还是非常非…

实时通讯技术实现

实时通讯技术实现 前言 在CS架构中&#xff0c;经常会有实时通信的需求。客户端和服务端建立连接&#xff0c;服务端实时推送数据给客户端。本文介绍几种常见的实现方式&#xff0c;希望能给读者们一点点参考。 实时通讯的主要实现技术 长轮询(Long Polling) WebSocket 服务器发…

牛客周赛 Round 38(A,B,C,D,E,F,G)

比赛链接 官方讲解&#xff08;不分P不分段直接两小时怼上来是坏文明 &#xff09; 这场的题很棒&#xff0c;思维有难度&#xff0c;考察的知识点广泛&#xff0c;有深度&#xff0c;很透彻。感觉学到了很多。建议补题。 A 小红的正整数自增 思路&#xff1a; 签到。 可以…

6.5 Batch Normalization

在训练神经网络时&#xff0c;往往需要标准化&#xff08;normalization&#xff09;输入数据&#xff0c;使得网络的训练更加快速和有效。 然而SGD&#xff08;随机梯度下降&#xff09;等学习算法会在训练中不断改变网络的参数&#xff0c;隐藏层的激活值的分布会因此发生变…

VTK对属性参数的设置,以及用vtkFieldData存储属性数据的方法

数据集的属性&#xff08;属性数据&#xff09;是与数据集结构相关联的。而数据集又是建立在点和单元的基础上&#xff0c;所以数据属性很自然地是与点和单元相关联。即每个点或每个单元都有与其对应的数据属性。 数据属性的值称为属性数据。属性数据一般设置为一些有实际意义的…

学生宿舍智能控电柜安装调试技术

学生宿舍智能控电柜安装调试石家庄光大远通电器有限公司宿舍控电限电管理系统是一种用于管理学生宿舍用电的智能系统&#xff0c;主要功能包括: 1.实时监控和控制:该系统能够实时监测和记录宿舍的用电情况&#xff0c;包括电器使用情况、电量消耗等。管理人员可以通过电脑或手机…

探索酷开科技独特魅力|加入酷开会员让观影之旅更畅快|酷开系统

你是否渴望一场震撼心灵的观影之旅&#xff1f;不妨走进酷开系统的世界&#xff0c;徜徉在剧集的海洋&#xff0c;满足无限观影的渴望&#xff01;还在担心剧荒吗&#xff1f;还在为无聊的周末发愁吗酷开系统为你赶走无聊&#xff0c;它拥有海量的影视资源&#xff0c;4大片库、…

nginx详解(持续更新)

nginx定义 nginx安装 nginx目录 程序相关命令 服务相关命令 虚拟主机&#xff08;server&#xff09; 路由匹配&#xff08;location&#xff09; 代理&#xff08;proxy_pass&#xff09; 正向代理 反向代理 负载均衡&#xff08;upstream&#xff09; 负载均衡策略 动静分…

C++ 简单模拟实现 STL 中的 list 与 queue

目录 一&#xff0c;list 1&#xff0c; list 的节点与迭代器 2&#xff0c;list 的数据结构、一些简单的功能、构造函数 3&#xff0c;list 的对元素操作 4&#xff0c;C 11 的一些功能 5&#xff0c;完整代码&#xff1a; 二&#xff0c;queue 一&#xff0c;list std…

开源 OLAP 及其在不同场景下的需求

目录 一、开源 OLAP 综述 二、OLAP场景思考 2.1 面向客户的报表 2.2 面向经营的报表 2.3 末端运营分析 2.4 用户画像 2.5 订单分析 2.6 OLAP技术需求思考 三、开源数据湖/流式数仓解决方案 3.1 离线数仓体系——Lambda架构 3.2 实时数据湖解决方案 3.3 实时分析解决…

Java毕业设计-基于springboot开发的校园台球厅人员与设备管理系统-毕业论文+答辩PPT(附源代码+演示视频)

文章目录 前言一、毕设成果演示&#xff08;源代码在文末&#xff09;二、毕设摘要展示1、开发说明2、需求分析3、系统功能结构 三、系统实现展示1、系统功能模块2、管理员功能模块3、用户功能模块 四、毕设内容和源代码获取总结 Java毕业设计-基于springboot开发的校园台球厅人…

Linux-1.常见指令以及权限理解

目录 本节目标 使用 XShell 远程登录 Linux 关于 Linux 桌面 下载安装 XShell 查看 Linux 主机 ip 使用 XShell 登陆主机 XShell 下的复制粘贴 Linux下基本指令 登录Linux服务器 新建多用户 全屏 1.快速认识5~6个命令 2.详细谈论课件的所有指令 01. ls 指令 02…

初识redis(一)

前言 引用的是这本书的原话 Redis[1]是一种基于键值对&#xff08;key-value&#xff09;的NoSQL数据库&#xff0c;与很多键值对数据库不同的是&#xff0c;Redis中的值可以是由string&#xff08;字符串&#xff09;、hash&#xff08;哈希&#xff09;、list&#xff08;列…

33.HarmonyOS App(JAVA)鸿蒙系统app数据库增删改查

33.HarmonyOS App(JAVA)鸿蒙系统app数据库增删改查 关系数据库 关系对象数据库&#xff08;ORM&#xff09; 应用偏好数据库 分布式数据库 关系型数据库&#xff08;Relational Database&#xff0c;RDB&#xff09;是一种基于关系模型来管理数据的数据库。HarmonyOS关系型…

Pandas与Jupyter Notebook的完美结合【第153篇—数据分析】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 利用Python进行数据分析&#xff1a;Pandas与Jupyter Notebook的完美结合 在数据科学和分析…

职场口才提升之道

职场口才提升之道 在职场中&#xff0c;口才的重要性不言而喻。无论是与同事沟通协作&#xff0c;还是向上级汇报工作&#xff0c;亦或是与客户洽谈业务&#xff0c;都需要具备良好的口才能力。一个出色的职场人&#xff0c;除了拥有扎实的专业技能外&#xff0c;还应具备出色…

web自动化测试系列-selenium的安装和运行(一)

目录 web自动化系列之如何安装selenium 1.web自动化中的三大亮点技术 2.web自动化能解决什么问题 &#xff1f; 3.为什么是selenium ? 4.selenium特点 5.selenium安装 6.下载浏览器及驱动 7.测试代码 web自动化系列之如何安装selenium web自动化 &#xff0c;一个老生…