C++线性表

线性表的定义及其运算

线性表是一种最简单、最基本也是最常用的线性结构。在线性结构中,数据元素之间存在一个对一个的线性关系,数据元素“一个接一个地排列”。在一个线性表中,数据元素的类型是相同的,或者说,线性表是由同一类型的数据元素构成的。在实际问题中,线性表的例子很多,如学生自然情况信息表、工资信息表等都是线性表。

线性表是具有相同数据类型的n(n≥0)个数据元素的有限序列,通常记为:(a1,a2, …, ai-1, ai, ai+1, …, an)。其中,n为数据元素个数,称为表长。当n=0时称为空表。

表中相邻元素之间存在顺序关系。将ai-1称为ai的直接前驱,ai+1称为ai的直接后继。也就是说,对于ai,当i=2, …, n时,有且仅有一个直接前驱ai-1;当i=1,2, …,n-1时,有且仅有一个直接后继ai+1。a1是表中第一个数据元素,它没有前驱;an是最后一个数据元素,它无后继。

线性表的基本运算如下:

(1)线性表的初始化:void Initiate()

初始条件:线性表不存在。
操作结果:构造一个空的线性表。

(2)求线性表的长度:int Length()

初始条件:线性表已存在。
操作结果:返回线性表所含数据元素的个数。

(3)取表元:DataType Get(int i)

初始条件:表存在且1≤i≤Length()。
操作结果:返回线性表的第i个数据元素的值。

(4)按值查找:int Locate(DataType x)

初始条件:线性表已存在,x是给定的一个数据元素。

操作结果:在线性表中查找值为x的数据元素,返回首次出现的值为x的那个数据元素的序号,称为查找成功;如果未找到值为x的数据元素,返回0表示查找失败。

(5)插入操作:int Insert(DataType x, int i)

初始条件:线性表已存在。

操作结果:在线性表的第i个位置上插入一个值为x的新元素,使原序号为i,i+1, …,n的数据元素的序号变为i+1,i+2, …,n+1,插入后表长=原表长+1,返回1表示插入成功;若线性表L中数据元素个数少于i-1个,则返回0表示插入失败。

(6)删除操作:int Deleted(int i)

初始条件:线性表已存在。

操作结果:在线性表L中删除序号为i的数据元素,删除后使序号为i+1, i+2, …, n的元素变为序号i, i+1, …, n-1,新表长=原表长-1,返回1;若线性表中数据元素个数少于i,则返回0表示删除失败。

线性表的顺序存储结构

线性表的顺序存储结构是指在内存中用一组地址连续的存储空间顺序存放线性表的各数据元素,使得逻辑关系上相邻的数据元素在物理位置上也相邻。采用这种存储形式存储的线性表称为线性表的顺序存储结构,简称顺序表。

线性表的顺序存储结构就是线性表中数据的内存空间都是连续的,所有数据不仅在逻辑上是相邻的,物理意义上也是相邻的

因为内存空间是连续的,所以设数据元素a1的存储地址为Loc(a1),每个数据元素占用d个存储地址,则第i个数据元素的地址为:

Loc(ai)=Loc(a1)+(i-1)*d  (1 <= i <= n)

也就是说,只要知道顺序表的首地址和每个数据元素所占用地址单元的个数,就可以求出第i个数据元素的地址。这也是顺序表具有按数据元素的序号随机存取的特点。

顺序表中数据元素及其存储位置示意图:

顺序表

在程序设计语言中,一维数组的数据在内存中是一段连续的内存空间,所以可以用数组来表示顺序表的数据存储区域。

考虑到线性表中的插入、删除运算,所以线性表的长度也会不停的发生变化,数组的容量要设计的足够大,假设用data[MAXSIZE]表示,其中MAXSIZE是一个根据实际问题定义的足够大的整数,但是存放当前线性表元素的个数可能达不到MAXSIZE大小,所以需要一个变量len来记录当前线性表的长度,始终指向线性表中最后一个元素的下一个位置,其值为最后一个数据元素的位置值,若该表为空表,则len=0。

