队列的实现与OJ题目解析

"不是你变优秀了, 那个人就会喜欢你."

文章索引

  • 前言
  • 1. 什么是队列
  • 2. 队列的实现
  • 3. OJ题目解析
  • 4. 总结

前言

感情可以培养是个伪命题. 如果有足够多的时间和爱, 就可以让另一个人爱上你的话, 那谁和谁都可以相爱了. 爱情之所以会让人死去活来, 是因为, 答案都写在了彼此第一次见面的那天.

本文旨在介绍队列的实现方法以及OJ有关队列的题目分析

博客主页: 酷酷学!!!

期待更多好文 感谢关注~


正文开始

1. 什么是队列

队列: 只允许一端进行插入数据操作, 在另一端进行删除操作的特殊线性表, 队列具有先进先出FIFO(First In First Out)
入队列: 进行插入操作的一端称为队尾
出队列: 进行删除操作的一端称为队头

在这里插入图片描述

2. 队列的实现

队列也可以使用数组和链表的结构实现, 使用链表的结构更优一些, 因为如果使用数组结构, 出队列在数组头上出数据, 效率会比较低.而链表在头删会比较友好.

故我们采用链表结构继续队列的实现

在这里插入图片描述

第一步:

首先进行文件的创建
然后在Queue.h文件中进行声明和定义

在这里插入图片描述

定义链表的节点,包含一个数据域和一个指针域, 因为我们需要使用链表来实现队列

#pragma once

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

typedef int DataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	DataType val;
}QNode;

以下是我们需要实现的队列的方法, 也声明在头文件中,在链表的实现中,我们传递了一个头指针,指向了这个链表, 但是对于队列, 我们定义两个指针,头指针和尾指针, 这样我们在进行头删和尾插时比较方便, 那为什么链表为什么不定义连个指针呢, 一是链表需要进行头插尾插, 头删尾删, 我们还需要找到尾节点的前一个节点, 解决不了根本问题, 干脆定义一个指针进行遍历查找, 那么回想, 想要改变链表的头指针, 我们需要传递头指针的地址, 也就是二级指针, 那么这里也一样, 我们是不是也要传递队列的头指针的地址和尾指针的地址呢? 答案是肯定, 但是麻烦, 有没有更好的办法呢?

 队尾插入
//void QueuePush(QNode** pphead, QNode** pptail, QDataType x);
 队头删除
//void QueuePop(QNode** pphead, QNode** pptail);

有,可以定义一个结构体, 让队列的头指针和尾指针都存放在结构体中, 这样我们想要改变头指针和尾指针也就变成了改变结构体的成员, 那么传递结构体指针, 进行实参的修改就可以完美解决, 如下, 当然这里我们增加一个size变量用来记录队列元素的个数, 以避免我们需要知道个数是进行O(N)时间复杂度的查找, 接下来的方法实现也会深有体会.

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);

//队尾插入
void QueuePush(Queue* pq, DataType x);
//队头删除
void QueuePop(Queue* pq);

//判空
bool QueueEmpty(Queue* pq);
//获取节点个数
int QueueSize(Queue* pq);

//获取头节点
DataType QueueFront(Queue* pq);
//获取尾节点
DataType QueueTail(Queue* pq);

第二步:
队列接口的实现:

  • 初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

这里我们来对比学习, 可以点击查看链表的实现链表(1)
我们进行链表的实现时, 直接就是头插操作, 为什么呢, 答案是实现链表我们只需要一个指针, 指向链表就可以了,我们直接在插入时进行初始化, 而队列不是, 队列有三个变量, 所以我们需要对他进行初始化操作, 头指针尾指针和size变量.

  • 销毁
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

那么有了初始化, 必然少不了我们的销毁操作, 这里主要是针对我们动态开辟的节点, 我们需要手动释放, 不能让它内存泄漏, 当节点都释放完毕后, 需要让头指针和尾指针都置为NULL,规避野指针的出现, size也还原为0.

  • 入队列
