单链表是一种链式存储的线性表,其存储结构的特点是用一组地址任意的存储单元存放链表中的数据元素,链表中的数据以结点来表示,每个结点的构成为为:数据域+指针域,存储数据元素信息的域称为数据域,存储直接后继元素存储位置的域称为指针域,通过指针域可以将所有数据连接起来。
单链表的特点的快速的插入和删除,相比于数组,在插入和删除时链表不需要将其数据移动,直接修改结点的指向即可,单链表的缺点是查询效率很低,因为结点的指针域保存的是下一个元素的地址,遍历时只能向后走,不能向前走,因此每次查询都需要从头开始。
单链表分为两种:带头结点的单链表和不带头结点的单链表,说白了就是带头结点的单链表有一个头结点来指向第一个元素结点,而不带头结点的单链表没有这个。下面以带头结点的单链表进行分析:
头插:头插就是每次在头部插入,即插入的位置是phead的下一个,如下图所示,如果要将pnewnode插入,首先将pnewnode的指针域值指向phead->next,再将phead的指针域指向pnewnode。
尾插:尾插就是每次在尾部插入,即在最后一个结点的后面插入,如下图所示,如果要将pnewnode插入,首先遍历单链表,找到最后一个结点,将最后一个结点的指针域指向pnewnode,pnewnode的指针域为空。
删除:删除某个元素首先要找到被删元素的前一个结点pFront,将pFront的指针域指向pCur指向的结点即可,如下图所示,如果要将元素3删除,首先遍历单链表,找到元素3的前一个结点pFront,将pFront的指针域指向pCur指向的结点即可。
销毁:结点使用malloc申请,在销毁链表时应用free将其释放,首先将pCur和pNext指向第一个结点,如果pCur不为空,pNext指向pCur指向结点的下一个结点,将pCur指向的空间释放,再将pCur指向pNext指向的结点,重复上面的过程,最后将phead的指针域置为NULL。
逆置:逆置的过程相比于上面几个操作较为复杂,首先将pCur和pNext指向第一个数据结点,phead的指针域置为NULL,再将pNext指向pCur指向结点的下一个结点,pCur的指针域指向phead指向的结点,phead的指针域指向pCur,pCur指向pNext指向的结点,经过上面这些过程此时该带头结点的单链表中只有一个数据,不断重复上面的过程就可将此单链表逆置,下图画出了将数据1和2逆置的过程。
将数据1逆置:
将数据2逆置:
逆置后的结果如下所示:
下面是完整的代码:
//带头结点的单链表
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
using namespace std;
typedef int ELEMTYPE;
typedef struct Node
{
ELEMTYPE data;
struct Node* next;
}Node,*Link;
void Init(Link phead)//初始化
{
assert(phead != NULL);
if(phead == NULL)
{
return;
}
phead->next=NULL;
}
static Link BuyNode()//购买结点
{
struct Node* pnewnode=(struct Node*)malloc(sizeof(struct Node));
assert(pnewnode != NULL);
pnewnode->next=NULL;
return pnewnode;
}
bool InsertTail(Link phead,ELEMTYPE val)//尾插
{
if(phead == NULL)
{
return false;
}
struct Node *pCur=phead;
while(pCur->next != NULL)
{
pCur=pCur->next;
}
struct Node* pnewnode=BuyNode();
pnewnode->data=val;
pCur->next=pnewnode;
return true;
}
bool InsertHead(Link phead,ELEMTYPE val)//头插
{
if(phead == NULL)
{
return false;
}
struct Node* pnewnode=BuyNode();
pnewnode->data=val;
pnewnode->next=phead->next;
phead->next=pnewnode;
return true;
}
int Length(Link phead)//长度
{
if(phead == NULL)
{
return 0;
}
int length=0;
Node* pCur=phead->next;
while(pCur != NULL)
{
pCur=pCur->next;
length++;
}
return length;
}
bool DeleteKey(Link phead,ELEMTYPE val)//删除元素
{
if(phead == NULL)
{
return false;
}
Node* pFront=phead;
while(pFront->next != NULL)
{
if(pFront->next->data == val)
{
break;
}
pFront=pFront->next;
}
if(pFront->next == NULL)
{
return false;
}
Node *pCur=pFront->next;
pFront->next=pCur->next;
free(pCur);
return true;
}
void Print(Link phead)//打印
{
if(phead == NULL)
{
return;
}
struct Node* pCur=phead->next;
while(pCur != NULL)
{
printf("%d ",pCur->data);
pCur=pCur->next;
}
printf("\n");
}
void Clear(Link phead)
{
struct Node* pCur=phead->next;
struct Node* pNext=pCur;
while(pCur != NULL)
{
pNext=pCur->next;
free(pCur);
pCur=pNext;
}
phead->next=NULL;
}
void Destory(Link phead)
{
Clear(phead);
}
void Reverse(Link phead)
{
if(phead == NULL)
{
return;
}
Node* pCur=phead->next;
Node* pNext=pCur;
phead->next=NULL;
while(pCur != NULL)
{
pNext=pCur->next;
pCur->next=phead->next;
phead->next=pCur;
pCur=pNext;
}
}
int main()
{
Node head;
Init(&head);
for(int i=0;i<5;i++)
{
InsertTail(&head,i+1);
}
for(int i=0;i<5;i++)
{
InsertHead(&head,i+6);
}
Print(&head);
printf("%d\n",Length(&head));
Reverse(&head);
Print(&head);
DeleteKey(&head,10);
Print(&head);
Destory(&head);
return 0;
}
运行结果如下: