2022.3.15 带头结点的单链表

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

带头结点的单链表的形式:

有效数据结点堆里面申请来的,也就是malloc,头结点,系统开放的,不需要free。

2. 头结点没有单独的结构体设计,直接使用的是有效数据结点的结构体设计,这是因为头结点里面数据成员只需要一个成员---指针域,而有效数据结点里面的数据成员不仅有数据域还有指针域,所以直接都用有效数据结点的设计。

3. 实现

如果单独设计头结点结构体和数据结点结构体,有

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

  typedef int ELEM_TYPE;

 可以看出,头结点结构体设计和数据结点结构体设计中指针域都有,因此可以设计一个共同的结构体,只不过这个新的结构体中的数据域成员代表两个意思,有如下

 4.    .h文件

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


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


有效数据结点结构体设计总,不建议这样写
//typedef struct Node
//{
//	union   //这块联合体设计,如果上面有名字下面没有名字,不占内存
//	{
//		int length;
//		ELEM_TYPE data;//数据域,存放有效值
//	};
//	struct Node* next;//指针域,存放下一个有效数据结点的地址
//}Node,PNode;


有效数据节点结构体单独设计
//typedef struct Node
//{
//    ELEM_TYPE data;//数据域,存放有效值
//    struct Node* next;//指针域,存放下一个有效数据节点的地址
//    
//}Node, PNode;
头结点结构体单独设计
//typedef struct Head
//{
//    struct Node* next;//指针域,保存第一个有效数据节点的地址
//}Head, *PHead;


//带头结点的单链表操作函数
//初始化,头结点的数据成员全部赋初值
void Init_list(struct Node* plist);

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

//头插 ,是否成功,成功返回真,失败返回假,因此用bool类型
bool Insert_head(struct Node* plist, ELEM_TYPE val);

//尾插
bool Insert_tail(struct Node* plist, ELEM_TYPE val);

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

//头删
bool Del_head(struct Node* plist);

//尾删
bool Del_tail(struct Node* plist);

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

//按值删除
bool Del_val(struct Node* plist, ELEM_TYPE val);

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

//判空
bool Isempty(struct Node* plist);

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


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

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

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

//销毁2
void Destroy2(struct Node* plist);


//打印
void Show(struct Node* plist);

2.   .cpp文件

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


//初始化(对于头结点进行赋初值)
void Init_list(struct Node* plist)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return;
	}

	//plist->data;  头结点数据域不需要赋值
	plist->next = NULL;
}

//购买一个新节点
struct Node* 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 Insert_head(struct Node* plist, ELEM_TYPE val)
{
	//1.判断参数合法性
	assert(plist != NULL);
	if (plist == NULL)
	{
		return false;
	}
	//2.1不需要判满,申请新的节点(插入一个节点,malloc申请一个节点)
	struct Node* pnewnode = (struct Node*)malloc(1 * sizeof(struct Node));
	assert(pnewnode != NULL);
	if (pnewnode == NULL)  
	{
		return false;//说明动态内存申请失败
	}
	//2.2申请成功后将val值赋给新节点
	pnewnode->data = val;
	//pnewnode->next = NULL;//不用处理,下面对其进行
	//3.找到合适的插入位置(由于是头插函数,因此直接可以得到合适的插入位置)
	 
	//4.插入
	pnewnode->next = plist->next;//因为plist的next域指向首元素地址
	plist->next = pnewnode;
	return true;


}

//尾插
bool Insert_tail(struct Node* plist, ELEM_TYPE val)
{
	assert(plist != NULL);
	if (plist == NULL)
	{
		return false;//说明动态内存申请失败
	}
	//1.申请新节点、
	struct Node* pnewnode = BuyNode(val);
	assert(pnewnode != NULL);
	if(pnewnode==NULL)//购买失败
	{
		return false;
	}
	
	//2.找到合适的插入位置  让p指向插入位置的上一个节点,这就要求上一个节点不为空
	struct Node* p = plist;
	for ( p ; p->next != NULL; p = p->next);
	//此时指向尾节点

	//3.插入
	pnewnode->next = p->next;//pnewnode->next = NULL;
	p->next = pnewnode;
	return true;

}

