【数据结构与算法】程序内功篇三--单链表


一、线性表的链式存储结构

1、链表含义

线性表L=(a0,a1,……,an-1)各元素分布在存储器的不同存储块,称为结点,通过地址或指针建立元素之间的联系

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


2、结点类型描述

typedef struct node
{   
	data_t   data;   //结点的数据域//
	struct node *next;  //结点的后继指针域//
}listnode, *linklist;
例如:
listnode A;
linklist p = &A;

在这里插入图片描述


3、链表结点用法与创建

(1)设p指向链表中结点ai
在这里插入图片描述

获取ai,写作:p->data;
而取ai+1,写作:p->next->data 
若指针p的值为NULL,则它不指向任何结点, 
此时取p->data或p->next是错误的。

(2)可调用C语言中malloc()函数向系统申请结点的存储空间

linklist  p; 
p = (linklist)malloc(sizeof(listnode));

则创建一个类型为linklist的结点,且该结点的地址已存入指针变量p


二、单链表的基本相关算法

1、建立单链表

①创建头结点,为头结点开辟空间。函数如下:

/**
 * @description:单链表创建 
 * @param {*}
 * @return {链表头指针}
 */
linklist linknode_create()
{
	linklist H = (linklist)malloc(sizeof(linknode)); //开辟头指针空间
	if(H == NULL)
	{
		return 0;
	}

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

	return H;
}

②为链表增添元素我们使用尾插法,函数如下:

/**
 * @description: 尾插法增添链表元素 
 * @param {linklist} H -头指针
 * @param {data_t} value -结点的数据
 * @return {0-函数失败,1-函数成功}
 */
int linknode_tail_insert(linklist H, data_t value)
{
	linklist p = NULL,q = H;   

	if(H == NULL)
	{
		DEBUG_NULL();
		return 0;
	}
	
	p = (linklist)malloc(sizeof(linknode));  //结点空间开辟
	if(p == NULL)
	{
		DEBUG_CREATE();
		return 0;
	}
	p->data = value;
	p->next = NULL;

	while(q->next)  //遍历到最后链表最后一位结点
		q = q->next;
	q->next = p;

	return 1;
}

③为链表加入元素,可以输入-1作为结束。例如:

while(1)
{
	printf("Please input:\n");
	scanf("%d",&value);
	if(value == -1)
		break;
	linknode_tail_insert(H, value);
}

2、链表查找

按序号查找:实现linknode_element_inqure(H, i)运算。

算法思路:

从链表的a0起,判断是否为第i结点,若是则返回该结点的指针,否则查找下一结点,依次类推。函数如下:

/**
 * @description:链表元素查询 
 * @param {linklist} H -链表头指针
 * @param {int} i -查询的位置
 * @return {结点位置的地址}
 */
linklist linknode_element_inqure(linklist H, int i)
{
	int n = -1;
	if(H == NULL)
	{
		DEBUG_NULL();
		return 0;
	}

	if(i == -1)
		return H;
	if(i < -1)
	{
		#if DEBUG
		printf(" i value error !\n");
		#endif
		return 0;
	}
	while(n < i)
	{
		H = H->next;
		if(H == NULL)
		{
			#if DEBUG
			printf("i is too big!\n");
			#endif
			return 0;
		}
		n++;
	}
	return H;
}

3、链表遍历

从头结点开始打印结点数据,函数如下:

/**
 * @description:链表遍历 
 * @param {linklist} H -链表头指针
 * @return {0-函数失败,1-函数成功}
 */
int linkshow(linklist H)
{
	if(H == NULL)
	{
		DEBUG_NULL();
		return 0;
	}

	while(H->next)
	{
		H = H->next;
		printf("%d ", H->data);
	}
	puts("");

	return 1;
}

4、链表结点的插入

即实现InsertLinklist(h, x, i,)。将x插入表中结点ai之前的情况。

算法思路:

调用算法linknode_element_inqure(h, i-1),获取结点ai-1指针p(ai 之前驱),然后申请一个q结点,存入x,并将其插入p指向的结点之后。
在这里插入图片描述

函数如下:

/**
 * @description: 链表元素按位置插入
 * @param {linklist} H -链表头结点指针
 * @param {data_t} value -插入结点的值
 * @param {int} i -插入的位置
 * @return {0-函数失败,1-函数成功}
 */
int linknode_element_insert(linklist H, data_t value, int i)
{	
	linklist p,q = (linklist)malloc(sizeof(linknode));
	if(q == NULL)
	{
		DEBUG_CREATE();
		return 0;
	}
	if(H == NULL)
	{
		DEBUG_NULL();
		free(q);
		return 0;
	}

	p = linknode_element_inqure(H, i-1);
	if(p == NULL)
	{
		free(q);
		return 0;
	}

	q->data = value;

	q->next = p->next;
	p->next = q;

	return 1;
}

