ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

基础数据结构-单链表(不带头结点)

2022-01-17 22:04:10  阅读:136  来源: 互联网

标签:Node 结点 单链 struct no head next phead 数据结构


先写头文件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 no_head_Init_list(struct Node **phead);//struct Node ** == PNode *

//购买一个新节点
struct Node *no_head_BuyNode(ELEM_TYPE val);

//头插
bool no_head_Insert_head(PNode *phead, ELEM_TYPE val);

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

//按位置插入(pos=0 相当于头插  pos==length 相当于尾插)
bool no_head_Insert_pos(struct Node **phead, int pos, ELEM_TYPE val);

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

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

//按位置删(pos==0 相当于头删   pos==length-1 相当于尾删(pos==length非法))
bool no_head_Del_pos(struct Node **phead, int pos);
//按值删
bool no_head_Del_val(struct Node **phead, ELEM_TYPE val);

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

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

//判满  单链表不存在满这个概念

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

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

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

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

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

再写no_head_list.cpp文件

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "no_head_list.h"
//因为不带头结点的单链表没有头结点,所以我们之前使用上面for循环的函数都需要做修改
//但是像我们之前不需要前驱的操作,也就是不用让指针p指向头结点的操作,没有什么区别

//初始化函数(对于头指针进行赋初值)
void no_head_Init_list(struct Node **phead)//struct Node ** == PNode *
{
	//assert  phead!=NULL

	*phead = NULL;
	//修改一个指针,如果没有有效节点的话,将其指针指向NULL
	//这个指针保存第一个有效数据节点的地址   phead   *phead(保存的第一个有效数据的地址)   (*phead).next   
}

//购买一个新节点
struct Node *no_head_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 no_head_Insert_head(PNode *phead, ELEM_TYPE val)
{
	//assert
	assert(phead != NULL);
	if(NULL == phead)
	{
		return false;
	}
	//1.创建新节点
	struct Node* pnewnode = (struct Node *)malloc(1*sizeof(struct Node));
	assert(pnewnode != NULL);
	pnewnode->data = val;
	pnewnode->next = NULL;

	//2.找到合适的插入位置 (头插函数 已经找到插入位置)

	//3.插入
	pnewnode->next = *phead;
    *phead = pnewnode;

	return true;
}

//尾插
bool no_head_Insert_tail(struct Node **phead, ELEM_TYPE val)
{
	//assert

	if(no_head_IsEmpty(phead))//如果我是一个空链,则执行头插函数 不要执行尾插函数
	{
		return no_head_Insert_head(phead, val);
	}

	//1.创建新节点
	struct Node* pnewnode = no_head_BuyNode(val);
	assert(pnewnode!= NULL);

	//2.找到合适的插入位置
	struct Node *p;
	for( p= *phead; p->next!=NULL; p=p->next);//这里存在一个bug:如果是空链的话,*phead == p == NULL,那么语句2:p->next会报写入异常(在上面处理一下这个bug即可)
	//此时 指针p停留在尾结点,而不是尾结点的下一个节点

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

	return true;
}

//按位置插入(pos=0 相当于头插  pos==length 相当于尾插)
bool no_head_Insert_pos(struct Node **phead, int pos, ELEM_TYPE val)
{
	//assert   phead  pos
	assert(phead != NULL);
	assert(pos >=0 && pos<=no_head_Get_length(phead));//pos == length合法  相当于尾插 但是这个位置不能用于删除
	if(pos == 0)
	{
		return no_head_Insert_head(phead, val);
	}

	//1.创建新节点
	struct Node* pnewnode = no_head_BuyNode(val);
	assert(pnewnode!= NULL);

	//2.找到合适的插入位置    
	struct Node *p = *phead;  
	for(int i=1; i<pos; i++)这里存在一个bug:如果pos = 0,p没有办法跑到合适的位置,提前处理掉即可
	{
		p=p->next;
	}

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

//头删
bool no_head_Del_head(struct Node **phead)
{
	//assert
	//删除操作:需要判空
	if(no_head_IsEmpty(phead)) 
	{
		return false;
	}

	struct Node *p =*phead;
	*phead = p->next;
	free(p);

	return true;
}

//尾删
bool no_head_Del_tail(struct Node **phead)
{
	//assert
	//两种风险   第一种:q有可能为NULL(由于空链导致)
	if(no_head_IsEmpty(phead))
	{
		return false;
	}
	//什么情况下:q不为NULL,但是q->next为NULL?  //有节点,但是仅有一个节点
	//两种风险   第二种:q->next有可能为NULL(由于导致)
	if((*phead)->next == NULL) //仅有一个首元素节点
	{
		return no_head_Del_head(phead);//仅有一个节点,尾删和头删没区别
	}


	//用第二种方案去找p和q的位置:先找q再找p
	struct Node *q = *phead;
	for(q; q->next->next!=NULL; q=q->next);//q向后一次项探测两步(有bug风险:1.p可能为NULL  2.p->next为NULL)
	//此时q停在倒数第二个节点(尾删的待删除节点的上一个节点)

	struct Node *p = q->next;//p通过q来直接指向待删除节点

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


}

//按位置删(pos==0 相当于头删   pos==length-1 相当于尾删(pos==length非法))
bool no_head_Del_pos(struct Node **phead, int pos)
{
	assert(phead != NULL);
	assert(pos >=0 && pos<no_head_Get_length(phead));

	if(no_head_IsEmpty(phead))
	{
		return false;
	}
	if(pos == 0)
	{
		return no_head_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 no_head_Del_val(struct Node **phead, ELEM_TYPE val)
{
	if((*phead)->data  == val)//判断首元素的值  如果  == val
	{
		return no_head_Del_head(phead);
	}

	struct Node *p = no_head_Search(phead, val);
	if(p == NULL)
	{
		return false;
	}
	
	//此时  p指向待删除节点
	//q应该指向p的上一个节点
	struct Node *q = *phead;
	for(q; q->next!=p; q=q->next);//存在一个bug:如果要删除的值就是首元素的值,这时,q没有合适的位置去指向

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

}

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

//判空
bool no_head_IsEmpty(struct Node **phead)
{
	//assert

	return *phead == NULL;
}

//判满  单链表不存在满这个概念

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

//清空  相当于直接调用销毁
void no_head_Clear(struct Node **phead)
{
	no_head_Destroy(phead);
}

//销毁1(malloc申请来的空间 全部释放掉)
void no_head_Destroy(struct Node **phead)
{
	//assert

	while(*phead != NULL)
	{
		struct Node *p = *phead;
		*phead = p->next;
		free(p);
	}

	*phead = NULL;
}

//销毁2
void no_head_Destroy2(struct Node **phead)
{
	//assert
	struct Node *p = *phead;
	struct Node *q;

	*phead = NULL;

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

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

最后在主函数中测试一下

#include <stdio.h>
#include "assert.h"
#include <stdlib.h>
#include <vld.h>
//#include "sqlist.h"
//#include "Dsqlist.h"
//#include "list.h"
#include "no_head_list.h"

//不带头结点的单链表测试用例
int main()
{
	struct Node* head;
	no_head_Init_list(&head);

	for(int i=0; i<20; i++)
	{
		no_head_Insert_pos(&head, i, i+1);
	}
	no_head_Show(&head);

	no_head_Insert_head(&head, 100);
	no_head_Insert_tail(&head, 200);
	no_head_Show(&head);
	printf("length = %d\n", no_head_Get_length(&head));

	no_head_Del_head(&head);
	no_head_Del_tail(&head);
	no_head_Show(&head);

	no_head_Del_pos(&head, 4);
	no_head_Del_val(&head, 14);
	no_head_Show(&head);

	printf("length = %d\n", no_head_Get_length(&head));

	//Destroy(&head);
	no_head_Destroy2(&head);
	//printf("%d\n", sizeof(struct Node));

	return 0;

	return 0;
}

标签:Node,结点,单链,struct,no,head,next,phead,数据结构
来源: https://blog.csdn.net/hi332516_1/article/details/122549317

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有