//按位置插入
bool Insert_pos(struct Node* plist, int pos, ELEM_TYPE val)//pos=0就是头插,pos=length就是尾插
{
	//assert;
	assert(plist != NULL);
	assert(pos >= 0 && pos <= Getlength(plist));
    //1.购买新节点
	struct Node* pnewnode = BuyNode(val);
	assert(pnewnode != NULL);
	if (pnewnode == NULL)
	{
		return false;
	}
	//2.找到合适的插入位置
	struct Node* p = plist;
	for (int i = 0; i < pos; i++)
	{
		p = p->next;
	}
	//3.插入
	pnewnode->next = p->next;
	p->next = pnewnode;
	return true;
	
}
//头删
//注意:头删的时候,记得先让一个临时指针变量p指向待删除节点,然后再跨越指向
bool Del_head(struct Node* plist)
{
	//assert;
	if (Isempty(plist))
	{
		return false;
	}
	struct Node* p = plist->next;
	plist->next = p->next;//plist->next = plist->next->next;
	free(p);
	return true;
}

//尾删
bool Del_tail(struct Node* plist)
{
	//assert;
	if (Isempty(plist))
	{
		return false;
	}
	struct Node* p = plist;
	//让p指向尾节点
	for (p ; p->next != NULL; p = p->next);//此时,p指向尾节点
	struct Node* q = plist;
	for (q; q->next != p; q = q->next);//此时q停在尾节点p的前面
	
	q->next = p->next;//q->next = NULL;后面这个尽量不要写
	free(p);
	return true;

}

//按位置删  pos=0相当于头删,pos=length非法,只能pos=length-1就是尾删
bool Del_pos(struct Node* plist, int pos)
{
	assert(plist != NULL);
	assert(pos >= 0 && pos < Getlength(plist));
	if (Isempty(plist))
	{
		return false;
	}
	struct Node* p = plist;
	for (int i = 0; i <= pos; i++)
	{
		p = p->next;
	}
	struct Node* q = plist;
	for (int i = 0; i < pos; i++)
	{
		q = q->next;
	}
	q->next = p->next;
	free(p);
	return true;
}

//按值删除
bool Del_val(struct Node* plist, ELEM_TYPE val)
{
	//assert;
	struct Node*p = Search(plist, val);
	if (p == NULL)
	{
		return false;
	}
	struct Node* q = plist;
	for (q; q->next != p; q = q->next);
	//q在待删除节点p的前面
	q->next = p->next;
	free(p);
	return true;
}


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

//判空
bool Isempty(struct Node* plist)
{
	//assert;
	return plist->next == NULL;
}

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


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

//清空相当于直接销毁
void Clear(struct Node* plist)
{
	//Destroy1(plist);
}

//销毁1     malloc申请来的空间,全部释放掉
void Destroy1(struct Node* plist)
{
	//一直循环判断单链表里面还有没有节点,如果有,头删一次
	/*while (!Isempty(plist))
	{
		Del_head(plist);
	}
	plist->next = NULL;*/
	while (plist->next != NULL)
	{
		struct Node* p = plist->next;
		plist->next = p->next;
		free(p);
	}
	plist->next = NULL;
}


//销毁2
void Destroy2(struct Node* plist)//不建议用,有点难
{
	//assert;
	struct Node* p = plist->next;
	struct Node* q = NULL;//q先不要赋值为p->next,因为p有可能指向空

	plist->next = NULL;//接下来,与头结点没关系;、
	while (p != NULL)
	{
		q = p->next;
		free(p);
		p = q;
	}
}


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

 主函数文件.cpp

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include "2022.1.15.h"
/*
//上节课,结尾讲了单链表分为带头结点和不带头结点
//带头结点的单链表,相对比不带头结点的来说好实现
//本节课学习:主讲:带头结点的单链表
1.写带头结点的单链表结构体设计
2.写带头结点的单链表有哪些可执行操作
3.实现这些功能

头结点乜有单独设计其结构体,直接使用的是有效数据节点的结构体设计
(因为头结点里数据成员只需要一个:指针域)
(而有效数据节点里的数据成员)
*/

int main()
{
	struct Node head;
	Init_list(&head);    //初始化
	for (int i = 0; i < 20; i++)
	{
		Insert_pos(&head, i, i + 1);     //插入值1~20
	}
	Show(&head);
	printf("length = %d\n", Getlength(&head));

	Insert_head(&head, 100);   //头插
	Insert_tail(&head, 200);   //尾插
	Show(&head);
	printf("length = %d\n", Getlength(&head));

	Del_head(&head);    //头删
	Del_tail(&head);    //尾删
	Show(&head);
	printf("length = %d\n", Getlength(&head));

	Del_pos(&head, 4);   //按位置删除
	Show(&head);
	printf("length = %d\n", Getlength(&head));

	Del_val(&head, 14);    //按值删除
	Show(&head);
	printf("length = %d\n", Getlength(&head));

	return 0;
}

z运行结果:::

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值