数据结构学习之单链表

有头节点的单链表(C语言实现)

总纲

  1. 单链表头节点的定义
  2. 单链表的初始化
  3. 单链表元素的插入
  4. 单链表元素的删除
  5. 单链表元素的查找
  6. 单链表元素的索引
  7. 单链表元素的修改
  8. 单链表的打印
  9. 单链表的销毁

一. 单链表头节点的定义

单链表每个节点的数据可以是任意类型,所以可以用typedef把数据类型用一个变量存起来
一个节点由两部分组成:

  • 数据域:存储该节点的数据
  • 指针域:存储指向下一个节点的指针
typedef int type; // 将类型重命名

typedef struct Node
{                                                       // 链表节点结构体
	type data; // 数据域
	struct Node* next; // 指针域 
}ListNode;  // 同样为了方便,将struct node重命名 

typedef struct link_list
{
    int size; // 存链表大小
    ListNode* head;   // 头节点,是应指向第一个元素的指针  // 链表整体结构体

}LinkedList;

其实这里也可以不创建链表整体的结构体,看个人喜好吧


二. 单链表的初始化

链表初始化后无元素,就只有一个头节点

void init(LinkedList* list)
{
	list->head = NULL; // 头节点置空
	list->size = 0; // 此时链表内无元素,所以size为0
}

三. 单链表元素的插入

这里提供三种插入方法:头插; 尾插; 任意位置插入

1. 头插

头插就是插到头节点之后,所有元素之前

void HeadInsert(LinkedList* list, type x) // x是要插入的元素
{
    ListNode* tmp = (ListNode*)malloc(sizeof(ListNode)); // 创建一个节点 
	if (tmp != NULL)  // 检查分配空间是否成功 
	{
		list->size ++;
		tmp->data = x;
		tmp->next = list->head;
		list->head = tmp;
	}
    /*
    注意这里tmp->next要指向head原来指向的那个元素,head后可能已经有一个元素了
    然后head要指向新插入的这个元素。用tmp的原因是不能改变头节点
    */
    else
    {
        printf("HeadInsertError");
        return;
    } 
}

2. 尾插

尾插就是插到所有元素之后啦
注意在插入这个元素之前,最后一个元素是指向NULL的

void TailInsert(LinkedList* list, type x)
{
	ListNode* pre = (ListNode*)malloc(sizeof(ListNode)); // 先创建一个节点
	if (pre != NULL) // 检查空间是否分配成功 
	{
		pre->data = x;  // x存进pre中
		pre->next  = NULL;  // 该pre指向NULL
		ListNode* tmp = list->head;
		while(tmp->next != NULL)
		{
			tmp = tmp->next;
		}
		tmp->next = pre; // 这时tmp就是最后一个元素了,要指向pre
		list->size ++;
	}
	else
	{
		printf("TailInsertError\n");
	} 
}

3. 任意位置插入

这个时候要判断插入位置合不合法了
因为涉及到头节点时要改变头节点,所以要分开讨论了

void insert(LinkedList* list, int index, type x)
{
	if (index < 0 || index >= list->size)
	{
		printf("invalid index\n");
		return;
	}
	ListNode* tmp = (ListNode*)malloc(sizeof(ListNode)); // 先创建一个节点保存这个数据
	if (tmp != NULL)
	{
		if (index == 0)    //如果是头插入
		{
			HeadInsert(list, x);
		}
		else if (index == list->size - 1)  //如果是尾插入
		{
			TailInsert(list, x);
		}
		else
		{
			ListNode* start = list->head;   // 定义一个左指针便于寻找位置 
			for (int i = 1; i < index; i++)
			{
				start = start->next; //  for循环控制指针移动的次数 
			} // 这时 start在index前一个位置  
			tmp->next = start->next; // 这个元素(插入元素)要指向原来位置是index的元素,
			start->next = tmp;  // start接下来要指向这个元素
			tmp->data = x;	// 存数据 
			list->size ++;  // 长度增加1 
		}
	}
}

四. 单链表元素的删除

因为涉及到头节点的话要修改头节点,所以要分类讨论
注意要释放被删除元素的那个空间,也要判断位置是否合法

void Delete(LinkedList* list, int index)
{
	if (index < 0 || index >= list->size)  // 检查位置是否合法 
	{
		printf("invalid index\n");
		return;	
	}
	if (index == 0) // 这个时候要修改头节点 
	{
		ListNode* tmp = list->head; // 为释放空间做准备 注意这里head存的就是第一个(也就是要删的)的地址
		list->head = list->head->next; // head 存的是原来第二个元素的地址了
		free(tmp); // 释放原来第一个元素地址所指向的内存空间 
		tmp = NULL; // 置空 
	}
	else
	{
		// 这里不需要修改头节点,要找到正确的位置
		ListNode* start = list->head; // 定义一个指针便于寻找位置
		for (int i = 0; i < index - 1; i++)
		{
			// for循环控制指针移动次数
			start = start->next; 
		}// 这时指针已经来到了要被删除元素前一个元素的位置
		ListNode* tmp_2 = start->next; // 这个是要被删除的元素
		start->next = tmp_2->next; // start要指向被删除元素的后一个元素
		free(tmp_2);  // 释放被删除元素地址所指向的空间
		tmp_2 = NULL; // 置空
	} 
	list->size--; // 不要忘记长度要减一
} 

五. 单链表元素的查找

返回符合条件的第一个元素的索引

int find(LinkedList* list, type x)
{
	int ret = 0; // 返回的结果 
	ListNode* tmp = list->head; // 定义一个指针,用来避免修改头节点
	while(tmp->data != x && tmp != NULL)
	{
		tmp = tmp->next;
		ret++;
	}
	if (tmp == NULL)
	{
		ret = -1;	// 找不到返回-1,说明没有
	}
	return ret;
} 

六. 单链表元素的索引

根据索引值来找到对应的元素,记得判断索引是否合法

int value(LinkedList* list, int index)
{
	if (index < 0 || index >= list->size)
	{
		return -1; // 判断索引是否合法 
	}
	ListNode* tmp = list->head; // 同样,是为了避免修改头节点
	while(index--)
	{
		tmp = tmp->next;	
	}
	return tmp->data;
}

七. 单链表元素的修改

根据索引找到那个元素,然后修改

void set(LinkedList* list, int index, type x)
{
	if (index < 0 || index >= list->size)
	{
		printf("invalid index");
		return;
	}
	ListNode* tmp = list->head;
	while(index--)
	{
		tmp = tmp->next;
	}
	tmp->data = x;
} 

与单链表元素的索引大致相同


九. 单链表的打印

循环打印

void printlist(LinkedList* list)
{
	ListNode* tmp = list->head;
	while(tmp != NULL)
	{
		printf("%d ",tmp->data);
		tmp = tmp->next;
	}
} 

十. 单链表的销毁

每一个节点指向的内存空间都要释放,指针要置空

void destroy(LinkedList* list)
{
	while(list->head != NULL)
	{
	
		ListNode* tmp = list->head;
		list->head = tmp->next;
		free(tmp);
		tmp = NULL;
		list->size--;
	}
}
  • 21
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值