(数据结构)带头结点的单链表操作——C实现(新增一些面试经典题目)

带头结点的单链表

特点:逻辑相邻,物理不相邻
头结点:只在其指针域存放下一结点的地址,用于标记链表的起始(第一个有效数据节点的地址),不存数据。
头结点的空间:栈区或全局区
有效数据的空间:堆区

声明单链表结点类型

typedef struct LNode
{
	int data;//数据域
	struct LNode* next;//指针域
}LNode,* Linklist

操作

链表初始化

//链表初始化
void InitLinkList(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	plist->next = NULL;
}

为了实现以下插入,删除等一系列操作,先封装以下功能:

获取节点个数

//获取结点个数
int ListLength(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
 		return 0 ;
	}

	int length = 0;
	Linklist p = plist->next;
	while (p)
	{
		length++;
		p = p->next;
	}

	return length;
}

根据位置找结点

//根据位置找结点
Linklist GetNode(Linklist plist,int pos)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return NULL;
	}

	if(pos < 0) return NULL;

	Linklist p = plist;

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

	return p;
}

申请节点

static Linklist _ApplyNode(int val,Linklist next)
{
	Linklist s = (Linklist)malloc(sizeof(LNode));
	assert(s != NULL);
	if (s == NULL)
	{
		printf("Insert Fail : Apply Space Error\n");
		return NULL;
	}

	s->data = val;
	s->next = next;
	return s;
	
}

插入

void Insert(Linklist plist, int val, int pos)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	//判断pos位置是否合法
	if(pos < 0  || pos >ListLength(plist))
	{
		printf("Insert error : Pos is Error\n");
		return ;
	}

	Linklist p = GetNode(plist,pos);

	p->next = _ApplyNode(val,p->next);
}

头插

//头插
void Insert_Head(Linklist plist, int val)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

    plist->next = _ApplyNode(val,plist->next);
}

尾插

//尾插
void Insert_Tail(Linklist plist, int val)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	Insert(plist,val,ListLength(plist));
}

删除

1.先找到要删除节点的前一个节点,
2.再通过这个节点访问要删除的结点
3.让删除节点的前一个节点的next指向删除节点的next域
4.释放空间

//删除
void Delete(Linklist plist,int pos)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	if(pos < 0 || pos > ListLength(plist))
	{
		printf("Delete Fail : Pos is Error\n");
		return ;
	}

	Linklist p = GetNode(plist,pos-1);//要删除节点的前一个节点

	Linklist q = p->next;//要删除的节点pos位置

	p->next = q->next;

	free(q);
}

头删

//头删
void Delete_Head(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}
	Linklist q = plist ->next;
	if(q != NULL )
	{
		plist->next = q->next;
	}

	free(q);
}

尾删

//尾插
void Delete_Tail(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}
	Delete(plist,ListLength(plist));
}

判空

//判空
int ListEmpty(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		printf("Not have Head\n");//不是一个单链表
		return 0 ;
	}

	if(plist->next == NULL)//空
	{
		return 1;
	}
	return 0;
}

置空

//置空
void Clear(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	while(!ListEmpty(plist))
	{
		Delete_Head(plist);
	}
}

显示

