数据结构 -- 线性表:头结点实现单链表、头指针实现单链表、找到倒数第K个结点 O(1)下删除单链表结点P,P不为尾结点、判断两个单链表是否相交,返回相交的第一个结点、判断两个单链表是否有环,返回入环

本文介绍了链表的基本概念,包括单链表、双向链表和循环链表,并详细阐述了如何使用头结点实现单链表的插入、删除、输出和求长度等操作。此外,还讨论了头指针实现单链表的方法以及一些常见的链表面试题,如找倒数第K个节点、O(1)删除节点、判断链表相交和环等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、链表的概念

链表也是一种线程结构。

链表存储数据时,在逻辑上是连续的,但是在物理空间上不连续。既然在物理空间上不连续,那我们不能使用下标来访问链表中的结点。
在这里插入图片描述

链表的分类

1.、单链表 每一个存储数据的结点,只能记录其直接后继结点的位置。
2.、双向链表 每一个存储数据的结点, 既可以记录其直接后继结点的位置,
也可以记录其直接前驱结点的位置
3、循环链表 在以上两种结构中做一些处理,使得他最后一个结点可以记录第一个结点的位置

二、头结点实现单链表

有一个头结点(栈区),头结点的数据域不存储有效数据,只是使用其指针域指向第一个存储数据的结点。
在这里插入图片描述

1、结构的声明

typedef int ElemType;
typedef struct Node
{
	ElemType data; // 存储数据元素的
	struct Node *next; // 存储下一个结点的地址的
}HeadList;

2、操作方法声明

void InitHeadList(HeadList * head);

int GetLength(HeadList *head);
void ShowList(HeadList *head);

bool InsertOFPos(HeadList *head, ElemType val, int pos);
bool InsertOFHead(HeadList *head, ElemType val);
bool InsertOFTail(HeadList *head, ElemType val);

bool DeleteOFPos(HeadList *head, int pos);
bool DeleteOFHead(HeadList *head);
bool DeleteOFTail(HeadList *head);
bool DeleteOFValue(HeadList *head, ElemType val);

void ClearHeadList(HeadList *head);
void DestroyHeadList(HeadList *head);

3、操作方法实现

初始化

void InitHeadList(HeadList * head)
{
	if (head == NULL) exit(0);
	head->next = NULL;
}

插入

bool InsertOFPos(HeadList *head, ElemType val, int pos)
{
	if (head == NULL) exit(0);
	
	if (pos < 0) return false;
	
	HeadList *p = head;
	
	while (pos && p != NULL)
	{
		pos--;
		p = p->next;
	}
	
	if (p == NULL) return false;
	
	HeadList *newNode = ApplyNode(val, p->next);
	if (newNode == NULL) return false;
	
	p->next = newNode;
	return true;
}
bool InsertOFHead(HeadList *head, ElemType val)
{
	if(head == NULL) exit(0);
	return InsertOFPos(head, val, 0);
}
bool InsertOFTail(HeadList *head, ElemType val)
{
	if(head == NULL) exit(0);
	
	HeadList *p = head;
	while(p->next != NULL) p = p->next;
	
	p->next = ApplyNode(val, NULL);
	
	return true;
}

求长度、输出

int GetLength(HeadList *head)
{
	if (head == NULL) exit(0);

	int length = 0;

	HeadList *p = head->next;
	while (p != NULL)
	{
		length++;
		p = p->next;
	}

	return length;
}

