单链表的两种实现方式:带头结点和不带头结点
需要注意的地方:
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);
}