线性表之单链表(C实现)

本文围绕C语言链表展开,介绍了链表的存储结构和结点类型描述,阐述了单链表的创建方法,包括建立、尾部插入、遍历、查找、插入、删除和释放等操作,还涉及链表的复杂操作,如反转、求相邻两结点最大值和有序链表合并,最后给出相关程序文件。

考察:逻辑思维能力、编程思路清晰度、对内存管理的应用、对指针的灵活运用。

一、链表及其原理

1、存储结构

在线性表L=(a0,a1,...,an−1)中,将各个元素分布在存储器的不同区域,并将这些区域称为结点。 在线性表 L=(a_0, a_1, ..., a_{n-1}) 中,将各个元素分布在存储器的不同区域,并将这些区域称为结点。在线性表L=(a0,a1,...,an1)中,将各个元素分布在存储器的不同区域,并将这些区域称为结点。
这些结点之间通过地址或指针来建立联系,以便在需要时能够快速访问和操作这些元素。 这些结点之间通过地址或指针来建立联系,以便在需要时能够快速访问和操作这些元素。 这些结点之间通过地址或指针来建立联系,以便在需要时能够快速访问和操作这些元素。

节点
结点的data于存放数据元素ai,而next域是一个指针,指向ai直接后继ai+1所在的结点。 结点的data于存放数据元素a_i,而next域是一个指针,指向a_i直接后继a_{i+1}所在的结点。 结点的data于存放数据元素ai,而next域是一个指针,指向ai直接后继ai+1所在的结点。
在这里插入图片描述

2、结点类型描述

typedef struct node
{
	data_t data;//终节点的数据域
	struct node *next;//终节点的后继指针域
}listnode, *linklist;

设P指向链表中的结点ai。设P指向链表中的结点a_i。P指向链表中的结点ai
获取ai,写作:p−>data;获取a_i,写作:p->data;获取ai,写作:p>data;
而获取ai+1,写作:p−>next−>data;而获取a_{i+1},写作:p->next->data;而获取ai+1,写作:p>next>data;
若指针p为NULL,则他不指向任何结点,此时获取p−>data或p−>next是错误的。若指针p为NULL,则他不指向任何结点,此时获取p->data或p->next是错误的。 若指针pNULL,则他不指向任何结点,此时获取p>datap>next是错误的。
可调用C语言中malloc()函数向系统申请结点的存储空间
linklist p;
p = (linklist)malloc(sizeof(listnode));
则创建一个类型为linklist的结点,且该结点的地址已存入指针变量p中;

二、单链表创建

1、建立单链表

依次读入表L=(a0,.....,an−1)中每一元素ai(假设为整型),若ai≠结束符(−1), 依次读入表L=(a_0,.....,a_{n-1})中每一元素a_i(假设为整型),若a_i≠结束符(-1),依次读入表L=(a0,.....,an1)中每一元素ai(假设为整型),若ai=结束符(1),
则为ai创建一结点,然后插入表尾,最后返回链表的头结点指针H。则为a_i创建一结点,然后插入表尾,最后返回链表的头结点指针H。 则为ai创建一结点,然后插入表尾,最后返回链表的头结点指针H
链表的结构是在算法运行时动态形成的,也就是说,在算法开始执行之前,链表的结构是不存在的。

linklist list_create()
{
	linklist H;

	H = (linklist)malloc(sizeof(listnode));
	if (NULL == H)
	{
		printf("malloc failed\n");
		return H;
	}

	H->data = 0;
	H->next = NULL;

	return H;
}

三、单链表尾部插入和遍历

1、尾部插入

算法思路:

1、建立一个新结点:①malloc内存 ②赋值。
2、找链表H的尾部节点。
3、做尾部插入。

int list_tail_insert(linklist H, data_t value)	
{
	if (NULL == H)
	{
		printf("H is NULL");
		return -1;
	}
	linklist p;
	linklist q;
	
	//1.new node p
	if ((p = (linklist)malloc(sizeof(listnode))) == NULL)
	{
		printf("malloc failed\n");
		return -1;
	}
	p->data = value;
	p->next = NULL;

	//2.locate tail node
	q = H;
	while (q->next != NULL)
	{
		q = q->next;
	}
	//3.insert
	q->next = p;
	
	return 0;
}

