2022.3.17 不带头结点的单链表

69 篇文章 3 订阅
21 篇文章 0 订阅

主要掌握带头结点的,不带头结点的偏难!!!

1. 不带头结点的单链表如下图

 

2. 结构体设计

no_head_list.h文件

#pragma once  //防止头文件重复


typedef int ELEM_TYPE;

typedef struct Node
{
	ELEM_TYPE data;//数据域(1.头结点:不保存任何数据 2.有效数据节点:保存有效值)
	struct Node* next;//指针域(1.保存第一个元素的地址   2.有效数据节点:保存下一个有效数据节点的地址)
}Node, *PNode;


//不带头结点的单链表操作函数---增删改查

//初始化函数,头结点的数据成员全部赋初值
void nohead_Init_list(struct Node** phead);  //PNode* plist==struct Node**plist

//购买一个新节点,记得只要是插入函数就得购买一个新节点
struct Node* nohead_BuyNode(ELEM_TYPE val);

//头插 ,是否成功,成功返回真,失败返回假,因此用bool类型
bool nohead_Insert_head(PNode* phead, ELEM_TYPE val);

//尾插
bool nohead_Insert_tail(struct Node** phead, ELEM_TYPE val);

//按位置插入
bool nohead_Insert_pos(struct Node** phead, int pos, ELEM_TYPE val);//pos=0就是头插,pos=length就是尾插

//头删
bool nohead_Del_head(struct Node** phead);

//尾删
bool nohead_Del_tail(struct Node** phead);

//按位置删  pos=0相当于头删,pos=length非法,只能pos=length-1就是尾删
bool nohead_Del_pos(struct Node** phead, int pos);

//按值删除
bool nohead_Del_val(struct Node** phead, ELEM_TYPE val);

//获取值位置,如果值存在,返回其地址,不然返回空
struct Node* nohead_Search(struct Node** phead, ELEM_TYPE val);

//判空
bool nohead_Isempty(struct Node** phead);

//判满,单链表不需要这个,因为用malloc申请,无限空间


//获取单链表有效数据结点个数
int nohead_Getlength(struct Node** phead);

//清空相当于直接销毁
void nohead_Clear(struct Node** phead);

//销毁1     malloc申请来的空间,全部释放掉
void nohead_Destroy1(struct Node** phead);

//销毁2
void nohead_Destroy2(struct Node** phead);


//打印
void nohead_Show(struct Node** phead);

 no_head_list.cpp文件

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include"no_head_list.h"

//访问二级指针,通过解引用变成一级指针再修改其中的东西,一级指针取地址升一级变为二级指针
void nohead_Init_list(struct Node** phead)  //PNode* plist==struct Node**plist
{
	assert(phead != NULL);
	if (phead == NULL)
	{
		return;
	}
	*phead = NULL;  //不能用(*phead).next,这个只代表的是指针域,初始化是将整个链表置空
	//修改一个指针,如果没有有效节点的话,将其指针指向空
	//这个指针保存第一个有效数据节点的地址  phead->二级指针    *phead->一级指针,保存第一个有效数据的地址
}

//购买一个新节点,记得只要是插入函数就得购买一个新节点
struct Node* nohead_BuyNode(ELEM_TYPE val)
{
	struct Node* pnewnode = (struct Node*)malloc(1 * sizeof(struct Node));
	assert(pnewnode != NULL);
	if (pnewnode == NULL)
	{
		return NULL;//说明动态内存申请失败
	}

	pnewnode->data = val;//申请成功  数据域
	pnewnode->next = NULL;//指针域

	return pnewnode;
}

//头插 ,是否成功,成功返回真,失败返回假,因此用bool类型
bool nohead_Insert_head(PNode* phead, ELEM_TYPE val)
{
	assert(phead != NULL);
	if (phead == NULL)
	{
		return false;
	}
	//创建新节点
	struct Node* pnewnode = (struct Node*)malloc(1 * sizeof(struct Node));
	assert(pnewnode != NULL);
	if (pnewnode == NULL)
	{
		return false;
	}
	pnewnode->data = val;
	pnewnode->next = NULL;

	//找到合适的插入位置

	//插入
	pnewnode->next = *phead;
	*phead = pnewnode;
	return true;
}

//尾插
bool nohead_Insert_tail(struct Node** phead, ELEM_TYPE val)
{
	assert(phead != NULL);
	if (phead == NULL)
	{
		return false;
	}
	//判断是否为空链,如果单链表是一个空链,那么对它执行尾插操作,可以直接替换成进行头插操作,即尾插函数
	if (nohead_Isempty(phead))
	{
		return nohead_Insert_head(phead, val);
    }
	//创建新节点
	struct Node* pnewnode = nohead_BuyNode(val);
	assert(pnewnode != NULL);
	if (pnewnode == NULL)
	{
		return false;
	}
	
	//找到合适插入位置
	struct Node* p = *phead;//指向有效数据结点的首元素地址
	for (p; p->next != NULL; p = p->next);//此时,指针p停留在尾节点,必须是有效数据节点存在,不存在的话,语句2会报错
	//插入
	pnewnode->next = p->next;
	p->next = pnewnode;
	return true;
}

//按位置插入
bool nohead_Insert_pos(struct Node** phead, int pos, ELEM_TYPE val)
{
	assert(phead != NULL);
	if (phead == NULL)
	{
		return false;
	}
	assert(pos >= 0 && pos <= nohead_Getlength(phead));
	if (pos == 0)
	{
		return nohead_Insert_head(phead, val);
	}
	//创建新节点
	struct Node* pnewnode = nohead_BuyNode(val);
	assert(pnewnode != NULL);
	if (pnewnode == NULL)
	{
		return false;
	}
	//找到合适插入位置
	struct Node* p = *phead;//pos=0,头插
	for (int i = 1; i < pos; i++)
	{
		p = p->next;
	}

	//插入
	pnewnode->next = p->next;
	p->next = pnewnode;
	return true;
}

