按位序插入(带头结点)
ListInsert(&L,i,e):插入操作。在表中的第i个位置上插入制定元素e。
我们采用的方式是找到第i-1个结点,将新的结点插入其后。只需将i-1结点的next的指针指向新的结点,然后再让新的结点的next指针指向第i个结点。
如图所示,可看出这个是一个有头结点的单链表,在外面在a1结点的前面插入元素时,可以把头结点看做是第0个结点,在头结点之后实现上述操作。而没有头结点的单链表要实现这个操作相对而言就比较麻烦了。
代码实现:
typedef struct LNode{ //定义单链表结点类型
ElemType data; //每个结点存放一个数据元素
struct LNode *next; //指针指向下一个结点
}LNode,*LinkList;
bool ListInsert(LinkList &L,int i ,ElemType e)
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 ture; //插入成功
按位序插入(不带头结点)
不带头结点的单链表与都头结点的单链表除了第一个结点之前插入元素与之不同外,之后的结点的插入是相同的方法,在插入删除第1个元素时,需要更改头指针L。
typedef struct LNode{ //定义单链表结点类型
ElemType data; //每个结点存放一个数据元素
struct LNode *next; //指针指向下一个结点
}LNode,*LinkList;
bool ListInsert(LinkList &L,int i ,ElemType e)
if(i<1)
return false;
if(i==1)
{
LNode 8s = (LNode *) malloc(sizeof(LNode));
s->data = e;
s=>next=L;
L=s; //头指针指向新的结点
return ture;
}
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 ture; //插入成功
不难发现,不抬头结点的单链表在i=1时进行了特殊处理,相对于不带头结点的单链表就麻烦了许多。
单链表的删除操作
指定结点的前插操作
当给定一个结点--p,我们可以用循环的方式来寻找后面的结点来实现插入操作,但是如果连头指针也不知道的情况下,p之前的结点我们就无从得知了,那如何实现结点的前操作呢?
bool InsertpriorNode (LNode *p, ElemType e)
假设我们在表中第i个结点之前插入一个结点e,我们可以先在第i个结点的后面插入一个data里没有数据的空结点,然后把第i个结点里的值传给新插入的结点里,然后再把插入结点e的值换到原本第i个结点里,就实现了结点的前插操作。
代码实现:
bool InsertPriorNode (LNode *p,ElemType e){
if (p==NULL)
return false;
LMode *s = (LNode *)malloc(sizeof(LNode));
id(s==NULL) //内存分配失败
return false;
s=>next=p->next;
p->next=s; //新结点s连到p之后
s->data=p->data; //将p中元素复制到s中
p->data=e; //p中元素覆盖为e
return ture;
}
按位序删除(带头结点)
ListDelete(&L,e&,e):删除操作。删除表中第i个位置的元素,并用e返回删除元素的值。
我们可以找到第i-1个结点将他的指针指向第i+1个结点,然后再释放(删除)第i个结点、
头结点可以看做是第0个结点在操作。
bool ListDelete(LinkList &L,int i,ElemType &e){
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 flase;
if(p->next == NULL) //第i个结点之后已经没有其他结点
return flase;
LNode *q=p->next; //令q指向被删除的结点
e = q->data; //用e返回元素的值
p->next=q->next; //将*q结点从链中断开
free(q); //释放结点的储存空间
return ture; //删除成功
}
指定结点的删除
当我们不知道头指针时候,假设我们要删除第i个结点,那么就把第i+1个结点元素的值给复制给要删除的元素也就是第i个结点里,然后让第i个结点的next指针指向第i+2个结点,之后再释放掉第i+1个结点的空间,这样就实现了对第i个结点的删除操作。
代码实现:
bool DeleteNode (LNode *p){
if (p==NULL)
return false;
LNode *q=p->next; //令q指向*p的后续结点
p->data=p->next->data; //和后续结点交换数据域
p-<next=q->next; //将*q结点从链中断开
free(p); //释放后继续结点的储存空间
return ture;
}
上述代码有个问题就是当要删除的p结点刚好是最后一个结点时,他的next指针指向的是一个NULL空指针,那么上述的第5行操作就会出错,当我们对最后一个结点进行释放时候,上一个结点的next指针就会成为野指针。所以,我们应该判断一下,假如是最后一个的话,就把上一个结点的next指针设置为NULL空指针。