第2章 双向链表

news2024/12/28 19:06:00

双向链表

概念

对链表而言,双向均可遍历是最方便的,另外首尾相连循环遍历也可大大增加链表操作的便捷性。因 此,双向循环链表,是在实际运用中是最常见的链表形态。

基本操作

与普通的链表完全一致,双向循环链表虽然指针较多,但逻辑是完全一样。

基本的操作包括:

1. 节点设计

2. 初始化空链表

3. 增删节点

4. 链表遍历

5. 销毁链表

节点设计

双向链表的节点只是比单向链表多了一个前向指针。示例代码如下所示:

typedef int DATA;
typedef struct node
{
// 以整型数据为例
DATA data;
// 指向相邻的节点的双向指针
struct node *prev;
struct node *next;
}NODE;

初始化

所谓初始化,就是构建一条不含有效节点的空链表。 以带头结点的双向循环链表为例,初始化后,其状态如下图所示:

在初始空链表的情况下,链表只有一个头结点,下面是初始化示例代码:

int dlist_create(NODE** head,DATA data)
{
// 创建新节点(申请内存空间)
NODE *pNew = (NODE*)malloc(sizeof(NODE));
if(!pNew)
return -1;
// 给节点赋初值
pNew -> data = data;
// 前后指针默认都指向NULL
pNew -> prev = pNew -> next = NULL;
// 将新节点作为头节点
*head = pNew;
return 0;
}

插入节点

与单链表类似,也可以对双链表中的任意节点进行增删操作,常见的有所谓的头插法、尾插法等, 即:将新节点插入到链表的首部或者尾部,示例代码是:

头插法:将新节点插入到链表的头部

// 将新节点pNew,插入到链表的首部
int dlist_addHead(NODE** head,DATA data)
{
// 创建新节点并申请内存
NODE *pNew = (NODE*)malloc(sizeof(NODE));
if(!pNew)
return -1;
// 给新节点赋值
pNew -> data = data;
pNew -> prev = NULL;
// 后针指向头指针
pNew -> next = *head;
// 如果头指针存在
if(*head)
// 头指针的前指针指向新节点
(*head) -> prev = pNew;
// 新插入的节点作为新的头节点
*head = pNew;
return 0;
}
// 将新节点pNew,插入到链表的尾部
int dlist_addTail(NODE** head,DATA data)
{
// 创建节点并申请内存
NODE *pNew = (NODE*)malloc(sizeof(NODE));
if(!pNew)
return -1;
// 初始化节点
pNew -> data = data;
pNew -> prev = NULL;
pNew -> next = NULL;
// 用来记录尾节点,默认头节点就是尾节点
NODE* p = *head;
if(!p)
{
// 头节点不存在,新插入的节点作为头节点
*head = pNew;
return 0;
}
// 通过循环,查找尾节点
while(p -> next)
{
p = p -> next;
}
// 尾节点的后指针指向新插入的节点
p -> next = pNew;
// 新插入的节点的前指针指向尾节点
pNew -> prev = p;
// 此时的新节点作为了新的尾节点
return 0;
}

中间插法:将新节点插入到链表的指定位置

// 将新节点pNew,插入到链表的指定位置
int dlist_insert(NODE** head,DATA pos,DATA data)
{
NODE *pNew = (NODE*)malloc(sizeof(NODE));
if(!pNew)
return -1;
pNew -> data = data;
pNew -> prev = NULL;
pNew -> next = NULL;
NODE* p = *head, *q = NULL;
if(!p)
{
*head = pNew;
return 0;
}
if(memcmp(&(p -> data),&pos,sizeof(DATA)) == 0)
{
pNew -> next = p;
p -> prev = pNew;
*head = pNew;
return 0;
}
while(p)
{
if(memcmp(&(p -> data),&pos,sizeof(DATA)) == 0)
{
pNew -> next = p;
pNew -> prev = q;
p -> prev = pNew;
q -> next = pNew;
return 0;
}
q = p;
p = p -> next;
}
q -> next = pNew;
pNew -> prev = q;
return 0;

剔除节点

注意,从链表中将一个节点剔除出去,并不意味着要释放节点的内容。当然,我们经常在剔除了一个 节点之后,紧接着的动作往往是释放它,但是将“剔除”与“释放”两个动作分开,是最基本的函数封装的 原则,因为它们虽然常常连在一起使用,但它们之间并无必然联系,例如:当我们要移动一个节点的 时候,实质上就是将“剔除”和“插入”的动作连起来,此时就不能释放该节点了。

在双向链表中剔除指定节点的示例代码如下:

// 将data对应的节点从链表中剔除
int dlist_delete(NODE** head,DATA data)
{
NODE* p = *head;
if(!p)
return -1;
if(memcmp(&(p -> data),&data,sizeof(DATA)) == 0)
{
if(p -> next == NULL)
{
*head = NULL;
free(p);
return 0;
}
*head = p -> next;
p -> next -> prev = NULL;
free(p);
return 0;
}
while(p)
{
if(memcmp(&(p -> data),&data,sizeof(DATA)) == 0)
{
p -> prev -> next = p -> next;
if(p -> next == NULL)
p -> prev -> next = NULL;
else
p -> next -> prev = p -> prev;
free(p) ;
return 0;
}
p = p -> next;
}
return -1;
}

链表的遍历

对于双向循环链表,路径可以是向后遍历,也可以向前遍历。

下面是根据指定数据查找节点,向前、向后遍历的示例代码,假设遍历每个节点并将其整数数据输 出:

// 根据指定数据查找节点
NODE* dlist_find(const NODE* head,DATA data)
{
const NODE* p = head;
while(p)
{
if(memcmp(&(p -> data),&data,sizeof(DATA)) == 0)
return (NODE*)p;
p = p -> next;
}
return NULL;
}
// 向前|向后遍历
void dlist_showAll(const NODE* head)
{
const NODE* p = head;
while(p)
{
printf("%d ",p -> data);
p = p -> next;// 向后遍历
// p = p -> prev;// 向前遍历
}
printf("\n");
}

修改链表

我们也可以针对链表中的数据进行修改,只需要提供一个修改的源数据和目标数据即可。 示例代码如下:

int dlist_update(const NODE* head,DATA old,DATA newdata)
{
NODE* pFind = NULL;
if(pFind = dlist_find(head,old))
{
pFind -> data = newdata;
return 0;
}
return -1;
}

销毁链表

由于链表中的各个节点被离散地分布在各个随机的内存空间,因此销毁链表必须遍历每一个节点,释 放每一个节点。

注意:

销毁链表时,遍历节点要注意不能弄丢相邻节点的指针

示例代码如下:

void dlist_destroy(NODE** head)
{
NODE *p = *head, *q = NULL;
while(p)
{
q = p;
p = p -> next;
free(q);
}
*head = NULL;
}

完整案例

dlist.h

#ifndef __DLIST_H
#define __DLIST_H
typedef int DATA;
typedef struct node
{
DATA data;
struct node *prev;// 前驱指针
struct node *next;// 后继指针
}NODE;
// 创建链表(初始化)
int dlist_create(NODE**,DATA);
// 向链表插入数据(头插法)
int dlist_addHead(NODE** head,DATA data);
// 向链表插入数据(尾插法)
int dlist_addTail(NODE** head,DATA data);
// 向链表插入数据(中间插法)
int dlist_insert(NODE** head,DATA pos,DATA data);
// 链表数据查询
NODE* dlist_find(const NODE* head,DATA data);
// 链表数据更新
int dlist_update(const NODE* head,DATA old,DATA newdata);
// 链表数据遍历
void dlist_showAll(const NODE* head);
// 链表数据删除
int dlist_delete(NODE** head,DATA data);
// 链表回收
void dlist_destroy(NODE** head);
#endif
#include "dlist.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int dlist_create(NODE** head,DATA data)
{
NODE *pNew = (NODE*)malloc(sizeof(NODE));
if(!pNew)
return -1;
pNew -> data = data;
pNew -> prev = pNew -> next = NULL;
*head = pNew;
return 0;
}
int dlist_addHead(NODE** head,DATA data)
{
NODE *pNew = (NODE*)malloc(sizeof(NODE));
if(!pNew)
return -1;
pNew -> data = data;
pNew -> prev = NULL;
pNew -> next = *head;
if(*head)
(*head) -> prev = pNew;
*head = pNew;
return 0;
}
int dlist_addTail(NODE** head,DATA data)
{
NODE *pNew = (NODE*)malloc(sizeof(NODE));
if(!pNew)
return -1;
pNew -> data = data;
pNew -> prev = NULL;
pNew -> next = NULL;
NODE* p = *head;
if(!p)
{
*head = pNew;
return 0;
}
while(p -> next)
{
p = p -> next;
}
p -> next = pNew;
pNew -> prev = p;
return 0;
}
int dlist_insert(NODE** head,DATA pos,DATA data)
{
NODE *pNew = (NODE*)malloc(sizeof(NODE));
if(!pNew)
return -1;
pNew -> data = data;
pNew -> prev = NULL;
pNew -> next = NULL;
NODE* p = *head, *q = NULL;
if(!p)
{
*head = pNew;
return 0;
}
if(memcmp(&(p -> data),&pos,sizeof(DATA)) == 0)
{
pNew -> next = p;
p -> prev = pNew;
*head = pNew;
return 0;
}
while(p)
{
if(memcmp(&(p -> data),&pos,sizeof(DATA)) == 0)
{
pNew -> next = p;
pNew -> prev = q;
p -> prev = pNew;
q -> next = pNew;
return 0;
}
q = p;
p = p -> next;
}
q -> next = pNew;
pNew -> prev = q;
return 0;
}
int dlist_delete(NODE** head,DATA data)
{
NODE* p = *head;
if(!p)
return -1;
if(memcmp(&(p -> data),&data,sizeof(DATA)) == 0)
{
if(p -> next == NULL)
{
*head = NULL;
free(p);
return 0;
}
*head = p -> next;
p -> next -> prev = NULL;
free(p);
return 0;
}
while(p)
{
if(memcmp(&(p -> data),&data,sizeof(DATA)) == 0)
{
p -> prev -> next = p -> next;
if(p -> next == NULL)
p -> prev -> next = NULL;
else
p -> next -> prev = p -> prev;
free(p) ;
return 0;
}
p = p -> next;
}
return -1;
}
NODE* dlist_find(const NODE* head,DATA data)
{
const NODE* p = head;
while(p)
{
if(memcmp(&(p -> data),&data,sizeof(DATA)) == 0)
return (NODE*)p;
p = p -> next;
}
return NULL;
}
int dlist_update(const NODE* head,DATA old,DATA newdata)
{
NODE* pFind = NULL;
if(pFind = dlist_find(head,old))
{
pFind -> data = newdata;
return 0;
}
return -1;
}
void dlist_destroy(NODE** head)
{
NODE *p = *head, *q = NULL;
while(p)
{
q = p;
p = p -> next;
free(q);
}
*head = NULL;
}
void dlist_showAll(const NODE* head)
{
const NODE* p = head;
while(p)
{
printf("%d ",p -> data);
p = p -> next;
}
printf("\n");
}

dlist_main.c

#include "dlist.h"
#include <stdio.h>
#define OP 2
int main(void)
{
NODE* head = NULL;
int a[] = {1,3,5,7,9};
int n = sizeof a / sizeof a[0];
register int i = 0;
for(; i < n; i++)
dlist_addTail(&head,a[i]);
dlist_showAll(head);
while(1)
{
#if (OP == 0)
DATA data;
NODE *pFind = NULL;
printf("请输入要查找的数据(-1 退出):");
scanf("%d",&data);
if(data == -1)
break;
if(!(pFind = dlist_find(head,data)))
{
puts("查找的数据不存在,请重试...");
continue;
}
printf("在内存地址为 %p 的内存空间中找到了 %d\n",&(pFind->data),pFind->data);
#elif (OP == 1)
DATA data;
NODE *pFind = NULL;
printf("请输入要插入位置的数据(-1 退出):");
scanf("%d",&data);
if(data == -1)
break;
if(dlist_insert(&head,data,407))
{
puts("插入失败,请重试...");
continue;
}
dlist_showAll(head);
#else
DATA data;
NODE *pFind = NULL;
printf("请输入要删除位置的数据(-1 退出):");
scanf("%d",&data);
if(data == -1)
break;
if(dlist_delete(&head,data))
{
puts("删除失败,请重试...");
continue;
}
dlist_showAll(head);
#endif
}
dlist_destroy(&head);
puts("=====回收后====");
dlist_showAll(head);
return 0;
}

循环双向链表【扩展】

案例代码

循环双向链表是在双向链表的基础上进行了改动,整体改动不是很大,请看下面代码:

dclist.h

#ifndef __DCLIST_H
#define __DCLIST_H
typedef int DATA;
typedef struct node
{
DATA data;
struct node *prev;
struct node *next;
}NODE;
int dclist_add(NODE** head,DATA data);
int dclist_delete(NODE** head,DATA data);
NODE* dclist_find(const NODE* head,DATA data);
int dclist_update(const NODE* head,DATA old,DATA newdata);
void dclist_showAll(const NODE* head);
void dclist_destroy(NODE** head);
#endif

dclist.c

#include "dclist.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static NODE* dclist_create_node(DATA data)
{
NODE* p = (NODE*)malloc(sizeof(NODE));
if(!p)
return NULL;
p -> data = data;
p -> prev = p;
p -> next = p;
return p;
}
/*
@argument: new: 待插入的节点
@argument: prev: 插入后节点的上一个节点
@argument: next: 插入后节点的下一个节点
*/
static void __dclist_add(NODE* new, NODE* prev,NODE* next )
{
new -> prev = prev;
new -> next = next;
prev -> next = new;
next -> prev = new;
}
int dclist_add(NODE** head,DATA data)
{
if(*head == NULL)
{
*head = dclist_create_node(data);
int ret = (*head)?0:-1;
return ret ;
}
NODE *new = dclist_create_node(data);
if(!new)
return -1;
__dclist_add(new,(*head)->prev,*head);
return 0;
}
static void __dclist_delete(NODE* del, NODE* prev,NODE* next )
{
prev -> next = next;
next -> prev = prev;
free(del);
}
int dclist_delete(NODE** head,DATA data)
{
if(*head == NULL)
return -1 ;
NODE *del = dclist_find(*head,data);
if(!del)
return -1;
NODE* p = *head;
if(del == p) //删除的是头节点
{
if(p -> next == p) // 唯一的头节点
{
*head = NULL;
free(p);
return 0;
}
p -> prev -> next = p -> next;
p -> next -> prev = p -> prev;
*head = p -> next;
free(p);
return 0;
}
__dclist_delete(del,del->prev,del->next);
return 0;
}
NODE* dclist_find(const NODE* head,DATA data)
{
const NODE* p = head;
while(p)
{
if(memcmp(&(p -> data),&data,sizeof(DATA)) == 0)
return (NODE*)p;
p = p -> next;
if(p == head)
break;
}
return NULL;
}
int dclist_update(const NODE* head,DATA old,DATA newdata)
{
NODE* pFind = dclist_find(head,old);
if(!pFind)
return -1;
pFind -> data = newdata;
return 0;
}
void dclist_showAll(const NODE* head)
{
const NODE* p = head;
while(p)
{
printf("%d ",p -> data);
p = p -> next;
if(p == head)
break;
}
printf("\n");
}
void dclist_destroy(NODE** head)
{
if(*head == NULL)
return ;
NODE* p = *head,*q = NULL;
p -> prev -> next = NULL;
while(p)
{
q = p ;
p = p -> next;
free(q);
if(p == *head)
break;
}
*head = NULL;
}

dclist_main.c

#include <stdio.h>
#include "dclist.h"
#define DELETE
int main(void)
{
NODE* head = NULL;
int a[] = {1,3,5,7,9,11,13};
int n = sizeof a / sizeof a[0];
register int i = 0;
for(; i < n ; i++)
dclist_add(&head,a[i]);
dclist_showAll(head);
DATA data ;
while(1)
{
#ifdef DELETE
printf("请输入要删除的数据:");
scanf("%d",&data);
if(data == -1)
break;
if(dclist_delete(&head,data) < 0)
{
puts("删除失败,请重试");
continue;
}
dclist_showAll(head);
#else
NODE *pFind = NULL;
printf("请输入要查找的数据:");
scanf("%d",&data);
if(data == -1)
break;
if(!(pFind = dclist_find(head,data)))
{
puts("查找的数据不存在,请重试");
continue;
}
printf("查找数据:%d 内存地址:%p\n",pFind -> data, &(pFind -> data));
#endif
}
dclist_destroy(&head);
puts("====销毁后=====");
dclist_showAll(head);
return 0;
}

适用场合

经过单链表、双链表的学习,可以总结链表的适用场合:

适合用于节点数目不固定,动态变化较大的场合

适合用于节点需要频繁插入、删除的场合 适合用于对节点查找效率不十分敏感的场合

内存监测(valgrind)

安装: sudo apt-get install valgrind
格式: valgrind [options] prog-and-args
例子: valgrind --tool=memcheck --leak-check=full --log-file=./result.txt ./dlist
注意: 在使用valgrind 工具进行内存监测时,要求可执行程序在编译时,要为gcc 编译器指定 -g 选项;

章节作业

(双向循环链表)

【1】用双向循环链表存储若干自然数,并将其奇偶重排输出。

比如:

链表中存储: 1 2 3 4 5 6 7 8 9 …

将其重排成: 1 3 5 7 9 … 8 6 4 2 (奇数升序偶数降序)

解析 从右到左,从最末一个元素开始往前遍历;遇到奇数记下位置,遇到偶数则移动到链表的末尾。

示例代码

// 双向循环链表:奇偶数重排
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
// 链表节点
typedef struct DNode
{
int data;
struct DNode *prior;
struct DNode *next;
}DNode, *DLinkList;
void perror_exit(const char *info)
{
perror(info);
exit(0);
}
/*************************
initialize the list
**************************/
void init_list(DLinkList *p2head)
{
if((*p2head=(DNode *)malloc(sizeof(DNode))) == NULL){
perror("malloc faild");
exit(1);
}
(*p2head)->prior = (*p2head)->next = *p2head;
}
/****************
insert node
*****************/
void insert(DLinkList head, int num)
{
DLinkList pnew;
if((pnew=(DNode *)malloc(sizeof(DNode))) == NULL)
perror_exit("malloc failed");
pnew->data = num;
/***********************************************
insert the new node onto the tail
***********************************************/
pnew->prior = head->prior;
head->prior->next = pnew;
pnew->next = head;
head->prior = pnew;
}
/********************
change the list
*********************/
void rerange(DLinkList old_list, DLinkList new_list)
{
DLinkList p = old_list->next;
//check the next two elements, prevent recycling
while((p->next!=old_list) && (p->next->next!=old_list)){
insert(new_list, p->data);
p = p->next->next;
}
//the list endup with an odd number
if(p->next == old_list){
insert(new_list, p->data);
p = p->prior;
if(p != old_list){
while(p->prior->prior!=old_list){
insert(new_list, p->data);
p = p->prior->prior;
}
insert(new_list, p->data);
}
}
//the list endup with an even number
else{
insert(new_list, p->data);
p = p->next;
insert(new_list, p->data);
while(p->prior->prior!=old_list){
p = p->prior->prior;
insert(new_list, p->data);
}
}
}
/*************
show list
**************/
void show(DLinkList head)
{
DNode *p=head->next;
while(1){
printf("%d ", p->data);
if(p->next == head)
break;
p = p->next;
}
printf("\n");
}
int main(int argc, char **argv)
{
int n;
scanf("%d", &n); // 输入所需链表的长度
// 创建空链表
DLinkList head;
init_list(&head);
// 依次插入节点
int i;
for(i=n; i>0; --i)
{
insert(head, n-i+1);
}
show(head);
/* arange the list */
DLinkList new_list;
init_list(&new_list);
rerange(head, new_list);
show(new_list);
return 0;
}

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

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

相关文章

PHP房屋出售出租多端多平台预约系统小程序源码

&#x1f3e0;&#x1f511;「房屋出售出租多端运营系统」——房产管理新纪元&#xff0c;一键掌控所有&#xff01;&#x1f680; &#x1f3e1; 开篇直击&#xff1a;房产市场新利器&#xff0c;轻松管理不再是梦&#xff01; 亲们&#xff0c;还在为房屋出售或出租的繁琐流…

【IC设计】跨时钟异步处理系列——单比特跨时钟

文章目录 建立时间和保持时间单比特信号的跨时钟处理慢时钟域的信号传输到快时钟域打两拍 快时钟域的信号传输到慢时钟域方案一 脉冲展宽同步 (打拍打拍&#xff0c;进行或)代码原理图 方案二 脉冲电平检测双触发器同步边沿检测代码原理图 建立时间和保持时间 所谓的建立时间或…

python可视化-漏斗图(转化分析)

1、数据生成 from scipy import stats# 构造数据 stage [浏览,加入购物车,下单,支付,交易成功] values [] for i in range(len(stage)):values.append(int(1000*stats.expon.pdf(i, scale0.95))) 2、基于漏斗图的转化分析 from pyecharts import options as opts from pye…

HarmonyOs如何获取rawfile目录下的所有文件列表

最近在做一个功能&#xff0c;需要使用获取rawfile下目录的所有文件 参考连接为&#xff1a; zRawfile-模块-C API-Localization Kit&#xff08;本地化开发服务&#xff09;-应用框架 - 华为HarmonyOS开发者 (huawei.com) 需要使用到native实现&#xff0c;实现步骤&#…

redis面试(二十四)Semaphore锁实现

Semaphore也是redis分布式锁支持的一种&#xff0c;同步组件 之前给大家的讲解的锁&#xff0c;基本上都是同时间只能一个客户端获取这个锁&#xff0c;然后做一些事情&#xff0c;处理完了以后释放锁 Semaphore&#xff0c;信号量&#xff0c;他作为一个锁机制&#xff0c;可以…

Charles苹果手机https抓包

1、电脑设置Charles代理端口 1)设置代理端口 Proxy-》Proxying Settings-》HTTP Proxy 设置端口 2)设置监控的代理地址 Proxy-》SSL Proxying Settings 添加Add允许所有地址*.* 2、电脑导入Charles的ssl证书 3、电脑查看Charles的IP地址和端口 4、手机无线wifi配置代理 5、手…

kubernetes HPA

K8S的HPA介绍 在Kubernetes&#xff08;K8S&#xff09;集群管理中&#xff0c;Horizontal Pod Autoscaler&#xff08;HPA&#xff09;是一种关键的自动扩缩容机制&#xff0c;用于根据当前负载情况自动调整Pod副本的数量。这一机制能够显著提升应用的响应能力和资源利用率&a…

虚幻5|按键触发学习

一&#xff0c;如图参考 1.下移 驱动阈值 越大按时间长才会触发&#xff0c;越小很快就可以触发 2.按下 当按下超出驱动阈值大小就会触发一次&#xff0c;这里的驱动阈值只能设置再0.1~1的大小 3.已松开 当按下的时候&#xff0c;先触发单次的started&#xff0c;如果按压…

华为OD 山峰个数 C语言实现

不知道是不是我理解错了&#xff0c;这个题目200分&#xff1f; int main(void) {int i 0;int len 0;char c \0;int data[1000] {0};int temp[1000] {-1};int top 0;int count 0;while(scanf("%d",&data[i]) 1){i;len;cgetchar();if(c \n){break;}}// …

【调研】弱网环境下分片上传方案调研

目标 在高延迟、高丢包的弱网环境下&#xff0c;合理调整上传任务中分片大小这一参数&#xff0c;以达到尽可能好的传输性能&#xff08;传输时间、成功率&#xff09;。 调研 腾讯云弱网分块续传功能 腾讯云的弱网分块续传功能&#xff0c;基于腾讯云对象存储(Cloud Object…