//显示
void Show(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	Linklist p = plist->next;

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

逆置

void Reverse_list(Linklist plist)
 {
 	if(plist == NULL || plist->next == NULL)
 	{
 		return;
 	}
	 Linklist p= NULL;//记录当前要处理的节点位置

	 Linklist q = plist->next;//当前要处理结点的后一个结点
	 Linklist s = q->next;

	 while(1)
	 {
		 q->next = p;
		 p = q;
		 q = s;
		 if(q == NULL)
		 {
		 	break;
		 }
		s = s->next;
	}
	plist->next = p;
 }

判断两个单链表是否相交

若相交返回第一个相交的结点地址,若不相交返回空
算法: 必须通过结点的地址进行判断,不能通过data域进行判断

  1. 先通过比较两个结点的长度差值,让长的链表先走这个差值,因为对于两个相交的链表来说,相交后的部分应该有同一个尾结点,在相交前应该从同一逻辑位置开始遍历
  2. 两个链表的指针同时往后走,每走一步判断两个结点是否相等
Linklist IsIntersect(Linklist p1,Linklist p2)
{
	if(ListEmpty(p1) || ListEmpty(p2))
	{
		return NULL;
	}
    if(p1 == NULL || p2 == NULL)
	{
		return NULL;
	}

	int len1 = ListLength(p1);
	int len2 = ListLength(p2);

	if(len1 > len2)
	{
		for(int i = 0;i < len1 - len2;i++)
		{
			p1 = p1->next;
		}
	}
	else
	{
		for(int i = 0;i < len2 - len1;i++)
		{
			p2 = p2->next;
		}
	}
	while(p1 != NULL)
	{
		if(p1 == p2)
			return p1;
		
		p1 = p1->next;
		p2 = p2->next;
	}

	return NULL;
}

找到倒数第k个结点

时间复杂度尽可能低地找
算法: 用两个指针来实现,先让一个指针向后走k个位置,然后两个指针同步往后走,当之前的指针为空时,后面的指针就是倒数第k个位置

Linklist R_GetNode(Linklist plist,int k)
{
	if (plist == NULL || plist->next == NULL || k <= 0)
	{
		return NULL;
	}

	Linklist p = plist;
	Linklist q = plist;

	if(k <= 0) return NULL;

	for(int i = 0;i < k && p != NULL;i++)
	{
		p = p->next; 
	}

	//k值大于链表长度
	if(p == NULL)
	{
		return NULL;
	}

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

删除p结点(p是指向链表中的某一个不为尾结点的结点)o(1)

算法: 删除一个节点不是删除结点本身,而是要删除这个节点存储的值

int DeleteNodeofP(Linklist plist,Linklist p)
{
	if(plist == NULL || p == NULL)
	{
		return 0;
	}

	if(p->next == NULL)//p指向尾结点
	{
		Delete_Tail(plist);//O(n)
		return 1;
	}

	Linklist q = p->next;
	p->data = q->data;
	p->next = q->next;
	free(q);
	return 1;
}

判断单链表是否有环

//判断单链表是否有环
bool IsCircle(Linklist plist)
{
	if(plist->next == NULL || plist == NULL)
	{
		return false;
	}
	Linklist fast = plist;//快指针
	Linklist slow = plist;//慢指针

	while(fast != NULL && fast->next != NULL)//fast->next != NULL保证快指针一次走两步两个节点能够成功
	{
		fast = fast->next->next;
		slow = slow->next;
	
		if(fast == slow)
		{
			break;
		}
	}

	if(fast == NULL || fast->next == NULL)//链表长度为奇数fast->next为空,偶数时fast为空
		return false;
	else
		return true;
}

找到有环单链表入环的第一个节点

//找到有环单链表入环的第一个节点
Linklist InToCircle(Linklist plist)
{
	Linklist fast = plist;//快指针
	Linklist slow = plist;//慢指针

	while(fast != NULL && fast->next != NULL)
	{
		fast = fast->next->next;
		slow = slow->next;
	
		if(fast == slow)
		{
			break;
		}
	}

	if(fast == NULL || fast->next == NULL)//链表长度为奇数fast->next为空,偶数时fast为空
		return NULL;

	//公式分析:
	/*
	设起点到入环点的距离为x,入环点到相遇点的距离为y,相遇点再到入环点的距离为k
	则快指针走过x+y+(k+y)n(n为圈数)
	慢指针走过x+y
	所以有2*(x+y)= x+y+(k+y)n
	——》x=(k+y)(n-1) + k
	*/
	slow = plist;
	while(slow != fast)
	{
		slow = slow->next;
		fast = fast->next;
	}
	return slow;
}

头文件

#pragma once

/*typedef struct LNode
{
     union
      {
		int data;//存储数据节点
		int length;//头结点记录链表中节点的个数
	   };
	struct LNode* next;
}LNode;*/

//对于单链表节点类型的声明
typedef struct LNode
{
	int data;
	struct LNode* next;
}LNode;

typedef struct LNode* Linklist;

//链表初始化
void InitLinkList(Linklist plist);

//插入
void Insert(Linklist plist, int val, int pos);

//获取结点个数
int ListLength(Linklist plist);

//根据位置找结点
Linklist GetNode(Linklist plist,int pos);

//头插
void Insert_Head(Linklist plist, int val);

//尾插
void Insert_Tail(Linklist plist, int val);

//删除
void Delete(Linklist plist,int pos);

//头删
void Delete_Head(Linklist plist);

//尾插
void Delete_Tail(Linklist plist);

//判空
int ListEmpty(Linklist plist);

//置空
void Clear(Linklist plist);

//显示
void Show(Linklist plist);

//判断两个单链表是否相交
Linklist IsIntersect(Linklist p1,Linklist p2);

//找到倒数第k个结点
Linklist R_GetNode(Linklist plist,int k);

//删除p结点(p是指向链表中的某一个不为尾结点的结点)o(1)
int DeleteNodeofP(Linklist plist,Linklist p);

//判断单链表是否有环
bool IsCircle(Linklist plist);

//找到有环单链表入环的第一个节点
Linklist InTo(Linklist plist);

//单链表逆置
 void Reverse_list(Linklist plist);

测试用例

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

//LNode head;  //全局定义的头结点
int main()
{
    LNode head;   //栈区定义的头结点
    InitLinkList(&head);

	Insert_Head(&head,10);
	Show(&head);

	Insert_Tail(&head,20);
	Show(&head);

	Delete_Head(&head);
	Show(&head);

	Clear(&head);
	Show(&head);
}

功能实现

#include "singlelist.h"
#include<assert.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
static Linklist _ApplyNode(int val,Linklist next)
{
	Linklist s = (Linklist)malloc(sizeof(LNode));
	assert(s != NULL);
	if (s == NULL)
	{
		printf("Insert Fail : Apply Spave Error\n");
		return NULL;
	}

	s->data = val;
	s->next = next;
	return s;
	
}

//链表初始化
void InitLinkList(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	plist->next = NULL;
}

//获取结点个数
int ListLength(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
 		return 0 ;
	}

	int length = 0;
	Linklist p = plist->next;
	while (p)
	{
		length++;
		p = p->next;
	}

	return length;
}

//根据位置找结点
Linklist GetNode(Linklist plist,int pos)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return NULL;
	}

	if(pos < 0) return NULL;

	Linklist p = plist;

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

	return p;
}

