前言
链表是线性表的链式存储方式,逻辑上相邻的数据在计算机内的存储位置不必须相邻,那么怎么表示逻辑上的相邻关系呢?可以给每个元素附加一个指针域,指向下一个元素的存储位
置。如图所示:
从图中可以看出,每个结点包含两个域:数据域和指针域,指针域存储下一个结点的地址,因此指针指向的类型也是结点类型。
一、单链表
链表的节点均单向指向下一个节点,形成一条单向访问的数据链。
1.单链表的初始化
单链表每个结点有两个域——数据域和指针域,多个节点连接在一起形成链表,初始化时要动态分配一块内存。
代码如下:
typedef struct _LinkNode
{
int data; //结点的数据域
struct _LinkNode *next; //结点的指针域
}LinkNode, LinkList; //链表节点、链表
bool InitList(LinkList* &L)//构造一个空的单链表 L
{
L=new LinkNode; //生成新结点作为头结点,用头指针 L 指向头结点
if(!L)
{
return false; //生成结点失败
}
L->next=NULL; //头结点的指针域置空
return true;
}
2.单链表前插
我们需要处理的是将头节点的指针域赋给节点1的指针域(node1->next = L->next;),再让头节点指向节点1即可,以此类推。
代码如下:
bool ListInsert_front(LinkList* &L, LinkNode * node)
{
if(!L || !node )
{
return false;
}
node->next = L->next; //此时node->next==NULL
L->next = node;
return true;
}
3.单链表尾插
这里我们需要先找到最后一个节点,然后将新结点指针域滞空,再将新节点插入即可。
代码如下:
bool ListInsert_back(LinkList* &L, LinkNode *node)
{
LinkNode *last = NULL;
if(!L || !node )
{
return false;
}
//找到最后一个节点
last = L;
while(last->next)
{
last=last->next;
}
//新的节点链接到最尾部
node->next = NULL;
last->next = node;
return true;
}
4.任意位置插入元素
直接看代码:
bool LinkInsert(LinkList* &L, int i, int &e)//单链表的插入
{
//在带头结点的单链表 L 中第 i 个位置插入值为 e 的新结点
int j;
LinkList *p, *s;
p=L;
j=0;
while (p&&j<i-1) //查找第 i-1 个结点,p 指向该结点
{
p=p->next;
j++;
}
if (!p || j>i-1)//i>n+1 或者 i<1
{
return false;
}
s=new LinkNode; //生成新结点
s->data=e; //将新结点的数据域置为 e
s->next=p->next; //将新结点的指针域指向结点 ai
p->next=s; //将结点 p 的指针域指向结点 s
return true;
}
5.打印单链表中的元素
逐步遍历链表各个节点,节点不为空时打印数据域中的元素,继续遍历下一节点,直至遇空为止。
代码如下:
void LinkPrint(LinkList* &L) //单链表的输出
{
LinkNode* p;
p=L->next;
while (p)
{
cout <<p->data <<"\t";
p=p->next;
}
cout<<endl;
}
6.单链表获取元素
思路:i 代表所要获取的元素的位置,我们用 j 配合 p 去找 i ,从而获取想要的元素。
代码如下:
bool Link_GetElem(LinkList* &L, int i, int &e)//单链表的取值
{
//在带头结点的单链表 L 中查找第 i 个元素
//用 e 记录 L 中第 i 个数据元素的值
int j;
LinkList* p;
p=L->next;//p 指向第一个结点,
j=1; //j 为计数器
while (j<i && p) //顺链域向后扫描,直到 p 指向第 i 个元素或 p 为空
{
p=p->next; //p 指向下一个结点
j++; //计数器 j 相应加 1
}
if (!p || j>i)
{
return false; //i 值不合法 i>n 或 i<=0
}
e=p->data; //取第 i 个结点的数据域
return true;
}
7.单链表查找元素
遍历单链表,用 e 去匹配单链表中的元素。
代码如下:
bool Link_FindElem(LinkList *L, int e) //按值查找
{
//在带头结点的单链表 L 中查找值为 e 的元素
LinkList *p;
p=L->next;
while (p && p->data!=e)
{//顺链域向后扫描,直到 p 为空或 p 所指结点的数据域等于 e
p=p->next; //p 指向下一个结点
}
if(!p)return false; //查找失败 p 为 NULL
return true;
}
8.单链表删除元素
代码如下:
bool LinkDelete(LinkList* &L, int i) //单链表的删除
{
//在带头结点的单链表 L 中,删除第 i 个位置
LinkList *p, *q;
int j;
p=L;
j=0;
while((p->next)&&(j<i-1)) //查找第 i-1 个结点,p 指向该结点
{
p=p->next;
j++;
}
if (!(p->next)||(j>i-1))//当 i>n 或 i<1 时,删除位置不合理
return false;
q=p->next; //临时保存被删结点的地址以备释放空间
p->next=q->next; //改变删除结点前驱结点的指针域
delete q; //释放被删除结点的空间
return true;
}
9.单链表销毁
代码如下:
void LinkDestroy(LinkList* &L) //单链表的销毁
{
//定义临时节点 p 指向头节点
LinkList *p = L;
cout<<"销毁链表!"<<endl;
while(p)
{
L=L->next; //L 指向下一个节点
cout<<"删除元素: "<<p->data<<endl;
delete p; //删除当前节点
p=L; //p 移向下一个节点
}
}
二、源码
#include<iostream>
#include<string>
#include<stdlib.h>
using namespace std;
typedef struct _LinkNode
{
int data; //结点的数据域
struct _LinkNode *next; //结点的指针域
}LinkNode, LinkList; //LinkList 为指向结构体 LNode 的指针类型
bool InitList(LinkList* &L)
{
L = new LinkNode;
if(!L)
return false;//生成节点失败
L->next = NULL;
L->data = -1;
return true;
}
//前插法
bool ListInsert_front(LinkList* &L, LinkNode *node)
{
if(!L || !node)
return false;
node->next = L->next;
L->next = node;
return true;
}
//尾插法
bool ListInsert_back(LinkList* &L, LinkNode *node)
{
LinkNode *last = NULL;
if(!L || !node)
return false;
last = L;
while(last->next)
{
last = last->next;
}
node->next = NULL;
last->next = node;
return true;
}
//指定位置插入
bool LinkInsert(LinkList* &L, int i, int &e)
{
if(!L)
return false;
int j =0;
LinkList *p, *s;
p = L;
while(p && j<i-1)
{//查找位置为 i-1 的结点,p 指向该结点
p = p->next;
j++;
}
if(!p || j>i-1)
return false;
s=new LinkNode;//生成新节点
s->data = e;
s->next = p->next;
p->next = s;
return true;
}
void LinkPrint(LinkList* &L)
{
LinkNode *p = NULL;
if(!L)
{
cout<<"链表为空."<<endl;
return ;
}
p = L->next;
while(p)
{
cout<<p->data<<"\t";
p = p->next;
}
cout<<endl;
}
bool Link_GetElem(LinkList* &L, int i, int &e)//单链表的取值
{
//在带头结点的单链表 L 中查找第 i 个元素
//用 e 记录 L 中第 i 个数据元素的值
int index;
LinkList *p;
if(!L || !L->next)
return false;
p = L->next;
index = 1;
while(p && index<i)
{//顺链表向后扫描,直到 p 指向第 i 个元素或 p 为空
p = p->next; //p 指向下一个结点
index++; //计数器 index 相应加 1
}
if(!p || index>i)
{
return false; //i 值不合法,i>n 或 i<=0
}
e=p->data;
return true;
}
bool Link_FindElem(LinkList *L, int e, int &index) //按值查找
{
//在带头结点的单链表 L 中查找值为 e 的元素
LinkList *p;
p=L->next;
index = 1;
if(!L || !L->next)
{
index = 0;
return false;
}
while(p && p->data!=e)
{
p=p->next;
index ++;
}
if(!p)
{
index = 0;
return false;//查无此值
}
return true;
}
bool LinkDelete(LinkList* &L, int i) //单链表的删除
{
LinkList *p, *q;
int index = 0;
p=L;
if(!L || !L->next)
return false;
while((p->next) && (index<i-1))
{
p = p->next;
index++;
}
if(!p->next || (index>i-1) )
//当 i>n 或 i<1 时,删除位置不合理
return false;
q = p->next; //临时保存被删结点的地址以备释放空间
p->next = q->next;//改变删除结点前驱结点的指针域
delete q; //释放被删除结点的空间
return true;
}
void LinkDestroy(LinkList* &L) //单链表的销毁
{
//定义临时节点 p 指向头节点
LinkList *p = L;
cout<<"销毁链表!"<<endl;
while(p)
{
L=L->next;//L 指向下一个节点
cout<<"删除元素: "<<p->data<<endl;
delete p; //删除当前节点
p = L; //p 移向下一个节点
}
}
int main(void){
LinkList *L = NULL;
LinkNode *s = NULL;
//1. 初始化一个空的链表
InitList(L);
//2. 使用前插法插入数据
/*int n;
cout<<"前插法创建单链表"<<endl;
std::cout<<"请输入元素个数 n:";
cin>>n;
cout<<"\n 请依次输入 n 个元素:" <<endl;
while(n>0){
s = new LinkNode; //生成新节点 s
cin>>s->data;
ListInsert_front(L, s);
n--;
}
*/
//3. 使用尾插法插入数据
/*int n;
cout<<"尾插法创建单链表"<<endl;
std::cout<<"请输入元素个数 n:";
cin>>n;
cout<<"\n 请依次输入 n 个元素:" <<endl;
while(n>0){
s = new LinkNode; //生成新节点 s
cin>>s->data;
ListInsert_back(L, s);
n--;
}
//4. 单链表的输出
LinkPrint(L);
*/
//5. 任意位置插入元素
for(int j=0; j<3; j++)
{
int i, x;
cout << "请输入插入的位置和元素(用空格隔开):";
cin >> i;
cin >> x;
if(LinkInsert(L, i, x))
{
cout << "插入成功.\n\n";
}
else
{
cout << "插入失败!\n\n";
}
LinkPrint(L);
}
//6. 单链表根据位置获取元素
int element = 0;
if(Link_GetElem(L, 2, element))
{
cout<<"获取第二个元素成功, 值:"<<element<<endl;
}
else
{
cout<<"获取第二个元素失败!"<<endl;
}
//7. 单链表根据值查询元素所在的位置
int index=0;
if(Link_FindElem(L, 10, index))
{
cout<<"查找元素 10 存在,所在位置: "<<index<<endl;
}
else
{
cout<<"不存在元素 10."<<endl;
}
//8. 单链表删除元素
if(LinkDelete(L, 2))
{
cout<<"删除第 2 个元素成功!"<<endl;
LinkPrint(L);
}
else
{
cout<<"删除第 2 个元素失败!"<<endl;
}
//9. 销毁单链表
LinkDestroy(L);
system("pause");
return 0;
}
总结
对链表的处理关键在对指针域与数据域的处理,理清了这两者,会降低学习难度。以上内容只是个人短见,如有误处,还请指正,共同学习。