2、链表的遍历

算法思路
1、判断H是否为有效链表。
2、若有效,则打印值p->next->data; 而后p = p->next;

int list_show(linklist H)
{
	linklist p;

	if (NULL == H)
	{
		printf("H is NULL\n");
		return -1;
	}
	
	p = H;
	while (p->next != NULL)
	{
		printf("%d ", p->next->data);
		p = p->next;
	}
	puts("");

	return 0;
}

3、链表的查找

按序号查找:实现list_get(h, i)运算。
算法思路
从链表的a0结点开始,判断当前结点是否为第i个结点。如果是,则返回该结点的指针;如果不是,则继续查找下一个结点,并重复这个过程。依次类推,直到找到第i个结点或遍历完整个链表。

linklist list_get(linklist H, int pos) 
{
	linklist p;
	int i;

	if(NULL == H)
	{
		printf("H is NULL\n");
		return NULL;
	}

	if(pos == -1) 
	{
		return H;
	}

	if(pos < -1)
	{
		printf("pos is invalid\n");
		return NULL;
	}

	p = H;
	i = -1;
	while(i < pos) 
	{
		p = p->next;
		if(p == NULL) 
		{
			printf("pos is invalid\n");
			return NULL;
		}
		i++;
	}

	return p;
}

4、链表的插入

即实现list_insert(h, i, i,)。将x插入表中结点ai之前的情况。
算法思路
①调用算法list_get(h, i-1),获取结点a(i-1)的指针p(ai 之前驱)。
②然后申请一个q结点,存入x,并将其插入p指向的结点之后。
注意:插入时新结点q的next域先指向插入位置后边,然后p指向q。

int list_insert(linklist H, data_t value, int pos)
{
	linklist p;
	linklist q;

	if (NULL == H)
	{
		printf("H is NULL\n");
		return -1;
	}

	//1.locate node p (pos-1)
	p = list_get(H, pos-1);

	if (p == NULL)
	{
		return -1;
	}
	
	//2.new node q
	if ((q = (linklist)malloc(sizeof(listnode))) == NULL)
	{
		printf("malloc failed\n");
		return -1;
	}
	q->data = value;
	q->next = NULL;

	//3.insert
	q->next = p->next;
	p->next = q;

	return 0;
}

5、链表的删除

即实现list_delete(h, i)。
算法思路
采用插入法,首先通过调用函数list_get(h, i-1)来获取结点ai的前驱节点,随后将结点ai从链表中删除。

int list_delete(linklist H, int pos)
{
	linklist p;
	linklist q;
	
	//1
	if(NULL == H)
	{
		printf("H is NULL\n");
		return -1;
	}
	
	//2.locate prior
	p = list_get(H, pos-1);
	if(p == NULL)
	{
		return -1;
	}
	if(p->next == NULL)
	{
		printf("delete pos is invalid\n");
		return -1;
	}

	//3.update
	q = p->next;
	p->next = q->next;//p->next = p->next->next;
	
	//4.free
	printf("free:%d\n",q->data);
	free(q);
	q = NULL;

	return 0;
}

6、链表的释放

即实现list_free(H)。
算法思路
移动头结点H,将p指向H,释放p结点。

linklist list_free(linklist H)
{
	linklist p;

	if(NULL == H)
	{
		return NULL;
	}

	p = H;
	
	printf("free:");
	while(H != NULL)
	{
		p = H;
		H = H->next;
		printf("%d ",p->data);
		free(p);
	}
	puts("");

	return NULL;
}

四、链表的复杂操作

1、链表的反转

设计并实现一个算法,用于将单链表H进行倒置。

算法思路
依次取原链表中各个结点,将其作为新链表首结点插入H结点后。

int list_reverse(linklist H)
{
	linklist p;
	linklist q;

	if(NULL == H)
	{
		printf("H is NULL\n");
		return -1;
	}

	if(H->next == NULL || H->next->next == NULL)
	{
		return 0;
	}
	
	p = H->next->next;
	H->next->next = NULL;

	while(p != NULL)
	{
		q = p;
		p = p->next;
		q->next = H->next;
		H->next = q;
	}
	
	return 0;
}