//插入
void Insert(Linklist plist, int val, int pos)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	//判断pos位置是否合法
	if(pos < 0  || pos >ListLength(plist))
	{
		printf("Insert error : Pos is Error\n");
		return ;
	}

	Linklist p = GetNode(plist,pos);

	p->next = _ApplyNode(val,p->next);
}

//头插
void Insert_Head(Linklist plist, int val)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

    plist->next = _ApplyNode(val,plist->next);
}

//尾插
void Insert_Tail(Linklist plist, int val)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	Insert(plist,val,ListLength(plist));
}

//删除
void Delete(Linklist plist,int pos)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	if(pos < 0 || pos > ListLength(plist))
	{
		printf("Delete Fail : Pos is Error\n");
		return ;
	}

	Linklist p = GetNode(plist,pos-1);//要删除节点的前一个节点

	Linklist q = p->next;//要删除的节点,pos位置

	p->next = q->next;

	free(q);
}

//头删
void Delete_Head(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}
	Linklist q = plist ->next;
	if(q != NULL )
	{
		plist->next = q->next;
	}

	free(q);
}


//尾插
void Delete_Tail(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}
	Delete(plist,ListLength(plist));
}

//判空
int ListEmpty(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		printf("Not have Head\n");//不是一个单链表
		return 0 ;
	}

	if(plist->next == NULL)//空
	{
		return 1;
	}
	return 0;
}

