声明部分:
typedef struct Lnode
{
int data;
struct Lnode *next;
} Lnode, *LinkList; //LinkList单链表
//以下操作均为带头结点的单链表
LinkList CreateLinkList(); //创建单链表
void DestoryLinkList(LinkList *L); //销毁单链表
int ListIsEmpty(LinkList L); //判空:=1为空表,=0为非空表
Lnode *GetListElem(LinkList L, int index); //按位查找:按位序查找该结点的指针
Lnode *LocateListElem(LinkList L, int elem); //按值查找:给定一个值,查找表中第一个数据域与其相等的结点指针
int ListLength(LinkList L); //求表长(结点个数,不算头结点)
int LinkListInsert(LinkList L, int index, int elem); //按位序插入:在index位置插入一个结点,其值为elem
int InsertNextNode(Lnode *node, int elem); //后插法:在node指针所指的结点后插入一个新节点
int InsertPriorNode(Lnode *node, int elem); //前插法:在node指针所指的结点前插入一个新节点
int LinkListDelete(LinkList L, int index); //按位序删除:在index位置删除一个结点
int DeleteNode(Lnode *p); //删除指定节点,仅给定需要删除的结点的指针(无法删除最后一个结点)
每个函数的具体定义:
创建单链表:
LinkList CreateLinkList()
{
LinkList L = (LinkList)malloc(sizeof(Lnode)); //用一个单链表的指针指向头结点
if (L == NULL)
{
printf("创建单链表失败,内存不足!\n");
}
else
{
L->data = 0; //头指针不存放数据
L->next = NULL; //暂时没有后继结点
return L;
}
}
销毁单链表:
void DestoryLinkList(LinkList *L)
{
LinkList p;
while(*L != NULL)
{
p = *L;
*L = (*L)->next;
free(p);
}
}
判空(=1为空表,=0为非空表):
int ListIsEmpty(LinkList L)
{
return L->next == NULL;
}
按位查找(按位序查找该结点的指针 ):
Lnode *GetListElem(LinkList L, int index)
{
int loc = 1; //当前p指针指向的结点是链表中的第几个
Lnode *p = L->next; //初始化时指向链表的第一个结点
if (index == 0)
{
return L;
}
if (index < 1)
{
printf("查找的入口非法\n");
return NULL;
}
while (p != NULL && loc < index)
{
p = p->next;
loc++;
}
return p;
}
按值查找(定一个值,查找表中第一个数据域与其相等的结点指针 ):
Lnode *LocateListElem(LinkList L, int elem)
{
Lnode *p = L->next;
while (p != NULL && elem != p->data)
{
if (p->next == NULL)
{
printf("未找到该元素\n");
return NULL;
}
else
{
p = p->next;
}
}
return p;
}
求表长(结点个数,不算头结点):
int ListLength(LinkList L)
{
int len = 0;
Lnode *p = L;
while (p->next != NULL)
{
len++;
p = p->next;
}
return len;
}
按位序插入(在index位置插入一个结点,其值为elem ):
int LinkListInsert(LinkList L, int index, int elem)
{
if (index < 1)
{
printf("入口越界!\n");
return 0;
}
Lnode *pre = GetListElem(L, index - 1); //寻找index前驱节点
InsertNextNode(pre, elem); //在前驱结点之后插入新结点
return 1;
}
后插法(在node指针所指的结点后插入一个新节点):
int InsertNextNode(Lnode *node, int elem)
{
if (node == NULL)
{
printf("输入了非法指针\n");
return 0;
}
Lnode *new = (Lnode *)malloc(sizeof(Lnode));
if (new == NULL)
{
printf("插入失败,内存不足!\n");
return 0;
}
new->data = elem;
new->next = node->next;
node->next = new;
return 1;
}
前插法(在node指针所指的结点前插入一个新节点):
int InsertPriorNode(Lnode *node, int elem)
{
int predata = 0;
if (node == NULL)
{
printf("输入了非法指针\n");
return 0;
}
Lnode *new = (Lnode *)malloc(sizeof(Lnode));
if (new == NULL)
{
printf("插入失败,内存不足!\n");
return 0;
}
//将原来的结点复制到新结点
new->next = node->next;
new->data = node->data;
//让原来结点的data = elem
node->next = new;
node->data = elem;
return 1;
}
按位序删除(在index位置删除一个结点):
int LinkListDelete(LinkList L, int index)
{
if (L == NULL)
{
printf("传入指针为空!\n");
return 0;
}
Lnode *pre = GetListElem(L, index - 1); //找到index的前驱结点
if (pre == NULL || pre->next == NULL) //前驱结点或此节点不存在
printf("此结点或前驱结点为空!\n");
return 0;
Lnode *p = pre->next;
pre->next = p->next; //断开p结点
free(p);
return 1;
}
删除指定节点(仅给定需要删除的结点的指针,无法删除最后一个结点):
int DeleteNode(Lnode *node)
{
if (node == NULL)
return 0;
Lnode *p = node->next;
node->data = p->data; //由于最后一个结点无后继结点,p的数据域没有数据,无法删除最后一个结点
node->next = p->next;
free(p);
return 1;
}
总结:
1.学习过程中发现,销毁单链表操作需要用到二级指针,由于 LinkList L 传入的为单链表的一级指针,所以只能在传入函数后通过一级指针修改其所指空间的内容(即数据域和指针域的内容),而销毁操作需要释放链表的空间,修改链表本身的地址为NULL,所以需要传入二级指针对链表本身的地址,才可以销毁链表。
2.引用头指针的好处:
(1).使得在链表的第一个位置上的操作和在表的其他位置上的操作一致。
(2).空表和非空表的处理一致。