void QueuePush(Queue* pq, DataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");//perror函数在<stdio.h>头文件包含,标准错误流
		return;
	}
	newnode->next = NULL;
	newnode->val = x;

	if (pq->ptail == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

接下来进行入队列的操作,首先传递过来的结构体肯定不能是个空的, 你是个空的那我还怎么去访问我的头指针和尾指针, 切记NULL不能访问, 然后因为我们初始化的时候没有开辟节点, 所以我们在这里进行节点的开辟, 当然这个是灵活变动的, 使用malloc函数每次开辟一个节点, 那么如果尾指针指向的地方为NULL, 说明没有节点, 那么就让头指针和尾指针都指向第一个节点, 那么反之, 如果有节点的话, 我们只需要让尾指针的next指向这个节点, 并且让新节点成为尾指针.最后size++.

注意: 这里不可以使用pq->tail == pq->haed 来判断是否队列为NULL, 因为如果有一个节点, 或者队列已满它们两个仍然指向同一个节点

  • 出队列
void QueuePop(Queue* pq)
{
	assert(pq!=NULL);//条件为真,程序继续
	assert(pq->size!=0); //条件为真,程序继续

	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}

这个出队列首先需要断言结构体不可以为NULL, 这里assert(pq)和assert(pq!=NULL)表示的是一个意思,因为空就表示假, 非空就表示真, 这里写出来是便于理解,assert()断言表示, 条件为真则程序继续执行, 如果条件为假则程序中断
接着, 出队列,里面当然还需要有数据, 所以pq->size!=0这个条件也必须为真.
如果只有一个节点的话不要忘记了把尾指针也置为NULL,否则尾指针会变成野指针, 如果有多个节点, 先保存下一个节点地址,然后在进行free.最后size–.

  • 判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
}

这里有了size这个变量我们只需要判断size是否为0即可.

  • 查看数据个数
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

直接返回size,因为size记录的就是数据的个数, 也规避了遍历查找元素个数.

  • 返回头节点数据
  • 返回尾节点数据
DataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);
	return pq->phead->val;
}
DataType QueueTail(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);
	return pq->ptail->val;
}

最后两个方法也非常简单, 只要存在, 我们直接返回所需节点的数据即可.

第三步:

测试,在test.c中测试我们的代码

#include"Queue.h"

int main()
{
	Queue pq;
	QueueInit(&pq);

	QueuePush(&pq, 2);
	QueuePush(&pq, 3);
	printf("%d ", QueueFront(&pq));
	QueuePop(&pq);

	printf("%d ", QueueFront(&pq));
	QueuePop(&pq);

	QueueDestroy(&pq);
	return 0;
}

在这里插入图片描述

当然运行程序结构是没有问题的, 也可以循环出队列,进行测试代码

3. OJ题目解析

题目链接: 用队列实现栈

题目描述:

在这里插入图片描述
原始模板:




typedef struct {
    
} MyStack;


MyStack* myStackCreate() {
    
}

void myStackPush(MyStack* obj, int x) {
    
}

int myStackPop(MyStack* obj) {
    
}

int myStackTop(MyStack* obj) {
    
}

bool myStackEmpty(MyStack* obj) {
    
}

void myStackFree(MyStack* obj) {
    
}

/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();
 * myStackPush(obj, x);
 
 * int param_2 = myStackPop(obj);
 
 * int param_3 = myStackTop(obj);
 
 * bool param_4 = myStackEmpty(obj);
 
 * myStackFree(obj);
*/

思路分析:

首先这道题需要我们使用两个队列来完成栈的实现, 这里我们的思路是, 栈的要求是后进先出, 而队列是先进先出, 让两个队列来回导数据, 插入数据时, 插入到不为空的队列中, 如果需要出数据, 先让不为空的队列的前n-1个数据导入到为空的队列中, 然后在出数据, 此时正好就是最后一个数据, 也就是后入先出, 如图

例如, 数据入队列q1, q2为空, 都为空的情况下, 入哪个都行, 假设q1不为空, 然后让q2一直保持空.
在这里插入图片描述

出数据, 先把q1的前n-1个数据导入到q2拿到q1最后一个数据,并且pop掉.
在这里插入图片描述
以此类推,保存一个存数据, 一个为空, 入数据不为空的队列, 出数据通过空的导一下.

在这里插入图片描述
步骤如下

  1. 因为C语言没有自带的队列, 所以我们需要把我们实现的队列写进去, C++会自带的队列.这里我们直接导入
  2. 创建MyStack,里面需要两个队列, myStackCreate其实也就是我们的初始化, 这里不可以直接 MyStack s, 因为函数里面的创建的变量出了函数就被释放了 ,所以我们需要动态开辟空间. 分别进行初始化
typedef struct {
    Queue q1;
    Queue q2;
} MyStack;


MyStack* myStackCreate() {
    MyStack* s = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&s->q1);
    QueueInit(&s->q2);
    return s;
}
  1. 入栈, 哪个不为空, 我们就把元素插入到哪个队列, 这里我使用了假设法, 来找出不为空的队列.
