带头结点的单链表的形式:
有效数据结点堆里面申请来的,也就是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运行结果:::