在顺序结构中,每个元素只需要存储元素的信息就可以,单链式结构中因为每个节点的位置在物理上的位置是不确定的,所以在存储时,需要不仅需要存储数据信息还要存储后继元素的位置(单链表),有的链表不但会存储后继节点的位置,还会存储前驱节点的位置(双向链表)。一般将存储数据的部分称为数据域存放下一个节点位置的区域叫做指针域,而数据和指针域和起来称为一个节点。而链表包含零个(空链表)或多个节点由多个节点。
由于链表中每一个元素的位置都是在其前驱节点的指针域中存储,但是第一个节点是没有前驱的,所以这是需要一个指针,帮助记录第一个节点的位置,这个变量就被称为“头指针”,可以通过这个“头指针”来遍历整个链表。而在链表的最后一个元素是没有后继的所以它的指针域为空。
和数组一样,对于链表也有很多操作,比如增删改查等,对于一些特殊的操作对边界的处理就可能和一般节点的处理不一样,为了统一这些操作,有时会在头指针的后面第一个节点的前面附设一个节点,称为头节点。
这里需要区分一下头节点和头指针的区别:
头指针 | 头节点 |
头指针是指链表指向第一个节点的指针, 若链表有头节点,则是指向头节点的指针 | 头节点是为了操作的统一和方便而设立的,放在第一个节点之前,其数据域一般无意义(也可存放链表长度,但要类型合适) |
头指针具有标识作用,所以常用头指针冠以链表的名字。 | 有了头节点,对在第一个节点之前插入和删除第一节点的操作就和其他节点相统一了 |
无论链表是否为空,头节点一定存在。头指针是链表的必要元素。 | 头节点不一定是链表的必要要素。 |
单链表的定义:
typedef int ElemType;
typedef struct _NODE
{
ElemType data;
_NODE* next;
}NODE;
typedef NODE* linkList;
//在指定位置插入节点(默认时头插)
bool insertNodePos(linkList* head, ElemType value, int pos = 0);
//在删除指定位置的节点(默认头删)
bool deleteNodePos(linkList* head, int pos = 0);
//查找指定位置的节点(默认查找第0个元素)
NODE* getPosData(linkList head, int pos=0);
//修改指定位置的节点的值(默认修改第0个元素的值)
bool makePosData(linkList* head, ElemType mValue, int pos = 0);
//展示链表内容
void showLinkList(linkList head);
//获取指定位置节点前一个节点的地址
NODE* getPrePos(linkList head,int pos);
//清空链表
void clearLinkList(linkList* head);
在指定位置插入节点(默认时头插)
bool insertNodePos(linkList* head,ElemType value,int pos)
{
linkList p = getPrePos(*head, pos);
//插入元素位置越界
if (NULL == p&&pos != 0)
{
return false;
}
NODE* newNode = (NODE*)malloc(sizeof(NODE));
newNode->data = value;
if (0 == pos)
{
newNode->next = *head;
*head = newNode;
}
else
{
newNode->next = p->next;
p->next = newNode;
}
return true;
}
在删除指定位置的节点(默认头删)
bool deleteNodePos(linkList* head,int pos)
{
linkList p = getPrePos(*head, pos);
//最后一个元素后继
if (NULL == p || (NULL == p->next&&pos != 0))
{
return false;
}
NODE* tNode=NULL;
if (0 == pos)
{
tNode = (*head);
(*head) = (*head)->next;
}
else
{
tNode = p->next;
p->next = tNode->next;
}
free(tNode);
tNode = NULL;
return true;
}
查找指定位置的节点(默认查找第0个元素)
NODE* getPosData(linkList head, int pos)
{
NODE* p = getPrePos(head, pos);
if (NULL == p || (NULL == p->next&&pos != 0))
{
return NULL;
}
if (0 == pos)
{
return p;
}
return p->next;
}
修改指定位置的节点的值(默认修改第0个元素的值)
bool makePosData(linkList* head, ElemType mValue, int pos)
{
NODE* p = getPrePos(*head, pos);
if (NULL == p || (NULL == p->next&&pos!=0))
{
return false;
}
if (0 == pos)
{
p->data = mValue;
}
else
{
p->next->data = mValue;
}
return true;
}
展示链表内容
void showLinkList(linkList head)
{
if (head == NULL)
{
return;
}
linkList p = head;
while (p)
{
printf("%d,",p->data);
p = p->next;
}
puts("");
}
获取指定位置节点前一个节点的地址
NODE* getPrePos(linkList head, int pos)
{
/*
(1)pos<0 空
(2)pos==0 头节点
(3)pos==1 头节点
(4)pos>1 前一个节点
(5)越界 空
*/
if (pos < 0)
{
return NULL;
}
linkList p = head;
for (int i = 0; (i < pos - 1) && (p != NULL); ++i)
{
p = p->next;
}
return p;
}
清空链表
void clearLinkList(linkList* head)
{
while (*head)
{
deleteNodePos(head);
}
}