Redis单线程和多线程

1.Redis的单线程 Redis的单线程主要是指Redis的网络IO和键值对读写是由一个线程完成的&#xff0c;Redis在处理客户端的请求时包括获取&#xff08;Socket读&#xff09;、解析、执行、内容返回&#xff08;Socket写&#xff09;等都由一个顺序串行的主线程处理&#xff0c;这…

OpenCV绘图函数(3)判断点一条直线是否在一个矩形范围内的函数clipLine()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 裁剪线段与图像矩形相交的部分。 cv::clipLine 函数计算出完全位于指定矩形内的线段部分。如果线段完全位于矩形之外&#xff0c;则返回 false。…

实现流程化办公,可专注于开源可视化报表设计器!

近日&#xff0c;有很多粉丝和朋友们会问我们关于低代码技术平台、开源可视化报表设计器的相关内容和问题。其实&#xff0c;在流程化办公新时代&#xff0c;愿意启用新软件平台的客户朋友会收获市场红利&#xff0c;也会站在前沿之地上斩获更多市场份额。今天我们就一起来了解…

【LLM之Data】SKYSCRIPT-100M论文阅读笔记

研究背景 随着短视频和短剧的兴起&#xff0c;自动化的剧本生成和短剧制作在影视行业中的需求逐渐增加。传统的剧本生成过程需要大量的人工干预&#xff0c;限制了其在规模和效率上的扩展性。当前的大型语言模型&#xff08;LLM&#xff09;在剧本生成方面展现出一定潜力&…