顺序表的数据类型定义如下:

#pragma once

#define		MAXSIZE		100

// 学生表
struct Student
{
	int					id_;		// 学生id
	std::string			name_;		// 学生姓名
	int					score_;		// 学生成绩

	Student()
		: id_(0), score_(0)
	{}
};

class SequenList
{
public:

	// 初始化线性表
	void Initiate();

private:

	Student stu_[MAXSIZE];

	int len_;
};

初始化运算

// 顺序表的初始化即构造一个数据元素个数为0的空表
void SequenList::Initiate()
{
	len_ = 0;
}

插入运算

顺序表的插入运算是指在顺序表的第i个位置上插入一个值为x的新元素,插入后使原表为n的顺序表(a1, a2, …, ai-1, ai, ai+1, …, an)变为表长为n+1的顺序表(a1, a2, …, ai-1, x, ai, ai+1, …, an)其中,i的合理取值范围为1 ≤ i ≤ n+1。

顺序表插入示意图如下:
在这里插入图片描述

bool SequenList::Insert(Student x, int i)
{
	if (len_ >= MAXSIZE)
	{
		std::cout << "顺序表已满" << std::endl;

		return false;
	}
	else if ((i < 1) || i > (len_ + 1))
	{
		std::cout << "插入位置不合法" << std::endl;

		return false;
	}
	else
	{
		for (int j = len_; j >= i; j--)
			stu_[j] = stu_[j - 1];

		stu_[i - 1] = x;
		len_++;

		return true;
	}
}

插入运算的时间性能分析

顺序表的插入运算时间主要浪费在数据的移动,在第i个位置上插入x,从第i个到第n个元素都要向后移动一个位置,共需要移动n-(i-1),即n-i+1个数据元素。而i的取值范围为
1 ≤ i ≤ n+1,即有n+1个位置可以插入。所以时间复杂度为O(n)。

删除运算

线性表的删除运算是指将表中第i个元素从线性表中删除,使原表长为n的线性表(a1,a2, …,ai-1,ai,ai+1, …,an)变成表长为n-1的线性表(a1, a2, …, ai-1, ai+1, …, an)i的取值范围为1 ≤ i ≤ n。

顺序表的删除示意图如下:

在这里插入图片描述

bool SequenList::Delete(const int& i)
{
	if (i < 1 && i > len_)
	{
		std::cout << "删除位置不合法" << std::endl;

		return false;
	}
	else
	{
		for (int j = i; j < len_; j++)
			stu_[j - 1] = stu_[j];

		len_--;

		return true;
	}

	return false;
}

同理,删除数据也需要移动数据,平均时间复杂度为O(n)。

按值查找

顺序表中的按值查找是指在线性表中查找与给定值x相等的数据元素。在顺序表中完成该运算最简单的方法是:从第一个元素a1起依次和x比较,直到找到一个与x相等的数据元素,则返回它在顺序表中的位置值(下标+1);或者查遍整个表都没有找到与x相等的数据元素,则返回0。

Student SequenList::Locate(const int& _id)
{
	for (int j = 0; j < len_; j++)
	{
		if (stu_[j].id_ == _id)
			return stu_[j];
	}

	return Student();
}

本算法的主要运算是比较。显然,比较的次数与x在表中的位置有关,也与表长有关。当a1=x时,比较一次成功;当an=x时,比较n次成功。在查找成功的情况下,平均比较次数为(n+1)/2,时间复杂度为O(n)。

读取第i个元素的值

Student SequenList::Get(const int& i)
{
	if (i < 1 || i > len_)
	{
		std::cout << "查找位置不合法" << std::endl;

		return Student();
	}

	return stu_[i - 1];
}

取得数据元素个数

int SequenList::GetLength()
{
	return len_;
}

线性表的链式存储结构

顺序表的存储特点是在物理位置上元素的存储地址连续,因此,对顺序表元素的插入、删除需要通过移动实现,大大降低了效率。