2、链表求相邻两结点最大值

假设链表中的每个结点包含一个整型数据域(data),我们需要找出链表中相邻两个结点数据域(data)值之和最大的那个第一结点的指针。
算法思路
在链表中,假设p和q是相邻的两个结点指针。我们需要找到这样一对结点,即p指向的结点和q指向的结点的数据之和是最大的。最后,返回指向p的指针即可。

linklist list_adjmax(linklist H, data_t *value)
{
	linklist p, q, r;
	data_t sum;

	if(NULL == H)
	{
		printf("H is NULL\n");
		return NULL;
	}

	if(H->next == NULL || H->next->next == NULL || \
       H->next->next->next == NULL)
	{
		return H;
	}

	q = H->next;
	p = H->next->next;
	r = q;
	sum = q->data + p->data;

	while(p->next != NULL)
	{
		p = p->next;
		q = q->next;
		if(sum < q->data + p->data)
		{
			sum = q->data + p->data;
			r = q;
		}
	}
	*value = sum;
	return r;
}

3、有序链表合并

设两单链表A、B按data值(设为整型)递增有序,将表A和B合并成一表A,且表A也按data值递增有序。
算法思路
设指针p、q分别指向表A和B中的结点,若p->data ≤q->data则p结点进入结果表,否则q结点进入结果表。

int list_merge(linklist H1, linklist H2)
{
	linklist p, q, r;

	if(NULL == H1 || NULL == H2)
	{
		printf(" H1 or H2 is NULL\n");
		return -1;
	}
	
	p = H1->next;
	q = H2->next;
	r = H1;
	H1->next = NULL;
	H2->next = NULL;
	
	while(p && q)//while(p != NULL && q != NULL)
	{
		if(p->data <= q->data)
		{
			r->next = p;
			p = p->next;
			r = r->next;
			r->next = NULL;
		}
		else
		{
			r->next = q;
			q = q->next;
			r = r->next;
			r->next = NULL;
		}
	}
	if(p == NULL)
	{
		r->next = q;
	}
	else
	{
		r->next = p;
	}
	return 0;
}

五、程序

1、test.c

#include <stdio.h>
#include "linklist.h"

void test_get();
void test_insert();
void test_delete();
void test_reverse();
void test_adjmax();

int main(int argc, char *argv[])
{
	linklist H1, H2;
	int arr1[] = {1, 3, 5, 7, 9, 14, 17, 19};
	int arr2[] = {2, 4, 6, 8, 10, 13, 15, 20};
	int i;

	H1 = list_create();
	if(NULL == H1)
		return -1;
	
	H2 = list_create();
	if(NULL == H2)
		return -1;
	
	for(i = 0; i < sizeof(arr1)/sizeof(int); i++)
	{
		list_tail_insert(H1, arr1[i]);
	}

	for(i = 0; i < sizeof(arr2)/sizeof(int); i++)
	{
		list_tail_insert(H2, arr2[i]);
	}

	list_show(H1);
	list_show(H2);

	list_merge(H1, H2);
	printf("merge:\n");

	list_free(H1);
	list_free(H2);

	return 0;
}


void test_get()
{

	linklist H;
	int value;
	linklist p;

	H = list_create();

	if(NULL == H)
		return;
	
	printf("input:");
	while(1)
	{
		scanf("%d",&value);
		if(value == -1)
		{
			break;
		}
		list_tail_insert(H, value);
		printf("input:");
	}

	list_show(H);

	p = list_get(H, 4);
	if(p != NULL)
	{
		printf("value=%d\n",p->data);
	}
}


void test_insert()
{
	linklist H;
	int value;

	H = list_create();

	if(NULL == H)
		return;
	
	printf("input:");
	while(1)
	{
		scanf("%d",&value);
		if(value == -1)
		{
			break;
		}
		list_tail_insert(H, value);
		printf("input:");
	}

	list_show(H);

	list_insert(H, 100, 2);

	list_show(H);
}


