目录
1、单链表的初始化
以下两种定义方式结果相同但内涵不同:
- LinkList L :强调这是一个单链表
- LNode * :强调这是一个结点
定义一个结构体来表示结点
typedef struct LNode
{
int data;
struct LNode *next;
}LNode, *LinkList;
初始化一个空的单链表
分为带头结点和不带头结点两种,区别在于,带头结点的单链表,头结点中不存储数据。
// 初始化一个空的单链表(不带头结点)
bool InitList(LinkList &L){
L=NULL; //空表,此时还没有任何结点(防止脏数据)
return true;
}
// 初始化一个单链表——带头结点(头结点不存储数据)
bool InitList1(LinkList &L){
L=(LNode *)malloc(sizeof(LNode)); // 分配一个头结点
if(L==NULL) // 内存不足,分配失败
return false;
L->next=NULL; // 头节点之后暂时还没有结点
return true;
}
判断单链表是否为空
/ 判断单链表是否为空
bool Empty(LinkList L){
// if(L==NULL)
// return true;
// else
// return false;
return(L==NULL);
}
// 判断单链表是否为空--带头结点
bool Empty1(LinkList L){
return(L->next==NULL);
}
2、求表长操作
// 求表的长度
int Length(LinkList L){
int len=0;
LNode *p=L;
while (p->next!=NULL)
{
p=p->next;
len++;
}
return len;
}
3、按序号查找结点
// 按位查找——带头结点
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;
}
4、按值查找表结点
// 按值查找
LNode * LocateElem(LinkList L, int e){
LNode *p =L->next;
// 从第一个结点开始查找数据域为e的结点
while (p!=NULL&& p->data != e)
p=p->next;
return p; // 找到后返回该结点指针,否则返回NULL
}
5、插入结点操作
按位序插入:
不带头结点的插入操作,第一个结点的操作与其他结点有所不同。
因为没有头结点,所以插入时,先将next指针指向原来的头指针L指向的区域,然后再将头指针指向自己(第一个结点)。
代码如下:
// 按位序插入(不带头结点)
bool ListInsert0(LinkList &L, int i, int e){
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指向第一个结点
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; // 插入成功
}
带头结点的插入:
// 按位序插入(带头结点)
bool ListInsert1(LinkList &L, int i, int e){
if(i<1)
return false;
LNode *p; // 指针p指向当前扫描到的结点
int j=0; // 当前P指向的是第几个结点
p=L; // L指向头结点,不存放数据
while (p!=NULL && j<i-1){ // 循环找到第i-1个结点
p=p->next;
j++;
}
// -----------------------------
if(p==NULL)
return false;
LNode *s =(LNode *)malloc(sizeof(LNode));
s->data=e;
s->next=p->next;
p->next=s; // 将结点s连接到p之后
return true; // 插入成功
// ------------------------------
//return InsertNextNode(p,e);
}
插入操作从下图可以更好的理解。
需要注意的是:步骤2和步骤3不可以颠倒。
如果先将p->next指向结点s,那么链表的剩余部分就再也无法找到。
以下均探讨带头结点的情况:
指定结点的后插操作:
// 指定结点的后插操作
bool InsertNextNode(LNode *p, int e){
if(p==NULL)
return false;
LNode *s=(LNode *)malloc(sizeof(LNode));
if(s==NULL) // 内存分配失败
return false;
s->data=e;
s->next=p->next;
p->next=s;
return true;
}
指定结点的前插操作:
由于每一个结点都有一个指针指向下一个结点,因此可以很容易的进行后插操作。但是在进行前插操作时,无法直接从指定结点找到他的前一个结点,一种想法是:传入头指针遍历循环。
在此,我们采用另一种更简单的思想:后插一个结点,再将结点p的值复制到插入的结点中,然后将结点p的值更改为要插入的结点的值。简单理解就是将要前插的结点后插,再交换位置,即可实现。代码如下:
// 指定结点的前插操作:
// 1、由于结点p的前一个结点不可知,传入头指针L循环遍历
// 2、后插一个结点,再将结点p的值复制到插入的结点中,再将结点p的值更改为要插入的结点的值
bool InsertPriorNode(LNode *p, int e)
{
if(p==NULL)
return false;
LNode *s=(LNode *)malloc(sizeof(LNode));
if(s==NULL) // 内存分配失败
return false;
s->next=p->next;
p->next=s;
s->data=p->data;
p->data=e;
return true;
}
// 另一种写法
bool InsertPriorNode1(LNode *p, LNode *s)
{
if(p==NULL)
return false;
s->next=p->next;
p->next=s;
int temp=p->data;
p->data=s->data;
s->data=temp;
return true;
}
6、删除结点操作
按位序删除(带头结点):
// 按位序删除(带头结点)
bool ListDelete(LinkList &L, int i, int &e){
if(i<1)
return false;
LNode *p; // 指针p指向当前扫描到的结点
int j=0; //当前P指向的是第几个结点
p=L; //L指向头结点,不存放数据
while (p!=NULL && j<i-1){ //循环找到第i-1个结点
p=p->next;
j++;
}
if(p==NULL)
return false;
if(p->next==NULL) // 第i个结点不存在
return false;
LNode *q=p->next;
e=q->data;
p->next=q->next;
free(q);
return true;
}
指定结点的删除:
类似于前插结点的操作,先将要删除的结点p的值设为p->next结点的值,再删除p->next结点。
// 指定结点的删除
bool DeleteNode(LNode *p){
if(p==NULL)
return false;
LNode *q=p->next;
p->data=p->next->data;
p->next=q->next;
free(q);
return true;
}
下图表示,删除最后一个结点的情况:
7、采用头插法建立单链表
需要注意的是:头插法需要将L->next=NULL
头插法的操作步骤由下图所示:
可以看出,最终得到的链表和我们输入元素的数据顺序相反,借助这一特性,我们可以将其用于链表的逆置:顺序读出链表元素依次用头插法存入新链表中。
// 头插 可以用于链表的逆置
LinkList List_HeadInsert(LinkList &L){ // 逆向建立单链表
LNode *s;
int x;
L=(LinkList)malloc(sizeof(LNode)); // 建立头结点
L->next=NULL; // 初始为空链表
scanf("%d",&x); // 输入结点的值
while (x!=9999) // 输入一个特殊值表示结束,此处为9999
{
s=(LNode *)malloc(sizeof(LNode));
s->data=x;
s->next=L->next;
L->next=s; // 将新结点插入表中,L为头指针
scanf("%d",&x);
}
return L;
}
8、采用尾插法建立单链表
// 单链表的建立---带头结点
// 尾插
LinkList List_TailInsert(LinkList &L){ // 正向建立单链表
int x; // 设ElemType为整型
L=(LinkList)malloc(sizeof(LNode)); // 建立头结点
LNode *s,*r=L; // r为表尾指针
scanf("%d",&x); // 输入结点的值
while (x!=9999) // 输入一个特殊值表示结束,此处为9999
{
s=(LNode *)malloc(sizeof(LNode));
s->data=x;
r->next=s;
r=s; // r指向新的表尾结点
scanf("%d",&x);
}
r->next=NULL; // 尾结点指针置空
return L;
}
以上就是单链表的各种基本操作,欢迎提问和补充!