Day04单链表学习

特点:逻辑相邻但是物理上不一定相邻。不能直接随机访问

实现方式:带头结点的和不带头结点的

今天先来闲谝带头结点的结构体设计

使用工具:一级指针,利用数据域和指针域传起来的单链表(铁链子)

part 1需要用到的头文件

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "list.h"//这个是我们自己设计的头文件

part 2单链表的结构体设计

typedef int ELEM_TYPE;//先用typedef将int重新命名为ELEM_TYPE,如果后面使用double数据直接把int改为double即可
struct Node{
    ELEM_TYPE data;//用于存放数据
    struct Node *next;//指针域,用于存储下一个节点的地址
}Node,*PNode;

Part 3单链表的增删改查

2.1 初始化

2.1.1函数代码实现

void Init_list(struct Node* plist){
    assert(plist!=NULL);//安全性处理,断言头结点不为空
    plist->data=NULL;//头结点不需要数据域可以省略
    plist->next =NULL;
}

2.2 头插

2.2.1函数代码实现

bool Insert_head(PNode plist, ELEM_TYPE val){
    assert(plist!=NULL);
    struct Node*pnewnode=(struct Node*)malloc(sizeof(struct Node));//购买新节点,也可以专门使用一个函数去实现这个功能
    assert(pnewnode!=NULL);//判断动态内存是否申请成功
    pnewnode->data=val;//将值传递给新购买的节点的数据域
    pnewnode->next=plist->next;
    plist->next=pnewnode;
    return true;
}

2.2.2头插分析

 

单链表特点就是屋里不相邻,因此不能直接插入

定义一个新节点,让新节点的数据域等于要插入的值

找到插入位置:头插永远是插到头节点的后面

然后先将新节点的指针域保存为头结点的位置(因为头结点的指针域保存的是下一个节点的地址我们可以使用这个地址找到其他节点的位置),然后将新节点的地址赋值给头结点next域

插入:一定要先修改新节点的next域,后修改带插入节点的next域

防止找不到后面元素

2.3尾插

2.3.1函数代码实现

bool Insert_tail(PNode plist, ELEM_TYPE val){
    assert(plist!=NULL);
    struct Node*pnewnode=(struct Node*)malloc(sizeof(struct Node));
    assert(pnewnode!=NULL);
    pnewnode->data=val;
    struct Node*p=plist;//购买一个新节点指向头函数
    for(;p->next!=NULL;p=p->next);
    pnewnode->next=p->next;
    p->next=pnewnode;
    return true;
    
}

2.3.2尾插分析

 

先定义一个p指针指向头结点,利用循环判断p->next是否为空(判断p下一个节点是否存在),将p转到尾结点,然后购买一个新节点  ,申请一个临时指针q指向待删除节点的上一个节点(前驱)  q指向倒数第二个节点,跨越指向。q->next域存放p->next(NULL);

2.4按位置插

2.4.1函数代码实现

bool Insert_pos(PNode plist, int pos, ELEM_TYPE val){
    assert(plist!=NULL);
    assert(pos>=0&&pos<=GetLength(plist));
    struct Node*pnewnode=(struct Node*)malloc(sizeof(struct Node));
    assert(pnewnode!=NULL);
    pnewnode->data=val;
    struct Node*p=plist;
    for(int i=0;i<pos;i++){
        p=p->next;
    }
    pnewnode->next=p->next;
    p->next=pnewnode;
    return true;
}

2.4.2按位置插分析

 

首先要判断pos的值,pos>=0&&pos<=单链表的有效长度,如果pos等于单链表的有效长度,即就是尾插。

先定义一个新指针p让p指向头结点,利用for循环将p指向要插入的位置(pos=几,则指针p从头节点

开始出发,向后走pos步)----->找到插入位置

购买一个新节点,数据域保存要插入的值,指针域保存p->next(p的位置在pnewnode前面,他存的地址就是后面一连串的地址,防止后续节点找不见),然后p的next域存新节点的地址

2.5头删

2.5.1函数代码实现

bool Del_head(PNode plist){
    assert(plist!=NULL);
    if(IsEmpty(plist))
    struct Node*p=plist->next;
    plist->next=p->next;
    free(p);
    return true;
}

 

2.5.2头插分析

 

申请两个临时指针p,q;p指向待删除节点,q指向待删除节点上一个节点(前驱);使用for循环分别找到待删除节点的位置,和其上一个节点的位置,然后跨越指向(q->next保存的是待删除节点的地址,p->next保存的是下一个节点的地址,让q->next=p->next这样q的next域就存放的是待删除节点的下个节点的地址)然后将待删除节点释放掉就好。但是头删有特殊性,他待删除的元素前驱就是头结点,因为不需要申请q指针。