void test_delete()
{
	linklist H;
	int value;

	H = list_create();

	if(NULL == H)
		return;
	
	printf("input:");
	while(1)
	{
		scanf("%d",&value);
		if(value == -1)
		{
			break;
		}
		list_tail_insert(H, value);
		printf("input:");
	}

	list_show(H);
	printf("H = %p\n", H);
	H = list_free(H);
	printf("H = %p\n", H);

	list_delete(H, -4);

	list_show(H);
}

void test_reverse()
{
	
	linklist H;
	int value;

	H = list_create();

	if(NULL == H)
		return;
	
	printf("input:");
	while(1)
	{
		scanf("%d",&value);
		if(value == -1)
		{
			break;
		}
		list_tail_insert(H, value);
		printf("input:");
	}

	list_show(H);
	list_reverse(H);
	list_show(H);

	list_free(H);
}

void test_adjmax()
{	
	linklist H;
	linklist r;
	int value;
	int sum;

	H = list_create();

	if(NULL == H)
		return;
	
	printf("input:");
	while(1)
	{
		scanf("%d",&value);
		if(value == -1)
		{
			break;
		}
		list_tail_insert(H, value);
		printf("input:");
	}

	list_show(H);
	r = list_adjmax(H, &sum);
	if(r != NULL && r != H)
	{
		printf("data:%d, sum:%d\n", r->data, sum);
	}
	list_show(H);

	list_free(H);
}

2、linklist.c

#include <stdio.h>
#include <stdlib.h>
#include "linklist.h"


linklist list_create()
{
	linklist H;

	H = (linklist)malloc(sizeof(listnode));
	if (NULL == H)
	{
		printf("malloc failed\n");
		return H;
	}

	H->data = 0;
	H->next = NULL;

	return H;
}

int list_tail_insert(linklist H, data_t value)	
{
	if (NULL == H)
	{
		printf("H is NULL");
		return -1;
	}
	linklist p;
	linklist q;
	
	//1.new node p
	if ((p = (linklist)malloc(sizeof(listnode))) == NULL)
	{
		printf("malloc failed\n");
		return -1;
	}
	p->data = value;
	p->next = NULL;

	//2.locate tail node
	q = H;
	while (q->next != NULL)
	{
		q = q->next;
	}
	//3.insert
	q->next = p;
	
	return 0;
}

int list_show(linklist H)
{
	linklist p;

	if (NULL == H)
	{
		printf("H is NULL\n");
		return -1;
	}
	
	p = H;
	while (p->next != NULL)
	{
		printf("%d ", p->next->data);
		p = p->next;
	}
	puts("");

	return 0;
}

linklist list_get(linklist H, int pos) 
{
	linklist p;
	int i;

	if(NULL == H)
	{
		printf("H is NULL\n");
		return NULL;
	}

	if(pos == -1) 
	{
		return H;
	}

	if(pos < -1)
	{
		printf("pos is invalid\n");
		return NULL;
	}

	p = H;
	i = -1;
	while(i < pos) 
	{
		p = p->next;
		if(p == NULL) 
		{
			printf("pos is invalid\n");
			return NULL;
		}
		i++;
	}

	return p;
}

int list_insert(linklist H, data_t value, int pos)
{
	linklist p;
	linklist q;

	if (NULL == H)
	{
		printf("H is NULL\n");
		return -1;
	}

	//1.locate node p (pos-1)
	p = list_get(H, pos-1);

	if (p == NULL)
	{
		return -1;
	}
	
	//2.new node q
	if ((q = (linklist)malloc(sizeof(listnode))) == NULL)
	{
		printf("malloc failed\n");
		return -1;
	}
	q->data = value;
	q->next = NULL;

	//3.insert
	q->next = p->next;
	p->next = q;

	return 0;
}
int list_delete(linklist H, int pos)
{
	linklist p;
	linklist q;
	
	//1
	if(NULL == H)
	{
		printf("H is NULL\n");
		return -1;
	}
	
	//2.locate prior
	p = list_get(H, pos-1);
	if(p == NULL)
	{
		return -1;
	}
	if(p->next == NULL)
	{
		printf("delete pos is invalid\n");
		return -1;
	}

	//3.update
	q = p->next;
	p->next = q->next;//p->next = p->next->next;
	
	//4.free
	printf("free:%d\n",q->data);
	free(q);
	q = NULL;

	return 0;
}

