单向链表是一种常见的数据结构,它由一系列节点组成,每个节点包含两部分:存储数据的元素和指向下一个节点的指针。每个节点只有指向下一个节点的指针,没有指向前一个节点的指针,因此称为单向链表。
在单向链表中,第一个节点称为头节点,最后一个节点称为尾节点。头节点用来表示链表的起始位置,尾节点则指向一个特殊的空节点(NULL或None),表示链表的结束。
单向链表的优点是插入和删除操作的时间复杂度为O(1),只需修改相邻节点的指针即可完成。而查找操作的时间复杂度较高,为O(n),需要从头节点开始遍历链表直到找到目标节点。
在单向链表中,可以实现很多常见的操作,例如:插入节点、删除节点、查找节点、反转链表等。由于内存中的节点是非连续存储的,因此单向链表的长度理论上是没有限制的。
如下所示是C语言版的单向链表模板
#include <stdio.h>
#include <stdlib.h>
#define eleType int
//链表那结点的结构体
typedef struct ListNode{
//数据
eleType data;
//后继节点
struct ListNode* next;
}ListNode;
//链表的结构体
typedef struct LinkedList{
//链表的头节点
struct ListNode* head;
//链表的元素个数
size_t size;
}LinkedList;
//链表的创建(初始化)
void LinkedListCreate(LinkedList *list){
//头节点置为NULL
list->head=NULL;
//元素个数为0 --->空链表
list->size=0;
}
//链表的销毁
void LinkedListDestroy(LinkedList *list){
while(list->head)
{
//头节点依次用它的后继节点赋值再释放
//最终头节点为NULL说明链表被销毁
ListNode * temp=list->head;
list->head=list->head->next;
free(temp);
}
list->size=0;
}
//链表的增加
void LinkedListInsert(LinkedList *list,int i,eleType value){
//判断插入位置是否合法
//因为最后一个元素的后继节点为NULL
//所以也可以取到,故if中不取等
if(i<0||i>list->size)
{
printf("Invalid index\n");
return;
}
//创建新节点
ListNode *newNode =(ListNode*)malloc(sizeof(ListNode));
//value的值赋值为新节点
newNode->data=value;
//判断插入位置(i)
if(i==0){
//插入头部
newNode->next=list->head;
list->head=newNode;
}//如果不是插入头部
else{
//从头节点开始
ListNode* current=list->head;
for(int j=0;j<i-1;j++){
//这个是链表,不是数组
//所以如果想在第i个位置插入元素
//应从头开始查找,一直到第i-1个位置
//current一个一个往后推
//推 i-1 次,此时current代表的便是第i-1个节点
current=current->next;
}
//新节点的后继节点指向第i个节点(current是第i-1个,它的next是第i个)
//那么后继节点便成为了第i个节点
newNode->next=current->next;
//current代表的仍然是第i-1个节点
//让第i-1个节点指向newNode
current->next=newNode;
}
//链表大小加1
list->size++;
}
//链表节点的删除
void LinkedListRemove(LinkedList* list,int i){
//判断删除位置是否合法
if(i<0||i>=list->size){
printf("Invalid index\n");
return ;
}
//如果删除的是头节点
if(i==0){
//定义一个新的节点指向头节点的后继节点
//存着后继节点的内容
ListNode *next=list->head->next;
//释放头节点
free(list->head);
//头节点指向原先头节点的后继节点
//新的头节点成为了原来头节点的后继节点
//原先的头节点已被释放
list->head=next;
}else{
//把链表的头节点储存到current中
ListNode* current=list->head;
for(int j=0;j<i-1;j++)
{
//找到第i个节点的前驱节点
//即第i-1个节点
current=current->next;
}
//存放下需要删除节点的后续节点
ListNode *next=current->next->next;
//释放需要删除的节点
free(current->next);
//把需要删除的节点的后续节点补过来
current->next=next;
}
list->size--;
}
//查找节点
ListNode* LinkedListFind(LinkedList *list,eleType value){
ListNode *current=list->head;
//用循环找所需查找节点
while(current){
//如果就是需要找的节点
if(current->data==value){
return current;
}
//current 往后递增
current=current->next;
}
//没有的话
return NULL;
}
//链表的索引
ListNode * LinkedListGet(LinkedList* list,int i){
//判断位置是否合法
if(i<0||i>=list->size){
printf("Invalid index\n");
return NULL;
}
ListNode* current=list->head;
for(int j=0;j<i;j++)
{
current=current->next;
}
return current;
}
//链表元素的修改
void LinkedListUpdate(LinkedList *list,int i,eleType value){
//利用链表元素的索引找到目标节点
ListNode* node=LinkedListGet(list,i);
if(node){
//修改链表元素
node->data=value;
}
}
//链表元素的打印
void LinkedListPrint(LinkedList* list){
//从链表头开始
//每次遍历一个节点
ListNode *current=list->head;
while(current){
//遍历到就打印出来
printf("%d->",current->data);
//下一个节点
current=current->next;
}
//链表的最后 打印NULL 代表最终结果
printf("NULL\n");
}
int main()
{
LinkedList list;
//初始化
LinkedListCreate(&list);
//插入数据
LinkedListInsert(&list,0,10);
LinkedListInsert(&list,1,20);
LinkedListInsert(&list,2,30);
LinkedListInsert(&list,3,40);
printf("Original List:\n");
//打印链表
LinkedListPrint(&list);
//移除元素
LinkedListRemove(&list,2);
LinkedListPrint(&list);
//修改元素
LinkedListUpdate(&list,2,300);
LinkedListPrint(&list);
//查找元素
ListNode * found=LinkedListFind(&list,40);
if(!found){
printf("Element not found!\n");
}
return 0;
}