void myStackPush(MyStack* obj, int x) {
     assert(obj);
    //假设法
    Queue* Empty = &obj->q1;
    Queue* nonEmpty = &obj->q2;
    if(QueueEmpty(&obj->q2))
    {
        Empty = &obj->q2;
        nonEmpty = &obj->q1;
    }
    //把数据插入到不为空的队列
    QueuePush(nonEmpty,x);
}
  1. 出栈, 还是假设法, 找到不为空的队列, 然后通过为空的队列导一下,不要忘记找到数据之后, 让最后一个数据出队列.
int myStackPop(MyStack* obj) {
    assert(obj);
    Queue* Empty = &obj->q1;
    Queue* nonEmpty = &obj->q2;
    if(QueueEmpty(&obj->q2))
    {
        Empty = &obj->q2;
        nonEmpty = &obj->q1;
    }
    //把不为空的队列的数据的前n-1个数据导入到为空的队列
    while(QueueSize(nonEmpty)>1)
    {
        QueuePush(Empty,QueueFront(nonEmpty));
        QueuePop(nonEmpty);
    }
    int top = QueueTail(nonEmpty);
    QueuePop(nonEmpty);
    return top;
}
  1. 剩下的几个方法总体来说比较简单, 代码如下,注意销毁操作, 我们手动开辟的空间都需要手动释放.
int myStackTop(MyStack* obj) {
    assert(obj);
    if(!QueueEmpty(&obj->q1))
    {
        return QueueTail(&obj->q1);
    }
    else{
        return QueueTail(&obj->q2);
    }

}

bool myStackEmpty(MyStack* obj) {
    return (QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2));
}

void myStackFree(MyStack* obj) {
    assert(obj);
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
}

全部代码如下:

#pragma once

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

typedef int DataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	DataType val;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);

//队尾插入
void QueuePush(Queue* pq, DataType x);
//队头删除
void QueuePop(Queue* pq);

//判空
bool QueueEmpty(Queue* pq);
//获取节点个数
int QueueSize(Queue* pq);

//获取头节点
DataType QueueFront(Queue* pq);
//获取尾节点
DataType QueueTail(Queue* pq);

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

void QueuePush(Queue* pq, DataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");//perror函数在<stdio.h>头文件包含,标准错误流
		return;
	}
	newnode->next = NULL;
	newnode->val = x;

	if (pq->ptail == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}

void QueuePop(Queue* pq)
{
	assert(pq!=NULL);//条件为真,程序继续
	assert(pq->size!=0); //条件为真,程序继续

	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
}

int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

DataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);
	return pq->phead->val;
}

DataType QueueTail(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);
	return pq->ptail->val;
}
typedef struct {
    Queue q1;
    Queue q2;
} MyStack;


MyStack* myStackCreate() {
    MyStack* s = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&s->q1);
    QueueInit(&s->q2);
    return s;
}

void myStackPush(MyStack* obj, int x) {
     assert(obj);
    //假设法
    Queue* Empty = &obj->q1;
    Queue* nonEmpty = &obj->q2;
    if(QueueEmpty(&obj->q2))
    {
        Empty = &obj->q2;
        nonEmpty = &obj->q1;
    }
    //把数据插入到不为空的队列
    QueuePush(nonEmpty,x);
}

int myStackPop(MyStack* obj) {
    assert(obj);
    Queue* Empty = &obj->q1;
    Queue* nonEmpty = &obj->q2;
    if(QueueEmpty(&obj->q2))
    {
        Empty = &obj->q2;
        nonEmpty = &obj->q1;
    }
    //把不为空的队列的数据的前n-1个数据导入到为空的队列
    while(QueueSize(nonEmpty)>1)
    {
        QueuePush(Empty,QueueFront(nonEmpty));
        QueuePop(nonEmpty);
    }
    int top = QueueTail(nonEmpty);
    QueuePop(nonEmpty);
    return top;
}

int myStackTop(MyStack* obj) {
    assert(obj);
    if(!QueueEmpty(&obj->q1))
    {
        return QueueTail(&obj->q1);
    }
    else{
        return QueueTail(&obj->q2);
    }

}

bool myStackEmpty(MyStack* obj) {
    return (QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2));
}

void myStackFree(MyStack* obj) {
    assert(obj);
    QueueDestroy(&obj->q1);
    QueueDestroy(&obj->q2);
    free(obj);
}