linklist list_free(linklist H)
{
	linklist p;

	if(NULL == H)
	{
		return NULL;
	}

	p = H;
	
	printf("free:");
	while(H != NULL)
	{
		p = H;
		H = H->next;
		printf("%d ",p->data);
		free(p);
	}
	puts("");

	return NULL;
}

int list_reverse(linklist H)
{
	linklist p;
	linklist q;

	if(NULL == H)
	{
		printf("H is NULL\n");
		return -1;
	}

	if(H->next == NULL || H->next->next == NULL)
	{
		return 0;
	}
	
	p = H->next->next;
	H->next->next = NULL;

	while(p != NULL)
	{
		q = p;
		p = p->next;
		q->next = H->next;
		H->next = q;
	
	}

	return 0;
}

linklist list_adjmax(linklist H, data_t *value)
{
	linklist p, q, r;
	data_t sum;

	if(NULL == H)
	{
		printf("H is NULL\n");
		return NULL;
	}

	if(H->next == NULL || H->next->next == NULL || \
       H->next->next->next == NULL)
	{
		return H;
	}

	q = H->next;
	p = H->next->next;
	r = q;
	sum = q->data + p->data;

	while(p->next != NULL)
	{
		p = p->next;
		q = q->next;
		if(sum < q->data + p->data)
		{
			sum = q->data + p->data;
			r = q;
		}
	}
	*value = sum;
	return r;
}

int list_merge(linklist H1, linklist H2)
{
	linklist p, q, r;

	if(NULL == H1 || NULL == H2)
	{
		printf(" H1 or H2 is NULL\n");
		return -1;
	}
	
	p = H1->next;
	q = H2->next;
	r = H1;
	H1->next = NULL;
	H2->next = NULL;
	
	while(p && q)//while(p != NULL && q != NULL)
	{
		if(p->data <= q->data)
		{
			r->next = p;
			p = p->next;
			r = r->next;
			r->next = NULL;
		}
		else
		{
			r->next = q;
			q = q->next;
			r = r->next;
			r->next = NULL;
		}
	}
	if(p == NULL)
	{
		r->next = q;
	}
	else
	{
		r->next = p;
	}
	return 0;
}

3、linklist.h

typedef int data_t;
typedef struct node

{
	data_t data;
	struct node *next;
}listnode, *linklist;

linklist list_create();
int list_tail_insert(linklist H, data_t value);//head
linklist list_get(linklist H, int pos);
int list_insert(linklist H, data_t value, int pos);
int list_show(linklist H);
int list_delete(linklist H, int pos);
linklist list_free(linklist H);
int list_reverse(linklist H);
linklist list_adjmax(linklist H, data_t *value);
int list_merge(linklist H1, linklist H2);
<think>我们正在处理一个关于在ComfyUI中创建视频工作流并支持提示词输入的问题。根据引用[2],我们知道ComfyUI具有高度灵活性,允许用户调整成流程中的参数和节点,并组合不同模型和插件。用户意是创建视频工作流,并且要支持提示词输入。由于ComfyUI本身是一个基于节点的工作流构建工具视频通常需要结合图像输入和视频成模型。我们可以考虑以下步骤:1.**图像输入节点**:用于接收初始图像。2.**提示词输入节点**:用于输入文本提示词,以指导视频的成。3.**视频成模型节点**:例如使用支持视频成的模型(如SVD、AnimateDiff等)。4.**参数调整节点**:如降噪强度、采样步数、采样方法等。但是,需要注意的是,目前(截至2024年)视频的模型和技术还在快速发展中,例如StableVideoDiffusion(SVD)是一个由StabilityAI推出的视频模型。在ComfyUI中,我们可以通过安装相应的插件(如comfyui-video或集成AnimateDiff等)来实现视频。具体步骤:1.**安装必要插件**:首先确保安装了视频成相关的插件,例如`ComfyUI-AnimateDiff`(用于动画成)或者`ComfyUI-VideoHelperSuite`(视频处理辅助)等。另外,如果使用SVD模型,需要安装支持SVD的节点。2.**工作流构建**:-使用`LoadImage`节点加载初始图像。-使用`CLIPTextEncode`节点对提示词进行编码(支持正面和负面提示词)。-将图像和编码后的提示词连接到视频成模型节点(如SVD模型节点或AnimateDiff节点)。-配置视频成参数(如帧数、帧率、降噪强度等)。-最后使用`SaveVideo`节点保存成的视频。3.**提示词的使用**:提示词输入将影响视频的成内容,例如描述场景的变化、物体的运动等。由于具体节点名称和连接方式可能因插件而异,下面给出一个概念性的工作流示例(以SVD模型为例):```图像输入(LoadImage)->图像预处理(例如调整大小等)->SVD模型节点正面提示词(CLIPTextEncode)->SVD模型节点负面提示词(CLIPTextEncode)->SVD模型节点SVD模型节点->视频保存(SaveVideo)```但是,由于SVD模型可能对输入图像尺寸有要求(如必须是1024x576),因此可能需要先调整图像尺寸。另外,根据引用[1]中提到的,用户可能已经学习了ComfyUI的基础教程,因此我们可以建议用户参考已学知识,并尝试安装相关插件来构建工作流。由于没有具体的插件和模型细节,这里无法给出精确的节点连接。但我们可以提供一个大致的思路,并建议用户查阅特定插件的文档。因此,回答如下:</think>### 在ComfyUI中创建视频工作流并支持提示词输入 以下是在ComfyUI中构建视频工作流的关键步骤和技术要点,结合引用内容和系统特性实现: #### 1. **核心工作流结构** ```mermaid graph LR A[初始图像] --> B(图像预处理) C[正面提示词] --> D(CLIP文本编码器) E[负面提示词] --> D B --> F(视频成模型) D --> F F --> G[输出视频] ``` #### 2. **关键节点配置** - **图像输入节点**:使用`LoadImage`节点加载初始图像 - **提示词处理**: - 添加两个`CLIPTextEncode`节点(正/负面提示词) - 支持动态提示词输入:`${prompt}`变量语法实现参数化 - **视频成引擎**: - 使用`VideoDiffusion`或`AnimateDiff`模型节点 - 连接提示词编码输出到模型的`conditioning`输入端口 - **参数控制**: ```python # 伪代码示例 "video_params": { "fps": 24, "length": 48, # 帧数 "cfg_scale": 7.5, "denoise_strength": 0.8 } ``` #### 3. **技术实现要点 1. **图像预处理**: - 通过`ImageScale`节点统一分辨率(如1024x576) - 使用`VAEEncode`将像素空间转换为潜空间 2. **提示词融合技术**: - 权重分配:`(正面提示词:1.2), (负面提示词:-0.8)` - 时间轴控制:使用`Schedule`节点实现提示词随时间变化 3. **视频成优化**: - 添加`ControlNet`节点保持内容一致性 - 使用`FrameInterpolation`提升流畅度 - 通过`LatentUpscale`增强分辨率 #### 4. **完整工作流示例 ```json { "nodes": [ { "type": "LoadImage", "inputs": {"image_path": "input.png"} }, { "type": "CLIPTextEncode", "inputs": {"text": "${positive_prompt}"} }, { "type": "VideoDiffusion", "inputs": { "image": "@1.output", "conditioning": "@2.output", "fps": 24, "num_frames": 48 } }, { "type": "SaveVideo", "inputs": {"video": "@3.output"} } ] } ``` #### 5. **调试技巧 1. **提示词有效性验证**: - 先通过文测试提示词效果 - 使用`PromptAnalyzer`节点检查关键词权重 2. **性能优化**: - 启用`xFormers`加速显存优化 - 设置`--lowvram`参数处理大视频 - 分阶段渲染:先低分辨率预览再全质量输出 > 提示:ComfyUI的灵活性允许自由组合节点,例如将工作流与视频成模块结合,实现风格化视频输出[^2]。最新插件如`ComfyUI-VideoHelperSuite`可简化关键帧控制流程。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值