5、链表结点的删除

即实现linknode_element_delete(h, i)。

算法思路:

同插入法,先调用函数linknode_element_inqure(h, i-1),找到结点ai的前驱,然后将结点ai删除之。
在这里插入图片描述
函数如下:

/**
 * @description: 链表元素按位置删除
 * @param {linklist} H -链表头指针
 * @param {int} i -删除的位置
 * @return {0-函数失败,1-函数成功}
 */
int linknode_element_delete(linklist H, int i)
{
	linklist p,q;
	if(H == NULL)
	{
		DEBUG_NULL();
		return 0;
	}

	p = linknode_element_inqure(H, i-1);
	if(p == NULL)
	{
		return 0;
	}

	if(p->next == NULL)
	{
		#if DEBUG
		printf("The place of i is NULL!\n");
		#endif
		return 0;
	}
	q = p->next;
	p->next = q->next;

	free(q);
	q = NULL;

	return 1;
}

6、链表删除

删除整个链表

/**
 * @description: 链表删除
 * @param {linklist} H -链表头指针
 * @return {0-函数失败,1-函数成功}
 */
int linknode_delete(linklist H)
{
	linklist p = H;
	if(H == NULL)
	{
		DEBUG_NULL();
		return 0;
	}

	while(p)
	{
		p = p->next;  //指针前移
		free(H);   //释放上一个结点
		H = p;
	}

	return 1;
}

三、相关单链表的题

1、单链表倒置

设计算法,将单链表H倒置

算法思路:

依次取原链表中·各结点·,将其作为·新链表首结点·插入H结点之后
在这里插入图片描述
函数如下:

/**
 * @description: 链表倒置
 * @param {linklist} H -链表头指针
 * @return {0-函数失败,1-函数成功}
 */
int linknode_invert(linklist H)
{
	linklist p,q;
	if(H == NULL)
	{
		DEBUG_NULL();
		return 0;
	}

	if(H->next == NULL)
	{
		#if DEBUG
		printf("empty list!\n");
		#endif
		return 0;
	}
	p = H->next;
	q = p->next;
	p->next = NULL;
	
	while(q)
	{
		p = q;
		q = q->next;

		p->next = H->next;
		H->next = p;
	}

	return 1;
}

2、相邻两结点最大值

设结点data域为整型,求链表中相邻两结点data值之和为最大的第一结点的指针

算法思路:

p,q分别为链表中相邻两结点指针,求p->data+q->data为最大的那一组值,返回其相应的指针p即可
在这里插入图片描述

/**
 * @description: 链表求相邻结点数据之和最大值
 * @param {linklist} H -链表头指针
 * @param {data_t} *value -数据之和最大值的地址
 * @return {相邻结点数据之和最大值的第一个结点地址}
 */
linklist linknode_max_adjoin(linklist H,data_t *value)
{	
	linklist p,q,m;
	int n = 0;

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

	if(H->next == NULL || H->next->next == NULL)
	{
		#if DEBUG
		printf("empty list or only one element!\n");
		#endif
		return 0;
	}
	
	p = H->next;
	q = p->next;
	m = p;
	n = p->data + q->data;

	while(q->next)
	{
		p = q;
		q = q->next;
		if(n < p->data + q->data)
		{
			m = p;
			n = p->data + q->data;
		}
	}

	*value = n;
	return m;
}

3、有序排列问题

两单链表A、B按data值(设为整型)递增有序,将表A和B合并成一表A,且表A也按data值递增有序

算法思路:

指针p、q分别指向表A和B中的结点,若p->data ≤q->data则p结点进入结果表,否则q结点进入结果表
在这里插入图片描述
函数如下:

/**
 * @description: 两个有序链表并排为一个有序列表
 * @param {linklist} H1 -链表1头指针
 * @param {linklist} H2 -链表2头指针
 * @return {0-函数失败,1-函数成功}
 */
int linknode_order_merger(linklist H1,linklist H2)
{	
	linklist p;
	if(H1 == NULL || H2 == NULL)
	{
		DEBUG_NULL();
		return 0;
	}
	if(H2->next == NULL)
	{
		#if DEBUG
		printf("list2 is a empty list!\n");
		#endif
		return 0;
	}

	p = H2->next;
	
	while(p)
	{
		while(H1->next)
		{
			if(H1->next->data > p->data)
				break;
			H1 = H1->next;
		}
		H2 = p;
		p = p->next;

		H2->next = H1->next;
		H1->next = H2;
	}

	return 1;
}

这里给出免费的程序实现文件下载链接;
单链表程序文件


到这里就结束啦!
在这里插入图片描述

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

修成真

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值