线性表的链式存储结构不需要用地址连续的存储单元来实现,因为它不要求逻辑关系上相邻的两个数据元素物理位置上也相邻。它通过“链”建立起数据元素之间的逻辑关系。因此对线性表的插入、删除不需要移动数据元素。

单链表结构

链表的元素的内存空间是不连续的,那么要想实现线性结构,需要在每个数据元素中存放一个指针,这个指针指向下一个元素的地址,这样的一个结构称为结点。

在这里插入图片描述
在这里插入图片描述定义链表

// 学生表
struct Student
{
	int					id_;		// 学生id
	std::string			name_;		// 学生姓名
	int					score_;		// 学生成绩

	Student(int _id, std::string _name, int _score)
		: id_(_id), score_(_score), name_(_name)
	{}

	Student()
		: id_(0), score_(0)
	{}
};

class Item
{
public:

	Link* next_;

	Student stu_;

	Item()
	{
		next_ = nullptr;
	}
};

class Link
{
public:

	Link();

	~Link();

	void Initiate();                  //初始化
	void DeleteAll();                 //删除所有结点
	void HeadCreate(int n);           //从头建链表
	void TailCreate(int n);           //从尾建链表
	void HeadCreateWithHead(int n);   //建立带表头的链表(从头)
	void TailCreateWithHead(int n);   //建立带表头的链表(从尾)
	int Length();                     //求链表长度
	Item *Locatex(Student x);        //查找值为x的数据元素
	Item *Locatei(int i);            //查找第i个元素
	bool Insert(Student x, int i);    //在链表第i个结点之前插入x
	bool Deleted(int i);              //删除链表中第i个结点
	void Print();                     //打印链表

private:

	Item* item_;	// 链表头指针
};

单链表的运算

1、初始化

void Link::Initiate()
{
    DeleteAll();

    head_ = nullptr;
}

2、建立单链表

(1)头插法:从表尾到表头建立单链表(不带有空白头结点)

因为是在链表的头部插入,读入数据的顺序和线性表中的逻辑顺序是相反的。

在这里插入图片描述

void Link::HeadCreate(int n)
{
    DeleteAll();

    Item* s = nullptr, *p = nullptr;

    for (int i = 0; i < n; i++)
    {
        s = new Item;
        s->stu_ = Student(i, "ss", i * 10 + 1);

        s->next_ = p;

        p = s;
    }

    head_ = p;
}

(2)尾插法:从表头到表尾建立单链表(不带有空白头结点)

因为是在链表的尾部插入,读入数据的顺序和线性表中的逻辑顺序是相同的。

在这里插入图片描述

void Link::TailCreate(int n)
{
    DeleteAll();

    Item* s = nullptr, * p = nullptr, * r = nullptr;

    for (int i = 0; i < n; i++)
    {
        s = new Item;
        s->stu_ = Student(i, "tailCreate", i * 20 + 1);
        s->next_ = nullptr;

        if (!p)
            p = r = s;
        else
        {
            r->next_ = s;
            r = s;
        }
    }

    head_ = p;
}

上述两种插入方法是头结点不是空白结点,这种链表在某些情况下不好处理,在链表中插入结点时,将结点插在第一个位置和其他位置是不同的;在链表中删除结点时,删除第一个结点和删除其他结点的处理也是不同的;为了方便操作,有时在链表的头部加入一个空白的“头结点”,头结点的类型与数据结点一致,数据域为空,在标识链表的头指针变量H中存放该结点的地址。这样即使是空表,头指针变量H也不再为空了。头结点的加入使得“第一个结点”的问题不再存在,也使得“空表”和“非空表”的处理成为一致。

在这里插入图片描述
3)头插法:从表尾到表头建立单链表(带有空白头结点)

void Link::HeadCreateWithHead(int n)
{
    DeleteAll();

    Item* s = nullptr, * p = new Item;

    p->next_ = nullptr;

    for (int i = 0; i < n; i++)
    {
        s = new Item;
        s->stu_ = Student(i, "headCreateWithHead", i * 30 + 3);
        
        s->next_ = p->next_;	// 1
        p->next_ = s;			// 2
    }

    head_ = p;
}

