链式存储特点
在链式存储中,节点之间的存储单元地址可能是不连续的。链式存储中每个结点都包含两部分:存储元素本身的数据域和存储结点地址的指针域。结点中的指针指向的是下一个结点,也就是存储的下一个结点的地址。
链式存储的实现
1.创建链表
在创建链表时,头结点不存储数据,但可以保存链表的信息。
struct Header
{
int length;//记录链表大小
struct Node *next; //指向第一个结点的指针
};
存储数据的结点包含两部分内容:数据域和指针域。
struct Node
{
int data; //数据域
struct Node *next; //指向下一个结点的指针
};
为了方便定义,将两个struct 用typedef重新定义:
typedef struct Node List; //节点
typedef struct Header pHead; //头节点
创建链表时,只需要创建一个头结点,每存储一个元素就分配一个存储单元,然后将存储单元的地址保存在上一个结点:
pHead* creatList()
{
pHead* ph=(pHead *)malloc(sizeof(pHead)); //为头结点分配内存
ph->length=0; //为头结点初始化,此时链表长度为0
ph->next=NULL;
return ph; //将头结点地址返回
}
2.获取链表大小
//获取链表的大小
int Size(pHead* ph)
{
if(ph==NULL)
{
printf("传入参数有误!");
}
return ph->length;
}
3.插入元素
在链表中插入元素时要比在顺序表中快。比如在pos位置插入,则先断开pos-1与pos的链接,然后将pos-1结点的指针指向val结点,将val结点的指针指向pos。
//插入元素,在pos位置插入元素val
void Insert(pHead* ph,int pos,int val)
{
if(ph==NULL || pos<0 || pos>ph->length)
{
printf("传入参数有误!");
return;
}
//首先将值val保存在一个结点中
List *pval=(List *)malloc(sizeof(List));
pval->data=val;
List *pCur=ph->next;
if(pos==0) //插在首节点
{
ph->next=pval;
pval->next=pCur;
}
else
{
for(int i=1;i<pos;i++)
{
pCur=pCur->next;
}
pval->next=pCur->next;
pCur->next=pval;
}
ph->length++; //长度加1
return;
}
4.查找某个元素
查找链表中的某个元素,其效率没有顺序表高,因为不管查找的元素在哪个位置,都需要将它前面的元素都全部遍历才能找到它。
//查找某个元素,并返回它的结点
List* find(pHead* ph,int val)
{
if(ph==NULL)
{
printf("输入参数有误");
return NULL;
}
//遍历链表来查找元素
List* pTmp=ph->next;
do
{
if(pTmp->data==val)
{
return pTmp;
}
pTmp=pTmp->next;
}while(pTmp->next!=NULL);
printf("没有值为%d的元素\n",val);
return NULL;
}
5.删除元素
在删除元素时,首先将被删除元素与上下结点之间的链接断开,然后将这两个上下结点重新链接。
//删除节点,删除值为val的元素,删除成功,返回删除的元素
void Delete(pHead * ph,int val)
{
if(ph==NULL)
{
printf("传入参数有误");
return;
}
List* pval=find(ph,val);
if(pval==NULL)
{
printf("没有找到值为%d的元素\n",val);
free(pval); //释放结点
return;
}
List* pRe=ph->next;
List* pCur=NULL;
if(pRe->data==val) //如果删除的是第一个节点
{
ph->next=pRe->next;
ph->length--;
free(pRe); //释放结点
return;
}
else
{
for(int i=0;i<ph->length;i++)
{
pCur=pRe->next;
if(pCur->data==val)
{
pRe->next=pCur->next;
ph->length--;
return;
}
pRe=pRe->next;
}
free(pCur); //释放结点
}
return;
}
6.销毁链表
销毁链表时,将链表中每个元素结点释放,头结点可以释放,也可以保留,将其置为初始化状态。
//销毁链表
void Destroy(pHead *ph)
{
List* pCur=ph->next;
List* pTmp;
if(pCur==NULL)
{
printf("传入参数有误");
}
while(pCur->next!=NULL)
{
pTmp=pCur->next;
free(pCur); //将结点释放
pCur=pTmp;
}
ph->length=0; //初始化头结点
ph->next=NULL;
printf("链表已销毁!\n");
}
7.遍历打印链表
//遍历打印链表
void print(pHead * ph)
{
List * pTmp=ph->next;
if(ph==NULL)
{
printf("传入参数有误");
}
while(pTmp!=NULL)
{
printf("%d ",pTmp->data);
pTmp=pTmp->next;
}
printf("\n");
}
8.样例测试
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//所有函数声明
pHead* creatList(); //创建链表
int Size(pHead* ); //获取链表的大小
void Insert(pHead*,int,int); //插入元素
List* find(pHead*,int); //查找某个元素
void Delete(pHead*,int); //删除节点
void Destroy(pHead*); //销毁链表
void print(pHead*); //遍历打印链表
int main()
{
List * ret;
pHead * ph=creatList();
int arr[10]={1,2,3,4,5,6,7,8,9,0};
for(int i=0;i<10;i++)
{
Insert(ph,0,arr[i]);
}
printf("链表长度:%d\n",Size(ph));
print(ph);
printf("删除链表中的节点\n");
int num;
scanf("%d",&num);
Delete(ph,num);
printf("元素删除成功,删除元素%d后,链表中元素为:\n",num);
print(ph);
ret=find(ph,3);
if(ret)
{
printf("get!\n");
}
else
{
printf("No!\n");
}
Destroy(ph);
return 0;
}
结果:
链表长度:10
0 9 8 7 6 5 4 3 2 1
删除链表中的节点
2
元素删除成功,删除元素2后,链表中元素为:
0 9 8 7 6 5 4 3 1
get!
链表已销毁!