//头删
bool nohead_Del_head(struct Node** phead)
{
	assert(phead != NULL);
	if (phead == NULL)
	{
		return false;
	}

	if (nohead_Isempty(phead))
	{
		return false;
	}
	//先排除空链的可能性
	struct Node* p = *phead;
	*phead = p->next; //如果是空链,存在bug:p->next 出错
	free(p);

	return true;
}

//尾删
bool nohead_Del_tail(struct Node** phead)
{
	assert(phead != NULL);
	if (phead == NULL)
	{
		return false;
	}
	//两种风险:第一种  q有可能为NULL(由于空链导致)
	if (nohead_Isempty(phead))
	{
		return false;
	}
	//第二种风险:q->next有可能为NULL (有节点,但是只有一个节点)
	if ((*phead)->next == NULL)//仅有一个元素节点,头删和尾删一样
	{
		return nohead_Del_head(phead);
	}
	//从q->next->next去找p和q的位置
	struct Node* q = *phead;
	for (q; q->next->next != NULL; q = q->next);//q一次性向后探测两步
	
	struct Node* p = q->next;//p通过q来直接指向待删除节点

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

	return true;
}

//按位置删  pos=0相当于头删,pos=length非法,只能pos=length-1就是尾删
bool nohead_Del_pos(struct Node** phead, int pos)
{
	assert(phead != NULL);
	if (phead == NULL)
	{
		return false;
	}
	assert(pos >= 0 && pos <= nohead_Getlength(phead));
	if (nohead_Isempty(phead))
	{
		return false;
	}
	if (pos == 0)//触发异常,特殊处理
	{
		return nohead_Del_head(phead);
	}
	struct Node* q = *phead;
	for (int i = 1; i < pos; i++)
	{
		q = q->next;
	}

	struct Node* p = q->next;//跨越指向
	q->next = p->next;
	free(p);

	return true;
}

//按值删除
bool nohead_Del_val(struct Node** phead, ELEM_TYPE val)
{
	if ((*phead)->data == val)
	{
		return nohead_Del_head(phead);
	}
	struct Node* p = nohead_Search(phead, val);
	if (p == NULL)
	{
		return false;
	}
	//此时,p指向待删除节点
	//q应该指向p的上一个节点
	struct Node* q = *phead;
	for (q; q->next != p; q = q->next);

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

//获取值位置,如果值存在,返回其地址,不然返回空
struct Node* nohead_Search(struct Node** phead, ELEM_TYPE val)
{
	struct Node* p = *phead;
	for (p; p != NULL; p = p->next)
	{
		if (p->data == val)
		{
			return p;
		}
	}
	return NULL;
}

//判空
bool nohead_Isempty(struct Node** phead)
{
	return *phead == NULL;
}

//判满,单链表不需要这个,因为用malloc申请,无限空间


//获取单链表有效数据结点个数
int nohead_Getlength(struct Node** phead)
{
	int count = 0;
	struct Node* p = *phead;
	for (p; p != NULL; p = p->next)
	{
		count++;
	}
	return count;
}

//清空相当于直接销毁
void nohead_Clear(struct Node** phead)
{
	nohead_Destroy1(phead);
}

//销毁1     malloc申请来的空间,全部释放掉
void nohead_Destroy1(struct Node** phead)
{
	while (*phead != NULL)
	{
		struct Node* p = *phead;
		*phead = p->next;
		free(p);
	}
	*phead = NULL;
}

//销毁2
void nohead_Destroy2(struct Node** phead)
{
	struct Node* p = *phead;
	struct Node* q;
	*phead = NULL;
	while (p != NULL)
	{
		q = p->next;
		free(p);
		p = q;
	}
}


//打印
void nohead_Show(struct Node** phead)
{
	struct Node* p = *phead;
	for (p; p != NULL; p = p->next)
	{
		printf("%d ", p->data);
	}
	printf("\n");
}

no_head_list.cpp主函数文件

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"no_head_list.h"



int main()
{
	struct Node *head;
	nohead_Init_list(&head);    //初始化
	for (int i = 0; i < 20; i++)
	{
		nohead_Insert_pos(&head, i, i + 1);     //插入值1~20
	}
	nohead_Show(&head);
	printf("length = %d\n", nohead_Getlength(&head));

	nohead_Insert_head(&head, 100);   //头插
	nohead_Show(&head);
	nohead_Insert_tail(&head, 200);   //尾插
	nohead_Show(&head);
	printf("length = %d\n", nohead_Getlength(&head));

	nohead_Insert_pos(&head, 4, 28);
	nohead_Show(&head);

	nohead_Del_head(&head);    //头删
	nohead_Show(&head);
	nohead_Del_tail(&head);    //尾删
	nohead_Show(&head);
	printf("length = %d\n", nohead_Getlength(&head));

	nohead_Del_pos(&head, 4);   //按位置删除
	nohead_Show(&head);
	printf("length = %d\n", nohead_Getlength(&head));

	nohead_Del_val(&head, 14);    //按值删除
	nohead_Show(&head);
	printf("length = %d\n", nohead_Getlength(&head));

	nohead_Destroy1(&head);

	return 0;
}

运行结果:

 总结:不带头结点的单链表比带头结点的单链表难,主要是在于处理一些小细节,比如插入,删除要判断有效数据节点是否存在,或者有效数据节点有且仅有一个,这些在写时要特别注意!!!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值