/**
 * Your MyStack struct will be instantiated and called as such:
 * MyStack* obj = myStackCreate();
 * myStackPush(obj, x);
 
 * int param_2 = myStackPop(obj);
 
 * int param_3 = myStackTop(obj);
 
 * bool param_4 = myStackEmpty(obj);
 
 * myStackFree(obj);
*/

4. 总结

队列是一种常见的数据结构,它遵循先进先出(FIFO)的原则。队列的主要操作包括入队(将元素添加到队列的末尾)和出队(将队列的首个元素移除)。队列还可以支持其他一些操作,如查看队列首个元素和判断队列是否为空。

队列的应用十分广泛,例如在多线程编程中可以用队列来实现线程间的通信,处理任务和数据的排序,网络传输中的消息队列等。

队列可以使用数组或链表来实现。使用数组实现队列时,需要考虑队列大小的限制,当队列已满时需要进行扩容操作。使用链表实现队列时,可以避免扩容的问题,但需要维护队列的头尾指针。

在时间复杂度方面,队列的入队和出队操作的平均时间复杂度为O(1)。但在使用数组实现队列且需要扩容时,入队操作的最坏时间复杂度为O(n)。

总结来说,队列是一种简单而有用的数据结构,适用于需要先进先出顺序的场景。它的实现方式多样,使用数组或链表都可以。在设计和实现队列时,需要考虑队列大小的限制以及扩容问题。



如果此文对您有帮助, 期待您的关注与点赞, 期待共同进步!!!

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

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

相关文章

【干货】西班牙跨境电商Miravia测评开店可以销售哪些产品类目?如何精养买家号矩阵补单?

西班牙(Reino de Espaa,,Spain)地理位置优越&#xff0c;位于欧洲、非洲和中东的交汇处。入驻的主流电商平台有亚马逊&#xff0c;阿里巴巴速卖通&#xff0c;Ebay&#xff0c;TikTok等&#xff0c; 同时还有西班牙本土电商Mediamarkt&#xff0c;El Corte Ingls&#xff0c;c…

彩虹pdm系统的BOM协同管理方案

彩虹pdm系统的BOM协同管理方案 彩虹PDM系统的BOM协同管理方案旨在为企业提供高效、准确且协同的物料清单&#xff08;Bill of Materials&#xff0c;简称BOM&#xff09;管理解决方案。以下是该方案的详细介绍&#xff1a; 一、BOM数据全面管理 彩虹PDM系统以产品结构为中心…

中药保护品种目录数据库

中药作为中华民族的瑰宝&#xff0c;不仅在中国有着悠久的历史和深厚的文化底蕴&#xff0c;在国际上也逐渐受到重视。为了保护中药品种的独特性和促进其可持续发展&#xff0c;中国政府实施了中药品种保护制度&#xff0c;据药融云数据库显示&#xff0c;中药保护品种数量多达…

创新方便的在线评审软件——原理图设计的利器

概述 在当今数字化时代&#xff0c;原理图设计已成为许多领域中不可或缺的一环。然而&#xff0c;对于设计师们来说&#xff0c;如何高效地进行评审却是一个挑战。 传统的评审方式往往需要多人齐聚一堂&#xff0c;耗时耗力。设计师们需要亲自填写评审意见&#xff0c;这不仅…

Unity WebGL全屏显示

一、删除footer节点 二、删除最下面点击事件绑定 修改Canvas宽高 canvas.style.width "960px"; canvas.style.height "600px"; 改成 canvas.style.width document.documentElement.clientWidth"px"; canvas.style.height document.document…

JavaScript-BOM编程

BOM对象 1 什么是BOM2 window对象的常见属性3 window对象的常见方法4 通过BOM编程控制浏览器行为演示5 通过BOM编程实现会话级和持久级数据存储 1 什么是BOM BOM是Browser Object Model的简写&#xff0c;即浏览器对象模型。 BOM由一系列对象组成&#xff0c;是访问、控制、修…

Coursera吴恩达深度学习专项课程01: Neural Networks and Deep Learning 学习笔记 Week 02

Week 02 of Neural Networks and Deep Learning Course Certificate 本文是学习 https://www.coursera.org/learn/neural-networks-deep-learning 这门课的笔记 Course Intro 文章目录 Week 02 of Neural Networks and Deep LearningLearning Objectives [1] Logistic Regres…

LINUX 精通 1——2.1.1 网络io与io多路复用select/poll/epoll