示意图如下:其中1和2的代码顺序不能变

在这里插入图片描述(4)尾插法:从表头到表尾建立单链表(带有空白头结点)

void Link::TailCreateWithHead(int n)
{
    DeleteAll();

    Item* s = nullptr, *r = nullptr, * p = new Item;
    p->next_ = nullptr;

    r = p;

    for (int i = 0; i < n; i++)
    {
		s = new Item;
		s->stu_ = Student(i, "tailCreateWithHead", i * 30 + 4);

		r->next_ = s;
		r = s;
    }

    head_ = p;
}

没有特殊说明的话,以下所有单链表都是带有空白结点

3.求表长

顺序表的表长可以方便获得。相比而言,求单链表的表长稍微复杂些。设有一个移动指针p和计数器j,初始化后,p所指结点后面若还有结点,p向后移动,计数器加1。

int Link::Length()
{
    int count = 0;

    Item* temp = head_;

    while (temp->next_)
    {
        count++;

        temp = temp->next_;
    }

    return count;
}

4.查找操作

(1)按序号查找

从单链表的第一个元素结点起,判断当前结点是否是第i个,若是,则返回该结点的指针;否则继续下一个结点的查找,直到表结束为止。若没有第i个结点,则返回空;如果i=0;则返回头指针。

Item* Link::Locatei(int i)
{
    if (i == 0)
        return head_;

    Item* p = head_->next_;

    int j = 1;
    while (!p && j < i)
    {
        p = p->next_;

        j++;
    }

    if (j == i)
        return p;

	std::cout << "查找位置大于链表长度" << std::endl;

	return nullptr;
}

(2)按值查找

即定位从链表的第一个元素结点起,判断当前结点值是否等于x,若是,返回该结点的指针,否则继续下一个结点的查找,直到表结束为止。若找不到,则返回空。

根据学生id查找

Item* Link::Locatex(Student x)
{
    Item* p = p->next_;

    while (!p)
    {
        if (p->stu_.id_ == x.id_)
            return p;

        p = p->next_;
    }

    std::cout << "没有该元素" << std::endl;

    return nullptr;
}

5.插入

(1)后插结点

设p指向单链表中某结点,s指向待插入的值为x的新结点,将s插入p的后面,插入示意图如下:

在这里插入图片描述操作如下:

s->next = p->next;
p->next = s;

注意:上述操作顺序不能反

(2)前插结点

设p指向链表中某结点,s指向待插入的值为x的新结点,将s插入p的前面,插入示意图如图所示。与后插不同的是,首先要找到*p的前驱*q,再完成在q之后插入s。设单链表头指针为L,操作如下:

在这里插入图片描述

q=L;

while(q->next! =p)
  q=q->next;              //找*p的直接前驱
  
s->next=q->next;
q->next=s;

后插操作的时间复杂度为O(1),前插操作因为要找p的前驱,时间复杂度为O(n)。其实,我们关心的是数据元素之间的逻辑关系,所以仍然可以将s插入*p的后面,然后将p->data与s->data交换即可。这样既满足了逻辑关系,也能使得时间复杂度为O(1)。

(3)插入算法

① 找到第i-1个结点;若存在,继续步骤②,否则结束。
② 申请新结点,将数据填入新结点的数据域。
③ 将新结点插入。

bool Link::Insert(Student x, int i)
{
    // 通过位置查找到结点
    Item* p = Locatei(i);

    if (!p)
        return false;

    Item* s = new Item;
    s->stu_ = x;
    s->next_ = p->next_;

    p->next_ = s;

    return true;
}

6、删除

① 找到第i-1个结点,若存在,继续步骤②,否则结束。
② 若存在第i个结点,则继续步骤③,否则结束。
③ 删除第i个结点,结束。