//置空
void Clear(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	while(!ListEmpty(plist))
	{
		Delete_Head(plist);
	}
}
//显示
void Show(Linklist plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	Linklist p = plist->next;

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

//判断两个单链表是否相交,若相交则返回第一个相交的结点,若没有相交,则返回空
Linklist IsIntersect(Linklist p1,Linklist p2)
{
	if(ListEmpty(p1) || ListEmpty(p2))
	{
		return NULL;
	}
    if(p1 == NULL || p2 == NULL)
	{
		return NULL;
	}

	int len1 = ListLength(p1);
	int len2 = ListLength(p2);

	if(len1 > len2)
	{
		for(int i = 0;i < len1 - len2;i++)
		{
			p1 = p1->next;
		}
	}
	else
	{
		for(int i = 0;i < len2 - len1;i++)
		{
			p2 = p2->next;
		}
	}
	while(p1 != NULL)
	{
		if(p1 == p2)
			return p1;

		p1 = p1->next;
		p2 = p2->next;

		
	}

	return NULL;
}

//找到倒数第k个结点
Linklist R_GetNode(Linklist plist,int k)
{
	if (plist == NULL || plist->next == NULL || k <= 0)
	{
		return NULL;
	}

	Linklist p = plist;
	Linklist q = plist;

	if(k <= 0) return NULL;

	for(int i = 0;i < k && p != NULL;i++)
	{
		p = p->next; 
	}

	//k值大于链表长度
	if(p == NULL)
	{
		return NULL;
	}

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

//删除p结点(p是指向链表中的某一个不为尾结点的结点)o(1)
int DeleteNodeofP(Linklist plist,Linklist p)
{
	if(plist == NULL || p == NULL)
	{
		return 0;
	}

	if(p->next == NULL)//p指向尾结点
	{
		Delete_Tail(plist);
		return 1;
	}

	Linklist q = p->next;
	p->data = q->data;
	p->next = q->next;
	free(q);
	return 1;
}

//判断单链表是否有环
bool IsCircle(Linklist plist)
{
	Linklist fast = plist;//快指针
	Linklist slow = plist;//慢指针

	while(fast != NULL && fast->next != NULL)
	{
		fast = fast->next->next;
		slow = slow->next;
	
		if(fast == slow)
		{
			break;
		}
	}

	if(fast == NULL || fast->next == NULL)//链表长度为奇数fast->next为空,偶数时fast为空
		return false;
	else
		return true;
}

//找到有环单链表入环的第一个节点
Linklist InToCircle(Linklist plist)
{
	Linklist fast = plist;//快指针
	Linklist slow = plist;//慢指针

	while(fast != NULL && fast->next != NULL)
	{
		fast = fast->next->next;
		slow = slow->next;
	
		if(fast == slow)
		{
			break;
		}
	}

	if(fast == NULL || fast->next == NULL)//链表长度为奇数fast->next为空,偶数时fast为空
		return NULL;

	//画图分析可知slow和fast第一次相遇的位置距离入环点的距离等于头结点到入环点的距离
	slow = plist;
	while(slow != fast)
	{
		slow = slow->next;
		fast = fast->next;
	}
	return slow;
}

//单链表逆置
 void Reverse_list(Linklist plist)
 {
	 Linklist p= plist->next;//记录当前要处理的节点位置

	 Linklist q ;//当前要处理结点的后一个结点

      //由于第一个元素没有真正意义上的前驱,故在循环前要初始化头结点的指针域为NULL,用来表示它没有前驱,刚好合适
	 plist ->next = NULL;

	 while(p)
	 {
		 q = p->next;//q记录下p的后继结点
		 p->next = plist->next;//使p的前驱变后继
		 plist->next = p;//plist->next指向已处理的结点
		 p = q;//p指向下一个待处理的结点
	 }
 }
  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值