数据结构-单链表的两种实现方式

单链表的两种实现方式:带头结点和不带头结点

需要注意的地方:

1、带头结点和不带头结点在实现插入和删除操作时的区别

2、封装的方便之处(见InsertPriorNode(LNode *p,ElemType e)),能够使代码更简洁,更加清晰

3、带头结点方式中,头结点不装入数据,并且看作成第0个结点。不带头结点方式,没有头结点,从第1个结点开始。

4、注意一些特判情况

例如:

按位序插入(带头结点),ListInsert(LinkList &L,int i,ElemType e),在第i个位置插入数据时,判断i的位置过大还是过小

删除操作,ListDelete(LinkList &L,int i,ElemType &e),删除第i个位置结点时,判断i的位置过大还是过小

后插操作,InsertNextNode(LNode *p,ElemType e),在p结点之后插入元素e时,需要判断p是否为NULL

前插操作,InsertPriorNode(LNode *p,ElemType e),在p结点之前插入元素e时,需要判断p是否为NULL

在分配内存空间给新结点s时,

Node *s=(LNode *)malloc(sizeof(LNode));
if(s==NULL)//内存分配失败
    return false;

总的来说就是需要判断,结点是否为NULL,插入或者删除的位置是否合法,内存空间的判断。

 

 

 

不带头结点的实现方式

/*写代码不方便*/
#include<iostream>
using namespace std;
struct LNode{
    ElemType data;//数据域
    struct LNode *next;//指针域,指针指向下一个节点
}LNode,*LinkList;//LNode结点,LinkList单链表
/*LinkList和LNode是等价的,LinkList强调的是链表,LNode强调的是结点,合适的地方使用合适的名字,代码可读性更高*/
bool IniList(LinkList &L)
{
    L=NULL;//空表,暂时还没有任何结点,防止脏数据
    return true;
}
bool Empty(LinkList L)
{
    if(L==NULL)
        return true;
    else
        return false;
    //return (L==NULL);//更简洁的写法
}

/*按位序插入(不带头结点)*/
/*插入操作,在表L中的第i个位置上插入指定元素e*/
/*即找到第i-1个结点,将新结点插入其后*/
void ListInsert(LinkList &L,int i,ElemType e)
{
    /*手动完成+判断平均时间复杂度:O(n)*/
    if(i<1)
        return false;
    if(i==1)//插入第一个结点的操作与其他结点操作不同
    {
        LNode *s=(LNode *)malloc(sizeof(LNode));
        s->data=e;
        s->next=L;
        L=s;//头指针指向新结点
        return true;
    }
    LNode *p;//指针p指向当前扫描到的结点
    int j=1;//当前p指向的是第几个结点
    p=L;//L指向头结点,头结点是第0个结点(不存数据)
    while(p!=NULL&&j<i-1)//循环找到第i-1个结点
    {
        p=p->next;
        j++;
    }
    if(p==NULL)//i值不合法
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    s->next=p->next;
    p->next=s;//将结点s连到p之后
    return true;//插入成功
}

void test()
{
    LinkList L;//声明一个指向单链表的指针,此处并没有创建一个结点
    //初始化一个空表
    InitList(L);

}

带头结点的实现方式

/*写代码更方便*/
#include<iostream>
#include<stdlib.h>
using namespace std;
#define ElemType int
struct LNode{
    ElemType data;//数据域
    struct LNode *next;//指针域,指针指向下一个节点
}LNode,*LinkList;//LNode结点,LinkList单链表

//初始化一个单链表(带头结点)
/*头结点看成第0个结点*/
bool IniList(LinkList &L)
{
    L=(LNode *)malloc(sizeof(LNode));//分配一个头结点,头结点不存储数据
    if(L==NULL)//内存不足,分配失败
        return false;
    L->next=NULL;//头结点之后暂时还没有节点
    return true;
}
//判断单链表是否为空(带头结点)
bool Empty(LinkList L)
{
    if(L->next==NULL)
        return true;
    else
        return false;
    //return (L->next==NULL);//更简洁的写法
}