bool Link::Deleted(int i)
{
    // 找到前驱结点
    Item* p = Locatei(i - 1);

    if (!p)
        return false;

    Item* q = p->next_;

    if (!q)
        return false;

    p->next_ = q->next_;
    
    delete q;
    q = nullptr;

    return true;
}

7.打印

void Link::Print()
{
    Item* p = head_->next_;

    while (p)
    {
        std::cout << p->stu_.id_ << " " << p->stu_.name_ << " " << p->stu_.score_ << std::endl;

        p = p->next_;
    }

    return;
}

8.删除所有结点

void Link::DeleteAll()
{
    Item* p = nullptr;

    while (head_)
    {
        p = head_->next_;

        delete head_;

        head_ = p;
    }

    return;
}

通过上面的基本操作可知:

① 在单链表上插入、删除一个结点,必须知道其前驱结点的指针;
② 单链表不具有按序号随机访问的特点,只能从头指针开始一个个顺次进行。

循环链表结构

单链表结构下最后一个结点的指针域指向空,循环链表结构的最后一个结点的指针域指向第一个结点。

在这里插入图片描述
在循环单链表上的操作基本上与非循环链表相同,只是将原来判断指针是否为NULL变为是否是头指针,没有其他较大的变化。

对于单链表,只能从头结点开始遍历整个链表;而对于循环单链表,则可以从表中任意结点开始遍历整个链表。

不仅如此,有时对链表常做的操作是在表尾、表头之间进行。此时可以改变链表的标识方法,不用头指针而用一个指向尾结点的指针R来标识,可以使操作效率得以提高。

例如,对两个循环单链表H1、H2的连接操作,是将H2的第一个数据结点接到H1的尾结点。如用头指针标识,则需要找到第一个链表的尾结点,其时间复杂度为O(n);而链表若用尾指针R1、R2标识,则时间复杂度为O(1)。操作如下:

p= R1->next;                  //保存R1的头结点指针
R1->next=R2->next->next;      //头尾连接
delete  R2->next;              //释放第二个表的头结点
R2->next=p;                   //组成循环链表

在这里插入图片描述

双向链表结构

单链表结构里有一个指针指向当前结点后继结点,双向链表结构多了一个指针指向当前结点的前驱结点。

在这里插入图片描述双向链表结构的结点定义:

class Item
{
public:

	Item* next_;

	Item* prior_;

	Student stu_;

	Item()
	{
		next_ = nullptr;

		prior_ = nullptr;
	}
};

带头结点的双向循环链表示意图如下,显然,通过某结点的指针p既可以直接得到它的后继结点的指针p->next,也可以直接得到它的前驱结点的指针p->prior。这样在有些操作中需要找前驱结点时,无须再用循环。

在这里插入图片描述
设p指向双向循环链表中的某一结点,即p是该结点的指针,则p->prior->next表示的是p结点的前驱结点的后继结点的指针,即与p相等;类似,p->next->prior表示的是p结点的后继结点的前驱结点的指针,也与p相等。所以有以下等式:

p->prior->next = p = p->next->prior

1.双向链表中结点的插入

设p指向双向链表中某结点,s指向待插入的值为x的新结点,将s插入p的前面,插入示意图如下:

s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;

在这里插入图片描述
2.双向链表中结点的删除

设p指向双向链表中某结点,删除*p。操作示意图如下:

操作如下:

p->prior->next=p->next;
p->next->prior=p->prior;
delete  p;

在这里插入图片描述

顺序表和链式表的比较

顺序表的优点:

  • 实现简单,大多数开发语言都支持数组
  • 减少开销:不需要开辟结点
  • 可以按元素序号随机访问元素

缺点:

  • 在顺序表中进行插入、删除的操作时需要移动元素,时间复杂度为O(n),表中元素较多时影响效率。
  • 需要预先分配足够大的存储空间,估计的过大,可能会导致顺序表空间大量闲置;预先分配过小,又会造成溢出。

链式表的优缺点恰好与顺序表相反。

怎么选择?