LINUX 精通 1 day12 20240509 算法刷题&#xff1a; 2道高精度 耗时 107min 课程补20240430 耗时&#xff1a;99 min day 13 20240512 耗时&#xff1a;200min 课程链接地址 前言 杂 工作5-10年 够用 费曼&#xff1a;不要直接抄&#xff0c;自己写&#xff1b;不要一个…

抖音爆火的QQ价格评估源码

内容目录 一、详细介绍二、效果展示1.部分代码2.效果图展示 三、学习资料下载 一、详细介绍 抖音爆火的QQ价格评估前端源码 最近抖音很火直播给别人测qq价值多少&#xff0c;这个源码只有前端&#xff0c; 包含激活码验证页&#xff0c;评估页&#xff0c;自行研究吧 二、效…

AI预测福彩3D采取887定位大底=23策略+杀断组+杀组选+杀和尾+杀和值012缩水测试5月15日预测第1弹

昨天与一位玩3D的彩友通过视频直播的形式聊了下&#xff0c;受益匪浅&#xff0c;给我提供了一些比较有价值的建议&#xff0c;比如&#xff0c;对于887的定位策略&#xff0c;方向是没问题的&#xff0c;但是8873的话&#xff0c;还是缺乏一定的命中率&#xff0c;如果88723&a…

海外静态IP:全球互联的稳定之选

在全球化的商业环境中&#xff0c;企业与个人对于网络的依赖日益加深&#xff0c;而一个稳定、可靠的海外静态IP成为了连接世界的关键。本文将从五个方面深入探讨海外静态IP的重要性、应用场景、技术优势、市场趋势以及选择时的考量因素。 一、海外静态IP的重要性 静态IP地址是…

微信小程序如何使用weui组件库?

一、方法一&#xff1a;通过npm安装 通过npm构建方式引入weui组件库 &#xff08;找到.eslintrc.js 右键&#xff0c;在内件终端打开&#xff09;打开命令提示符后&#xff0c;输入 &#xff08;1&#xff09;npm init -y来快速生成一个默认的package.json文件 &#xff08;…

nmap端口扫描工具——Win10

Nmap安装流程&#xff1a; Nmap官网链接&#xff1a;Download the Free Nmap Security Scanner for Linux/Mac/Windows Nmap参考文档链接&#xff1a;https://nmap.org/book/toc.html Nmap使用流程&#xff1a; Nmap软件主页面&#xff1a; 中文版&#xff1a; nmap -T4 -…

代码行数统计工具cloc

Release v2.00 AlDanial/cloc GitHub 代码量代码行数统计工具cloc的正确使用(windows平台亲测有效&#xff0c;本人踩过坑&#xff0c;文中提到&#xff01;)_cloc代码统计工具-CSDN博客

HTTP客户端手动解析响应体数据

服务端 package mainimport ("easyGo/person""encoding/json""net/http" )func main() {http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) {p : &person.Person{Name: "jackie",Age: 30,T: pe…

企业数字化转型规划设计(107页PPT)

一、资料介绍 《企业数字化转型规划设计》这份详尽的107页PPT资料&#xff0c;是对企业数字化转型的全方位、系统性规划设计的深度解读。该资料从数字化转型的必要性、现状分析、目标设定、实施路径、技术支持到风险评估等多个维度&#xff0c;为企业提供了一套完整且实用的操…

基于火山引擎云搜索的混合搜索实战

在搜索应用中&#xff0c;传统的 Keyword Search 一直是主要的搜索方法&#xff0c;它适合精确匹配查询的场景&#xff0c;能够提供低延迟和良好的结果可解释性&#xff0c;但是 Keyword Search 并没有考虑上下文信息&#xff0c;可能产生不相关的结果。最近几年&#xff0c;基…

三类银行互联网贷款迎新规!速看菊风信贷智能双录解决方案

​​继2020年《商业银行互联网贷款管理暂行办法》&#xff08;下称《办法》&#xff09;和2021年《关于进一步规范商业银行互联网贷款业务的通知》之后&#xff0c;时隔三年&#xff0c;股份制银行、城商行、民营银行&#xff08;简称“三类银行”&#xff09;互联网贷款业务再…

Advanced RAG 07:在RAG系统中进行表格数据处理的新思路

编者按&#xff1a; 目前&#xff0c;检索增强生成&#xff08;RAG&#xff09;系统成为了将海量知识赋能于大模型的关键技术之一。然而,如何高效地处理半结构化和非结构化数据&#xff0c;尤其是文档中的表格数据&#xff0c;仍然是 RAG 系统面临的一大难题。 本文作者针对这一…