Leetcode面试经典150题-5.最长回文子串

解法都在代码里&#xff0c;不懂就留言或者私信 class Solution {public static String longestPalindrome(String s) {if(s null || s.length() 0) {return null;}//加工字符串&#xff0c;例如abcdcba加工成#a#b#c#d#a#b#c#d#String str getManacherString(s);char[] str…

UWB实操:用litepoint确认UWB SP0和SP3帧长度

用litepoint确认UWB SP0和SP3帧长度 预备知识 SP0 frame length Sync+SFD = 65.1+ 8.1 = 73 us

uniapp定义scss颜色变量deep()修改子组件样式

uni.scss中 import "/common/style/base-style.scss"; 在base-style.scss中 $brand-theme-color:#28B389;//品牌主体红色$border-color:#e0e0e0 ; $border-color-light:#efefef; $text-font-color-1: #000 ;//文字主色 $text-font-color-2:#676767;//副标题颜色 $…

​​聆思CSK6大模型语音视觉开发板接入AIUI技能实操(控制风扇开关)

前言 本文以大模型语音控制灯的开关为例,讲解如何通过LSPlatform的云端编排功能在大模型语音交互流程中加入AIUI技能&#xff0c;实现更丰富的产品功能。 大模型语音模板加入AIUI技能节点后的数据流程如下图所示&#xff1a; 大模型语音控制AIUI之风扇 硬件准备工作 聆思CSK6大…

使用 FinalShell 链接 Centos

1. 安装 FinalShell 下载地址&#xff1a;https://www.hostbuf.com/t/988.html 2. 查看 IP地址。 2.1 通过命令查询IP 输入 ip addr show 查询&#xff0c;输出效果如下截图&#xff0c;其中的 192.168.1.5 就是 IP 地址。 2.2 通过可视化界面查询IP 点击右上角的网络图标…

LLM自对齐技术最新研究分享(二):对齐 Pipeline 数据合成(下)

LLM 自对齐技术最新研究进展分享 系列文章继续更新啦&#xff01;本系列文章将基于下图的架构&#xff0c;对当前 Self-alignment 相关工作进行全面梳理&#xff0c;厘清技术路线并分析潜在问题。 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 在上一篇…