当需要频繁的插入、删除操作时选择链式表,需要随机访问元素时选择顺序表。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【教程】Typecho Joe主题开启并修复壁纸相册不显示问题

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhang.cn] 背景说明 Joe主题本身支持“壁纸”功能&#xff0c;其实就是相册。当时还在网上找了好久相册部署的开源项目&#xff0c;太傻了。 但是网上教程很少&#xff0c;一没说如何开启壁纸功能&#xff0c;二没说开启后为…

Python跨年烟花秀

写在前面 今年跨年怎么过呢~博主用python的pygame实现了一场炫酷的烟花秀&#xff0c;一起来看看吧&#xff01; 环境需求 python3.11.4及以上PyCharm Community Edition 2023.2.5pyinstaller6.2.0&#xff08;可选&#xff0c;这个库用于打包&#xff0c;使程序没有python环境…

使用yolov5的2.0分支训练自己的模型并在x3派运行

目录 准备代码、权重、数据集配置环境准备数据标注数据 训练模型转换模型验证模型准备校准数据转换为板上模型模型精度分析 上板 之前训练自己模型的时候使用的是博主 bubbling的1.0分支的代码&#xff0c;博主的 博客比较详细&#xff0c;使用的是VOC2007数据集&#xff0c;…

迷宫问题的对比实验研究(代码注释详细、迷宫及路径可视化)

题目描述 对不同的迷宫进行算法问题&#xff0c;广度优先、深度优先、以及人工智能上介绍的一些算法&#xff1a;例如A*算法&#xff0c;蚁群算法等。 基本要求&#xff1a; &#xff08;1&#xff09;从文件读入9*9的迷宫&#xff0c;设置入口和出口&#xff0c;分别采用以上方…

基于huffman编解码的图像压缩算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 Huffman编码算法步骤 4.2 Huffman编码的数学原理 4.3 基于Huffman编解码的图像压缩 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ..…

基于ElementUI二次封装el-table与el-pagination分页组件[实际项目使用]

效果&#xff1a; 二次封装el-table组件 <template><div><!-- showHeader:是否显示头部size:表格的大小height:表格的高度isStripe:表格是否为斑马纹类型tableData:表格数据源isBorder:是否表格边框handleSelectionChange:行选中&#xff0c;多选内容发生变化回…

遗传算法的应用——求解一元函数的极值

遗传算法的应用——求解一元函数的极值 1 基本概念2 预备知识3.1 模拟二进制转化为十进制的方法3.2 轮盘赌选择算法 3 问题4 Matlab代码5 运行效果6 总结 1 基本概念 遗传算法(Genetic Algorithm,GA)是模拟生物在自然环境中遗传和进化过程从而形成的随机全局搜索和优化方法&am…

3D视觉-结构光测量-多线结构光测量

工作原理 多线结构光测量在测量方式上类似上述线结构光测量&#xff0c;但是两者也有着一些明显的差别。这种形式的结构光测量&#xff0c;也常常被成为面结构光测量。首先激光器发出电光源通过通过光栅的调制产生多个切片光束&#xff0c;这些切片光束照射到待测物体表面后形成…

2023第三届中国高校大数据挑战赛B题代码

任务已完成&#xff0c;聚类效果很好&#xff08;主要在于数据的处理以及特征工程&#xff09;, 需代码si&#xff0c;yuer有限先到先得。

AI测试|颠覆客户端UI自动化?别担心,你还不会失业!AppAgent框架简单试用

01 AppAgent会成为新的趋势吗&#xff1f; 近日&#xff0c;腾讯团队发表了一篇论文&#xff0c;并开源了一款基于大语言模型的&#xff0c;用于手机端执行复杂任务的多模态智能代理框架——AppAgent。该框架设计的初衷&#xff0c;是让 AI 智能体能自己操作手机&#xff0c;完…

数据压缩专题——静止图像的小波变换编码