/*按位序插入(带头结点)*/
/*插入操作,在表L中的第i个位置上插入指定元素e*/
/*即找到第i-1个结点,将新结点插入其后*/
void ListInsert(LinkList &L,int i,ElemType e)
{
    /*手动完成+判断平均时间复杂度:O(n)*/
    if(i<1)
        return false;
    LNode *p;//指针p指向当前扫描到的结点
    int j=0;//当前p指向的是第几个结点
    p=L;//L指向头结点,头结点是第0个结点(不存数据)
    while(p!=NULL&&j<i-1)//循环找到第i-1个结点
    {
        p=p->next;
        j++;
    }
    if(p==NULL)//i值不合法
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    s->next=p->next;
    p->next=s;//将结点s连到p之后
    return true;//插入成功
}


/*指定结点的后插操作*/
/*后插操作:在p结点之后插入元素e*/
bool InsertNextNode(LNode *p,ElemType e)
{
    /*手动完成+判断平均时间复杂度:O(1)*/
    if(p==NULL)
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    if(s==NULL)//内存分配失败
        return false;
    s->data=e;//用结点s保存数据元素e
    s->next=p->next;
    p->next=s;//将结点s连到p之后
    return true;
}

/*指定结点的前插操作*/
/*前插操作:在p结点之前插入元素e*/
bool InsertPriorNode(LNode *p,ElemType e)
{
    /*手动完成+判断平均时间复杂度:O(1)*/
    if(p==NULL)
        return false;
    LNode *s=(LNode *)malloc(sizeof(LNode));
    if(s==NULL)//内存分配失败
        return false;
    s->next=p->next;
    p->next=s;//将结点s连到p之后
    s->data=p->data;//将结点p,q元素互换
    p->data=e;
    return true;
}

/*删除操作*/
/*删除表L中第i个位置的元素,并用e返回删除元素的值*/
/*即找到第i-1个结点,并将其指针指向第i+1个结点,并且释放第i个结点*/
bool ListDelete(LinkList &L,int i,ElemType &e)
{
    /*手动完成+判断平均时间复杂度:O(n)*/
    if(i<1)
        return false;
    LNode *p;//指针p指向当前扫描到的结点
    int j=0;//当前p指向的是第几个结点
    p=L;//L指向头结点,头结点是第0个结点(不存数据)
    while(p!=NULL&&j<i-1)//循环找到第i-1个结点
    {
        p=p->next;
        j++;
    }
    if(p==NULL)//i值不合法
        return false;
    if(p->data==NULL)//第i-1个结点之后已无其他结点
        return false;
    LNode *q=p->next;//令q指向被删除结点
    e=q->data;//用e返回被删除的元素的值
    p->next=q->next;//将*q结点从链中断开
    free(q);//释放结点的存储空间
    //将结点s连到p之后
    return true;//插入成功
}

/*删除指定结点p*/
/*如果p是最后一个结点,则有bug,只能从表头开始一次寻找p的前驱*/
bool DeleteNode(LNode *p)
{
     /*手动完成+判断平均时间复杂度:O(1)*/
    if(p==NULL)
        return false;
    LNode *q=p->next;//令q指向*p的后继结点
    p->data=p->next->data;//和后继结点交换数据域
    //上一句等价于p->data=q->data;
    p->next=q->next;//将*q结点从链中断开
    free(q);//释放后继结点的存储空间
    return true;
}

/*封装,避免重复代码,简洁、容易维护*/
/*按位查找*/
/*按位查找,返回第i个元素(带头结点)*/
LNode * GetElem(LinkList L,int i)
{
    if(i<0)
        return NULL;
    LNode *p;//指针p指向当前扫描的结点
    int j=0;//当前p指向的是第几个结点
    p=L;//L指向头结点,头结点是第0个结点(不存数据)
    while(p!=NULL&&j<i)//循环找到第i个结点
    {
        p=p->next;
        j++;
    }
    return p;
}


/*按值查找*/
/*找到数据域==e的结点*/
LNode *LocateElem(LinkList L,ElemType e)
{
    /*时间复杂度:O(n)*/
    LNode *p=L->next;//从第1个结点开始查找数据域为e的结点
    while(p!=NULL&&p->data!=e)
        p=p->next;
    return p;//找到后返回该结点指针,否则返回NULL
}

/*求表的长度*/
int Length(LinkList L)
{
    /*O(n)*/
    int len=0;//统计表长
    LNode *p=L->next;
    while(p!=NULL)
    {
        p=p->next;
        len++;
    }
    return len;
}

void test()
{
    LinkList L;//声明一个指向单链表的指针,此处并没有创建一个结点
    //初始化一个空表
    InitList(L);

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值