目录
头文件定义
#ifndef _LINKLIST_H_
#define _LINKLIST_H_
#include <stdlib.h>
#define SUCCESS 10000
#define FAILURE 10001
#define TRUE 10002
#define FALSE 10003
typedef int ElemType;
struct node
{
ElemType data;
struct node *next;
};
typedef struct node Node;
int LinkInit( Node **ppHeadNode);
int LinkInsert(Node *pHeadNode, int position, ElemType e);
int LinkpHeadNodeInsert(Node *pHeadNode, ElemType e);
int LinkTailInsert(Node *pHeadNode, ElemType e);
int LinkLength(Node *pHeadNode);
int LinkEmpty(Node *pHeadNode);
int LinkTraverse(Node *pHeadNode, void (*visit)(ElemType));
int GetElem(Node *pHeadNode, int position, ElemType *e);
int LocateElem(Node *pHeadNode, ElemType e, int compare(ElemType, ElemType));
int LinkDelete(Node *pHeadNode, int position, ElemType *e);
int LinkClear(Node *pHeadNode);
int LinkDestory(Node **ppHeadNode);
int ReverseLink(Node *pHeadNode);
#endif
单链表初始化
进行初始化操作要注意的是形参传进来的应该是单链表头指针的地址,所以形参应该用二级指针。
同样的,在调用初始化函数的时候也应该是 LinkInit(&pLink) 而非 LinkInit(pLink)
(注:pLink被声明为一个Node类型的指针,即 Node *pLink == NULL)。
因为 pLink 这个指针变量里应该存的是单链表头结点的地址。有一点要注意的是,作为一个已声明的指针变量, pLink 占用了4个字节的空间,其本身也是有地址的。而在初始化分配空间的时候,是给头指针指向的头结点分配地址空间,这个头结点的地址作为一个值是不会直接反应给实参pLink的,所以需要在调用 LinkInit 函数的时候传pLink的地址。
//链表初始化
int LinkInit(Node **ppHeadNode)
{
if( NULL == ppHeadNode)//入参判断
{
return FAILURE;
}
(*ppHeadNode) = (Node *) malloc (sizeof(Node));//为(*ppHeadNode)分配节点空间
if( NULL == (*ppHeadNode))
{
return FAILURE;
}
(*ppHeadNode)->next = NULL;
return SUCCESS;
}
单链表插入
头插法
头插法基本思路是,先让新节点的指针域指向第一节点,再让头结点的指针域指向新节点,先下图。
//链表插入头插法
int LinkHeadInsert(Node *pHeadNode, ElemType e)
{
if( NULL == pHeadNode) //入参判断
{
return FAILURE;
}
Node *pNewNode = NULL; //pNewNode 为要插入的新节点
pNewNode = (Node *) malloc (sizeof(Node));//为 pNewNode 分配节点空间
pNewNode->data = e; //把要插入的数据传给新节点pNewNode
//插入操作
pNewNode->next = pHeadNode->next;
pHeadNode->next = pNewNode;
return SUCCESS;
}
尾插法
尾插法的基本思路是,先让新节点的指针域next指向NULL,再让尾节点的指针域指向新节点。
//链表插入尾插法
int LinkTailInsert(Node *pHeadNode, ElemType e)
{
if( NULL == pHeadNode) //入参判断
{
return FAILURE;
}
Node *pNewNode = NULL; //pNewNode 为要插入的新节点
Node *pTailNode = pHeadNode;//pTailNode 指向尾节点
while(pTailNode->next != NULL)
{
pTailNode = pTailNode->next;
}
pNewNode = (Node *) malloc (sizeof(Node));//为 pNewNode 分配节点空间
pNewNode->data = e; //把要插入的数据传给新节点pNewNode
//插入操作
pNewNode->next = pTailNode->next;
pTailNode->next = pNewNode;
return SUCCESS;
}
中间插入
中间插入首先要定位,定位到要插入的位置的前一个节点,再执行插入操作,见下图
//链表插入操作,position 是要插入的位置, e 是要插入的数据
int LinkInsert(Node *pHeadNode, int position, ElemType e)
{
if( NULL == pHeadNode || position < 1)//入参判断,链表要存在, position 最小为1
{
return FAILURE;
}
if( position > LinkLength(pHeadNode) + 1)//入参判断,确保 position 有效
{
return FAILURE;
}
Node *pNewNode = NULL; //pNewNode 为要插入的新节点
Node *pNode = pHeadNode; //pNode 遍历链表,最终指向要插入位置的前一个节点
int count = 0; //count 用来确定 pNode 指向的位置
while( count < position - 1)
{
pNode = pNode->next;
count++;
}
if( count != position -1) //再次确认位置
{
return FAILURE;
}
pNewNode = (Node *) malloc (sizeof(Node));//为 pNewNode 分配节点空间
pNewNode->data = e; //把要插入的数据传给新节点
//插入操作
pNewNode->next = pNode->next;
pNode->next = pNewNode;
return SUCCESS;
}
单链表求链表长度
//求链表长度
int LinkLength(Node *pHeadNode)
{
if( NULL == pHeadNode) //入参判断
{
return FAILURE;
}
Node *pNode = pHeadNode; //pNode 遍历链表
int length = 0; //length 记录链表长度
while( pNode->next != NULL)
{
pNode = pNode->next;
length++;
}
return length;
}
单链表判空
//判断链表是否为空
int LinkEmpty(Node *pHeadNode)
{
return pHeadNode->next == NULL ? TRUE : FALSE;
}
单链表遍历
//链表的遍历操作
int LinkTraverse(Node *pHeadNode, void (*visit)(ElemType))
{
if( NULL == pHeadNode) //入参判断
{
return FAILURE;
}
Node *pNode = pHeadNode; //pNode 遍历链表
while( pNode->next != NULL)
{
pNode = pNode->next;
(*visit)(pNode->data);
}
return SUCCESS;
}
单链表查找(通过位置求值)
//获取某个位置上的数据
int GetElem(Node *pHeadNode, int position, ElemType *e)
{
if( NULL == pHeadNode || position < 1) //入参判断
{
return FAILURE;
}
if( position > LinkLength(pHeadNode) )
{
return FAILURE;
}
Node *pNode = pHeadNode; //pNode 遍历链表
int count;
while( count < position) //让pNode指向position位置的节点
{
pNode = pNode->next;
count++;
}
if( count != position) //再次确认位置
{
return FAILURE;
}
*e = pNode->data; //传值
return SUCCESS;
}
单链表检索(通过值求第一次出现的位置)
//函数返回链表中第一个与元素e满足关系compare()的位置
int LocateElem(Node *pHeadNode, ElemType e, int compare(ElemType, ElemType))
{
if( NULL == pHeadNode) //入参判断
{
return FAILURE;
}
Node *pNode = pHeadNode->next; //pNode 遍历链表
int count = 0; //count 记录位置
while( pNode != NULL)
{
if( TRUE == compare(pNode->data, e)) //如果pNode->data与e满足关系compare
{
return count + 1; //返回位置
}
pNode = pNode->next;
count++;
}
return FAILURE;
}
单链表删除
单链表的删除要先定位,定位到要删除的节点的前一个节点。先保存下要删除的节点,再剥离这个节点,然后free释放
//链表的删除操作,position 为要删除的位置,*e 保存被删除的数据
int LinkDelete(Node *pHeadNode, int position, ElemType *e)
{
if( NULL == pHeadNode || position < 1) //入参判断
{
return FAILURE;
}
if( position > LinkLength(pHeadNode))
{
return FAILURE;
}
Node *pPreviousNode = pHeadNode; //pNode 遍历链表,最终指向要删除位置的前一个节点
Node *pNode = NULL; //pNode 指向要删除的节点
int count = 0; //count 确保位置
while( count < position - 1)
{
pPreviousNode = pPreviousNode->next;
count++;
}
if( count != position - 1) //再次确认位置
{
return FAILURE;
}
//删除操作
pNode = pPreviousNode->next;//pNode 记录下要删除的节点
*e = pNode->data; //记录将被删除节点上的数据
pPreviousNode->next = pNode->next; //剥离要删除的节
free(pNode); //释放已经被剥离的节点的空间
pNode = NULL;
return SUCCESS;
}
单链表重置
//清空链表
int LinkClear(Node *pHeadNode)
{
if( NULL == pHeadNode) //入参判断
{
return FAILURE;
}
Node *pFirstNode = pHeadNode->next; //first 指向第一节点
while( NULL != pFirstNode)
{
pHeadNode->next = pFirstNode->next; //剥离第一节点,让第二节点变成第一节点
free(pFirstNode); //释放刚刚剥离的第一节点
pFirstNode = pHeadNode->next; //让 pFirstNode 继续指向第一节点
}
return SUCCESS;
}
单链表销毁
//销毁链表
int LinkDestory(Node **ppHeadNode)
{
if( NULL == ppHeadNode || NULL == *ppHeadNode) //入参判断
{
return FAILURE;
}
Node *pTempNode = NULL; //pTempNode 指向将被销毁的节点的下一个节点
while( NULL != (*ppHeadNode) )
{
pTempNode = (*ppHeadNode)->next; //将下个要被销毁的节点的地址传给pTempNode
free(*ppHeadNode); //销毁 *ppHeadNode
(*ppHeadNode) = pTempNode; //把pTempNode里的地址传给*ppHeadNode
}
if( NULL == (*ppHeadNode)) //确认节点都被销毁
{
return SUCCESS;
}
return FAILURE;
}
反转单链表
链表的反转基本思路是,在第一节点和第二节点之间断开,然后从第二节点开始一个一个地插入到头结点后面。
//反转链表
int ReverseLink(Node *pHeadNode)
{
if( NULL == pHeadNode) //入参判断
{
return FAILURE;
}
//利用头插法来进行链表的反转,
//基本思路是从第二节点开始依次剥离,
//再用头插法把剥离的节点插入到头结点后面。
Node *pRestNode = pHeadNode->next->next; //pRestNode 指向剩下的节点
Node *pDetachedNode = NULL; //pDetachedNode 指向要被剥离的节点
pHeadNode->next->next = NULL; //把起初的第一节点变成尾节点
while(pRestNode != NULL)
{
pDetachedNode = pRestNode; //pDetachedNode 指向要被剥离的节点
pRestNode = pDetachedNode->next; //pRestNode 指向剩下的节点
pDetachedNode->next = pHeadNode->next; //开始头插节点
pHeadNode->next = pDetachedNode; //头插结束
}
if(pRestNode == NULL)
{
return SUCCESS;
}
return FAILURE;
}
测试文件
#include <stdio.h>
#include "linklist.h"
#include <time.h>
void print(ElemType e)
{
printf("%d ", e);
}
int eq(ElemType L_e, ElemType e)
{
return L_e == e ? TRUE : FALSE;
}
int gt(ElemType L_e, ElemType e)
{
return L_e > e ? TRUE : FALSE;
}
int lt(ElemType L_e, ElemType e)
{
return L_e < e ? TRUE : FALSE;
}
int main()
{
Node *pLink;
int ret, position, i;
ElemType e;
srand(time(NULL));
ret = LinkInit(&pLink); //链表初始化
if( FAILURE == ret)
{
printf("Init failure.\n");
}
else if( SUCCESS == ret)
{
printf("Init success.\n");
}
for( i = 0; i < 10; i++)
{
e = rand() % 20;
//ret = LinkInsert(pLink, i+1, e); //链表插入
//ret = LinkHeadInsert(pLink, e); //链表头插
ret = LinkTailInsert(pLink, e); //链表尾插
if( FAILURE == ret)
{
printf("Insert failure.\n");
}
else if( SUCCESS == ret)
{
printf("Insert success.\n");
}
}
ret = LinkLength(pLink); //链表求长
if( FAILURE == ret)
{
printf("Length failure.\n");
}
else
{
printf("Length = %d\n", ret);
}
ret = LinkEmpty(pLink); //链表判空
if( FALSE == ret)
{
printf("Not Empty.\n");
}
else if( TRUE == ret)
{
printf("Empty.\n");
}
ret = LinkTraverse(pLink, print); //链表遍历
if( FAILURE == ret)
{
printf("\nTraverse failure.\n");
}
else if( SUCCESS == ret)
{
printf("\nTraverse success.\n");
}
ret = ReverseLink(pLink); //反转链表
if( FAILURE == ret)
{
printf("Reverse failure.\n");
}
else if( SUCCESS == ret)
{
printf("Reverse success.\n");
}
ret = LinkTraverse(pLink, print); //链表遍历
if( FAILURE == ret)
{
printf("\nTraverse failure.\n");
}
else if( SUCCESS == ret)
{
printf("\nTraverse success.\n");
}
position = 4;
ret = GetElem(pLink, position, &e); //获取某位置上的数据
if( FAILURE == ret)
{
printf("Get %dth element failure.\n", position);
}
else if( SUCCESS == ret)
{
printf("%dth element is %d.\n", position, e);
}
e = 10;
ret = LocateElem(pLink, e, eq); //返回10在链表第一次出现的位置
if( FAILURE == ret)
{
printf("Locate element %d failure.\n", e);
}
else
{
printf("%d is %dth element.\n", e, ret);
}
position = 3;
ret = LinkDelete(pLink, position, &e); //链表删除
if( FAILURE == ret)
{
printf("Delete failure.\n");
}
else if( SUCCESS == ret)
{
printf("Delete %dth element %d success.\n", position, e);
}
ret = LinkTraverse(pLink, print); //链表遍历
if( FAILURE == ret)
{
printf("\nTraverse failure.\n");
}
else if( SUCCESS == ret)
{
printf("\nTraverse success.\n");
}
ret = LinkClear(pLink); //链表重置
if( FAILURE == ret)
{
printf("Clear failure.\n");
}
else if( SUCCESS == ret)
{
printf("Clear success.\n");
}
ret = LinkDestory(&pLink); //链表销毁
if( FAILURE == ret)
{
printf("Destory failure.\n");
}
else if( SUCCESS == ret)
{
printf("Destory success.\n");
}
for( i = 0; i < 4; i++)
{
e = rand() % 20;
ret = LinkInsert(pLink, i+1, e); //链表插入
if( FAILURE == ret)
{
printf("Insert failure.\n");
}
else if( SUCCESS == ret)
{
printf("Insert success.\n");
}
}
return 0;
}