随着数字图像技术的发展和应用的广泛&#xff0c;对图像的压缩和编码变得越来越重要。小波变换编码作为一种有效的图像压缩和编码方法&#xff0c;在静止图像处理中得到了广泛应用。本文将介绍静止图像的小波变换编码的基本原理和关键步骤&#xff0c;以及其在图像压缩中的应用…

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK获取相机当前实时帧率(C++)

Baumer工业相机堡盟工业相机如何通过NEOAPI SDK获取相机当前实时帧率&#xff08;C&#xff09; Baumer工业相机Baumer工业相机的帧率的技术背景Baumer工业相机的帧率获取方式CameraExplorer如何查看相机帧率信息在NEOAPI SDK里通过函数获取相机帧率&#xff08;C&#xff09; …

前后端分离nodejs+vue医院预约挂号系统6nrhh

医院预约挂号系统主要有管理员、用户和医生三个功能模块。以下将对这三个功能的作用进行详细的剖析。 运行软件:vscode 前端nodejsvueElementUi 语言 node.js 框架&#xff1a;Express/koa 前端:Vue.js 数据库&#xff1a;mysql 开发软件&#xff1a;VScode/webstorm/hbuiderx均…

HBuilder常用的快捷键

查看专栏目录 Network 灰鸽宝典专栏主要关注服务器的配置&#xff0c;前后端开发环境的配置&#xff0c;编辑器的配置&#xff0c;网络服务的配置&#xff0c;网络命令的应用与配置&#xff0c;windows常见问题的解决等。 文章目录 常用快捷键分9项快捷键1.文件(4)2.编辑(13)3.…

JavaScript系列——正则表达式

文章目录 需求场景正则表达式的定义创建正则表达式通过 / 表示式/ 创建通过构造函数创建 编写一个正则表达式的模式使用简单模式使用特殊字符常用特殊字符列表特殊字符组和范围 正则表达式使用代码演示 常用示例验证手机号码合法性 小结 需求场景 在前端开发领域&#xff0c;在…

Idea中使用Tomcat部署并启动Web项目

首先在Idea中选择编辑运行配置&#xff0c;如下图 左上角的“”号&#xff0c;选择Tomcat服务&#xff0c;如下图 自定义服务名称和项目在浏览器的访问路径 配置Tomcat服务器路径&#xff0c;如下图 然后在服务器中部署项目&#xff08;下面的警告提示&#xff1a;Warning: No …

PostgreSQL 作为向量数据库:入门和扩展

PostgreSQL 拥有丰富的扩展和解决方案生态系统&#xff0c;使我们能够将该数据库用于通用人工智能应用程序。本指南将引导您完成使用 PostgreSQL 作为向量数据库构建生成式 AI 应用程序所需的步骤。 我们将从pgvector 扩展开始&#xff0c;它使 Postgres 具有特定于向量数据库…

HCIP:rip综合实验

实验要求&#xff1a; 【R1-R2-R3-R4-R5运行RIPV2】 【R6-R7运行RIPV1】 1.使用合理IP地址规划网络&#xff0c;各自创建环回接口 2.R1创建环回 172.16.1.1/24 172.16.2.1/24 172.16.3.1/24 3.要求R3使用R2访问R1环回 4.加快网络收敛&#xff0c;减少路由条目数量&#xff0c;增…

Vue 框架前导:详解 Ajax

Ajax Ajax 是异步的 JavaScript 和 XML。简单来说就是使用 XMLHttpRequest 对象和服务器通信。可以使用 JSON、XML、HTML 和 text 文本格式来发送和接收数据。具有异步的特性&#xff0c;可在不刷新页面的情况下实现和服务器的通信&#xff0c;交换数据或者更新页面 01. 体验 A…

golang 图片加水印

需求&#xff1a; 1&#xff0c;员工签到图片加水印 2&#xff0c;水印文字需要有半透明的底色&#xff0c;避免水印看不清 3&#xff0c;图片宽设置在600&#xff0c;小于600或者大于600都需要等比例修改图片的高度&#xff0c;保持水印在图片中的大小和位置 4&#xff0c;处理…