void ShowList(HeadList *head)
{
	if (head == NULL) exit(0);

	HeadList *p = head->next;
	while (p != NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}

删除元素

bool DeleteOFPos(HeadList *head, int pos)
{
	if(head == NULL) exit(0);

	HeadList *p = head;

	while(pos && p->next != NULL)
	{
		pos--;
		p = p->next;
	}

	if(p->next == NULL) return false;

	HeadList *q = p->next;
	p->next = q->next;
	free(q);

	return true;
}
bool DeleteOFHead(HeadList *head)
{
	if(head == NULL) exit(0);

	return DeleteOFPos(head, 0);
}
bool DeleteOFTail(HeadList *head)
{
	if(head == NULL) exit(0);
	if(head->next == NULL) return false;

	HeadList *p = head, *q = head->next;

	while(q->next != NULL)
	{
		p = q;
		q = q->next;
	}
	p->next = NULL;
	free(q);
	
	return true;
}
bool DeleteOFValue(HeadList *head, ElemType val)
{
	if(head == NULL) exit(0);
	HeadList *p = head, *q = head->next;
	while(q != NULL)
	{
		if(q->data == val)
		{
			p->next = q->next;
			free(q);
			q = p->next;
		}
		else
		{
			p = q;
			q = q->next;
		}
	}
	return true;
}

清空、销毁

void ClearHeadList(HeadList *head)
{
	if(head == NULL) exit(0);
	while(head->next != NULL) 
		DeleteOFHead(head);
}
void DestroyHeadList(HeadList *head)
{
	if(head == NULL) exit(0);
	ClearHeadList(head);
}

三、头指针实现单链表

只有一个结点指针,只用这个结点指针保存第一个数据结点的地址。

在这里插入图片描述
和头结点单链表实现的区别: 插入时为空链的特殊处理和删除第一个结点的特殊处理。

1、 结构声明

typedef int ElemType;

typedef struct Node
{
	ElemType data; // 存储数据元素的
	struct Node *next; // 存储下一个结点的地址的
}PointList;

2 、操作方法声明

void InitPointList(PointList **pp); // 初始化

bool InsertPointList(PointList **pp, ElemType val, int pos); // 按位置插入
bool InsertHead(PointList **pp, ElemType val); // 头插
bool InsertTail(PointList **pp, ElemType val); // 尾插

bool DeletePonitList(PointList **pp, int pos); // 按位置删除
bool DeleteHead(PointList **pp); // 头删
bool DeleteTail(PointList **pp); // 尾删
bool DeleteValue(PointList **pp, ElemType val); // 按值删除

void DestroyPointList(PointList **pp); // 销毁

3、方法实现

申请新的节点

static PointList* ApplyNode(ElemType val, PointList *nt)
{
	PointList *s = (PointList *)malloc(sizeof(PointList));
	if (s == NULL) return NULL;
	
	s->data = val;
	s->next = nt;
	
	return s;
}

初始化

void InitPointList(PointList **pp)
{
	if (pp == NULL) exit(0);
	*pp = NULL;
}

插入

bool InsertHead(PointList **pp, ElemType val)
{
	if (pp == NULL) exit(0);
	*pp = ApplyNode(val, *pp);
	if (*pp == NULL) return false;
	return true;
}
bool InsertPointList(PointList **pp, ElemType val, int pos)
{
	if (pp == NULL) exit(0);
	if (pos < 0) return false;
	if (*pp == NULL || pos == 0) return InsertHead(pp, val);
	
	PointList *p = *pp;
	
	while (pos > 1 && p != NULL)
	{
		pos--;
		p = p->next;
	}
if (p == NULL) return false;

PointList *newNode = ApplyNode(val, p->next);
if (newNode == NULL) return false;

p->next = newNode;
return true;
 }

bool InsertTail(PointList **pp, ElemType val)
{
	if (pp == NULL) exit(0);
	if (*pp == NULL) return InsertHead(pp, val);

	PointList *p = *pp;

	while (p->next != NULL) p = p->next;

	p->next = ApplyNode(val, NULL);

	return false;
}

删除

bool DeleteHead(PointList **pp)
{
	if (pp == NULL) exit(0);
	if (*pp == NULL) return false;

	PointList *q = *pp;
	*pp = q->next;
	free(q);
	return true;
}

bool DeletePonitList(PointList **pp, int pos)
{
	if (pp == NULL) exit(0);
	if (*pp == NULL || pos < 0) return false;
	if (pos == 0)
	{
		return DeleteHead(pp);
	}

	PointList *p = *pp;

	while (pos > 1 && p->next != NULL)
	{
		pos--;
		p = p->next;
	}

	if (p->next == NULL) return false; // pos越界
	PointList *q = p->next;
	p->next = q->next;
	free(q);

	return true;
}

bool DeleteTail(PointList **pp)
{
	if (pp == NULL) exit(0);
	if (*pp == NULL) return false;

	if ((*pp)->next == NULL) return DeleteHead(pp);

	PointList *p = *pp;
	PointList *q = p->next;

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

	p->next = NULL;
	free(q);
	return true;
}

bool DeleteValue(PointList **pp, ElemType val)
{
	if (pp == NULL) exit(0);
	if (*pp == NULL) return false; // 进来就是空链表

	while (*pp != NULL && (*pp)->data == val)
	{
		DeleteHead(pp);
	}

	if (*pp == NULL) return true; // 将链表删除成空链

	PointList *p = *pp;
	PointList *q = p->next;

	while (q != NULL)
	{
		if (q->data == val)
		{
			p->next = q->next;
			free(q);
			q = p->next;
		}
	else
	{
		p = q;
		q = q->next;
	}
}

return true;
}

销毁

void DestroyPointList(PointList **pp)
{
	if (pp == NULL) 
		exit(0);

	while (*pp != NULL)
	{
	DeleteHead(pp);
	}
}

将头指针转化为头结点链表进行操作 – 以按位置插入为例

//将不带头结点的链表转化为带头结点的链表
bool InsertPointList2(PointList **pp, ElemType val, int pos)
{
	if (pp == NULL) exit(0);
	if (pos < 0) return false;

	PointList vmNode; // 虚拟头结点
	vmNode.next = *pp;

	PointList *p = &vmNode;

	while (pos && p != NULL)
	{
		pos--;
		p = p->next;
	}

	if (p == NULL) return false;

	PointList *newNode = ApplyNode(val, p->next);
	if (newNode == NULL) 
		return false;
		
	p->next = newNode;
	*pp = vmNode.next; // 将虚拟头结点的next域赋值给*pp
	return true;
}

四、单链表节点面试题:

1、找到倒数第K个结点

思路:

1、正数的第length-k+1个
 2、两个指针实现:p,q;q指针先走k个节点(并且判断一下k是否合法), 
 如果q不为空,则继续p和q同步向后走,直到q为空,p就是倒数第k个节点。
HeadList *FindReciK2(HeadList *head, int k)
{
	if (head == NULL) exit(0);
	
	HeadList *p = head, *q = head;
	
	// 找到正数第k个节点,并且判断k是否合法
	while (k && q != NULL)
	{
		q = q->next;
		k--;
	}
	
	if (q == NULL) return NULL;
	
	while (q != NULL)
	{	
		q = q->next;
		p = p->next;
	}
	
	return p;
}

2、O(1)下删除单链表结点P,P不为尾结点

思路:
将p->next 节点数据复制到p节点上,然后删除p->next节点就可以!

void DeletePNode(HeadList *head, HeadList *p)
{
	if (head == NULL || p == NULL || p->next == NULL) 
		return;
	
	HeadList *q = p->next;
	p->data = q->data;
	p->next = q->next;
	free(q);
}

3、判断两个单链表是否相交,返回相交的第一个结点

思路1:

先计算两个链表的长度,然后让指针p先在长的链表上走 差值 个, 然后指针p和指向短的链表的指针q同步向后走,并判断p和q是否相等,如
果相等,则返回p。

HeadList *IsIntersect(HeadList *l1, HeadList *l2)
{
	if (l1 == NULL || l2 == NULL) return NULL;
	
	int len1 = GetLength(l1);
	int len2 = GetLength(l2);
	
	HeadList *p = len1 - len2 > 0 ? l1 : l2; // p指向的是长的链表
	HeadList *q = len1 - len2 > 0 ? l2 : l1; // q指向的是短的链表

	int diff = fabs(len1 - len2);
	while (diff)
	{
		p = p->next;
		diff--;
	}	

	while (p != NULL)
	{
		if (p == q) return p;

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

	return NULL;
}

思路2:

两个指针p和q分别指向两个链表,同步向后遍历,当一个指针为NULL,让他指向另一个链表,直到p和q相等。p和q相等的两种情况:
1、指向了第一个相交的节点
2、 p和q都为NULL。

HeadList *IsIntersect2(HeadList *l1, HeadList *l2)
{
	if (l1 == NULL || l2 == NULL) return NULL;

	HeadList *p = l1;
	HeadList *q = l2;

	while (p != q)
	{
		p = p == NULL ? l2 : p->next;
		q = q == NULL ? l1 : q->next;
	}

	return p;
}

4、判断两个单链表是否有环,返回入环的第一个结点

思路: 快慢指针
在这里插入图片描述

HeadList *IsRing(HeadList *head)
{
	if (head == NULL) exit(0);

	HeadList *p = head;
	HeadList *q = head;

	while (p != NULL && p->next != NULL)
	{
		p = p->next->next; // 一次走两步
		q = q->next;

		if (p == q)
		{
			break;
		}
	}

	if (p != q) return NULL;

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

	return p;
}

5、将单链表原地逆置

void Reverse(HeadList *head)
{
	if (head == NULL || head->next == NULL || head->next->next == NULL) return;

	HeadList *s = NULL;
	HeadList *p = head->next;
	HeadList *q = p->next;

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

	head->next = s;
}

// 两个指针实现, 利用head->next代替上面代码的s指针
void Reverse(HeadList *head)
{
	if (head == NULL || head->next == NULL || head->next->next == NULL) return;

	HeadList *p = head->next;
	HeadList *q = p->next;
	head->next = NULL;

	while (p != NULL)
	{
		p->next = head->next;
		head->next = p;
		p = q;
		if (q != NULL)
		{
			q = q->next;
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值