何为链表
是一组被称为节点的自引用结构体的线形排列。要访问链表中的其中一个节点,必须从链表中的第一个节点开始。如果第一个节点的地址丢失,则链表中的其他节点都无法访问。所以,必须有一个指针指向第一个节点(这个指针也称为头指针)。节点nexttr的值被置为空,那么这个节点就是尾节点
优点
因为链表使用malloc函数申请内存,所以只要内存足够,链表的长度就可以无限增长。
相比于数组,链表拥有更高的内存利用率
缺点
每次访问链表中的数据时,都必须从头指针开始寻找
指针也占用存储空间,动态分配内存将增加函数调用的开销
链表的实现
创建一个结构体"节点"(需要有一个指针指向第一个节点,这个头指针会在main()函数中定义)
struct listNode { int value; //一个节点中存放的内容 struct listNode *nextPtr; //指向下一个节点的指针,链表最后一个节点这个值为空(NULL) };
1、增
创建函数insert(),传入类型为struct listNode *的头指针&headPtr,使用struct listNode **类型的形参来接收;传入数据value;传入unsigned int *类型的参数listLength。如果数据添加成功,链表的长度 + 1
void insert_random (struct listNode **headPtr, int value, unsigned int* listLength) { }
对于insert函数,有两种情况:
1)链表为空,创建一个节点并将头指针指向这个节点,同时将*nextPtr值置空
2)链表不为空,插入节点到末尾,更新尾节点。
insert函数具体实现
void insert_random(LISTNODE **headPtr, int value, unsigned int* listLength) { /* 新的内存区块 */ LISTNODE *newNode = malloc(sizeof(LISTNODE)); LISTNODE * currentPtr = *headPtr; /* 检测内存是否正确分配 */ if(newNode == NULL) { puts("内存不足!"); exit(2); } /* 将数据存放至节点 */ (*newNode).data = value; newNode->nextPtr = NULL; /* 添加区块 */ /* 添加区块到头节点 */ if (*headPtr == NULL) { *headPtr = newNode; }else { /* 添加区块到尾节点 */ while (currentPtr->nextPtr != NULL) { currentPtr = currentPtr->nextPtr; } currentPtr->nextPtr = newNode; } (*listLength)++;; printf("%s%d%s", "插入节点成功,当前链表长度:", *listLength, "\n\n"); }
2、删
创建delete函数,获取头指针的值&headPtr、要删除的节点序号value、链表长度listLength
void delete(struct listNode **headPtr, int value, unsigned int* listLength) { }
提示用户输入删除点,判断删除的位置:
1)用户输入值小于1,输入有误并退出
2)用户输入值为1,删除头节点,更新头指针
3)用户输入 大于1 且小于等于链表长度 的值,删除中间节点或尾节点,更新删除点 上层指针指向
4)用户输入值大于链表长度,提示用户要删除的节点不存在,程序退出
deleteLink函数具体实现
void delete(struct listNode **headPtr, int value, unsigned int* listLength) { LISTNODE *currentPtr = *headPtr; if (value == 1) { /* 要删除的节点 */ LISTNODE *delNode = NULL; /* 获取要删除的节点 */ delNode = currentPtr; /* 更新头指针 */ *headPtr = currentPtr->nextPtr; /* 删除节点 */ free(delNode); }else { /* 要删除节点的上一个区块 */ LISTNODE *previousPtr = NULL; int loop = 1; /* 移动到待删除点 */ while (loop < value) { previousPtr = currentPtr; currentPtr = currentPtr->nextPtr; loop++; } // delNode = currentPtr; /* 上层指针的nextPtr指向要删除节点的下一个节点 */ previousPtr->nextPtr = currentPtr->nextPtr; /* 删除节点 */ free(currentPtr); } (*listLength)--; printf("%s%d%s", "删除节点成功,当前链表长度:", *listLength, "\n\n"); }
3、改
创建updateLink函数,函数接收struct listNode *类型的头指针&headPtr、int类型的更新点updateNum、int类型更新后的值update
void updateLink(struct listNode **headPtr, int updateNum, int value) { LISTNODE *currentPtr = *headPtr; int loop = 1; while(loop < updateNum) { currentPtr = currentPtr->nextPtr; loop++; } if(currentPtr != NULL) { currentPtr->data = value; }else { puts("[-] 超出链表范围"); exit(3); } }
4、查
无论是打印一个节点中的特定值还是打印所有节点中的值,都是非常简单的。本例实现的是打印所有数据
void printLink(struct listNode **headPtr, unsigned int listLength) { LISTNODE *currentPtr = *headPtr; printf("%s%d\n", "当前链表长度: ", listLength); puts("+-------------------------+"); while (currentPtr != NULL) { printf("%s%d\n%s%p\n", "数据: ", currentPtr->data, "下一个节点: ", currentPtr->nextPtr); puts("+-------------------------+"); currentPtr = currentPtr->nextPtr; } puts(""); }
完整代码