2.6尾删

2.6.1函数代码实现

bool Del_tail(PNode plist)
{
	//0.安全性处理  不仅仅判断头结点是否存在,还需要判断是否是空链表
	//如果不是空链表,则代表至少有一个有效节点
	assert(plist != NULL);
	if(IsEmpty(plist))
	{
		return false;
	}

	//1.申请一个临时指针p指向待删除节点   p指向倒数第一个节点(尾结点)
	struct Node *p = plist;
	for(; p->next!=NULL; p=p->next);
	//此时,for循环执行结束,指针p指向尾结点

	//2.申请一个临时指针q指向待删除节点的上一个节点(前驱)  q指向倒数第二个节点
	struct Node *q = plist;
	for(; q->next!=p; q=q->next);	//for( ; q->next->next!=NULL; q=q->next);

	//3.跨越指向
	q->next = p->next;

	//4.释放待删除节点
	free(p);

	return true;
}

2.6.2尾删分析

 

2.7按位置删

2.7.1函数代码分析

bool Del_pos(PNode plist, int pos)
{
	//0.安全性处理   
	assert(plist!=NULL);
	assert(pos>=0 && pos<GetLength(plist));

	//这里先找q,再找p
	//1.申请一个临时指针q指向待删除节点的上一个节点(前驱) 
	struct Node *q = plist;
	for(int i=0; i<pos; i++)
	{
		q = q->next;
	}

	//2.申请一个临时指针p指向待删除节点,将q的next给p
	struct Node *p = q->next;

	//3.跨越指向
	q->next = p->next;

	//4.释放
	free(p);

	return true;
}

2.7.2按位置插分析

 

先申请一个q指针指向头结点,循环一直到pos位置前这时q存的就是要删除的节点的地址;申请一个p指针让他等于待删除节点的next域即就是后面节点存放地址,然后把将后面节点的地址给q的next域;然后把待删出的节点(即就是p)释放掉

2.8按值删

2.8.1函数实现

bool Del_val(PNode plist, ELEM_TYPE val){
    assert(plist!=NULL);
    struct Node*p=Search(plist,val);
    if(p==NULL){
        return false;
    }
    struct Node*q=plist;
    for(;q->next!=p;q=q->next);
    q->next=p->next;
    free(p);
    return true;
}

2.8.2按值删分析

先利用Search函数找到要删除节点的位置,然后申请一个q节点=头结点,利用for循环当q的next域不等于p地址,循环后将要删除的p节点的next域保存在q的next域,跨节点,防止后续节点丢失,最后释放p节点

2.9查找

2.9.1函数代码实现

struct Node* Search(PNode plist, ELEM_TYPE val){
    assert(plist!=NULL);
    struct Node*p=plist->next;
    for(;p!=NULL;p=p->next){
        if(plist->data==val){
            return p;
        }
    }
    return NULL;
}

2.9.2查找分析

使用for循环找到data域与value一样的并将地址返回给主函数

2.10判空

2.10.1函数代码分析

bool IsEmpty(PNode plist){
    assert(plist!=NULL);
    return plist->next==NULL;
}

2.10.2判空分析

当头结点的指针域都是空的时候,说明并没有存储后续节点,进而可以判断是否为空

2.11清空&&销毁

2.11.1函数代码实现

void Clear(PNode plist)
{
      assert(plist!=NULL);
    Destroy(plist);
}

void Destory1(PNode plist){
    assert(plist!=NULL);
    while(plist->next!=NULL){
        struct Node*p=plist->next;
        plist->next=p->next;
        free(p);
    }
}
void Destory2(PNode plist){
     assert(plist!=NULL);
     struct Node*q=NULL;
     struct Node*p=plist->next;
    while(p!=NULL){
        q=p->next;
        free(p);
        p=q;
        
    }
    
}

2.11.2清空&&销毁分析

第一种方法就是无限头删,不停删除第一个值

第二种方法使用两个指数合作,循环释放后续节点,q-.next指向p->next的位置,然后释放p,这样交错下去

2.12打印

2.12.1函数代码实现

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

2.12.2打印分析

让指针p指向第一个有效节点,使用for循环遍历所有节点并将其数据域打印出来

2.13获取有效值个数

2.13.1函数代码分析

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

 

2.13.2获取有效值分析

让指针p指向第一个有效节点,使用for循环遍历所有节点并将其数据域打印出来

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值