数据结构---单链表

链表的概念及结构
概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。
实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

  1. 单向、双向
  2. 带头、不带头
  3. 循环、非循环

一. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结
构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
二. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向
循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了,后面我们代码实现了就知道了。

在这里插入图片描述
头指针指向头节点,头插就是把新插入的这个数据变成头
知道了头指针,就能知道从什么位置开始访问,头指针必须存在
单链表的实现:

#include<stdio.h>
#include<stdlib.h>


typedef int LDataType;
//定义节点:数据+指针
typedef struct listNode
{
	LDataType _data;
	struct listNode* _next;
}listNode;
//listNode 是 struct listNode 的别名

//链表
typedef struct list
{
	//保存第一个节点的地址
	listNode* _head;
}list;

listNode* creatListNode(LDataType val)//返回一个指针
{
	listNode* node = (listNode*)malloc(sizeof(listNode));
	node->_data = val;
	node->_next = NULL;
	return node;
}

void listInit(list* lst)//传入的是指针,故&
{
	//初始化为空链表
	if (lst == NULL)
		return;
	lst->_head = NULL;
}
void listPushBack(list*lst, LDataType val)//尾插:时间复杂度:O(n)
{
	if (lst == NULL)
		return;
	if (lst->_head = NULL)//第一个节点为空
	{
		//插入第一个节点
		//创建节点
	lst->_head=creatListNode(val);//头节点
	}
	else
	{
		//从前向后遍历到最后一个节点
		struct listNode* tail = lst->_head;
		while (tail->_next != NULL)
		{
			tail = tail->_next;
		}
		tail->_next = creatListNode(val);
	}
}
void listPopBack(list* lst)//尾删:O(n)
{

	//尾删的prev在头指针,tail在头节点 -----important
	if (lst == NULL || lst->_head == NULL)
		return;
	//遍历,找到最后一个节点
	struct listNode* prev = NULL;
	struct listNode* tail = lst->_head;
	while (tail->_next != NULL)
	{
		 prev = tail;
		 tail = tail->_next;
	}
	//释放最后一个节点
	free(tail);
	//更新next
	if (prev == NULL)//只有一个节点,更新head
		//if(lst->_head->_next==NULL)
		lst->_head = NULL;//头空
	else
		prev->_next = NULL;//指向为空
}

//头插:O(1)

void listPushFront(list* lst, LDataType val)
{
	if (lst == NULL)
		return;
	//创建节点
	struct listNode* node = creatListNode(val);
	//node head
	node->_next = lst->_head;
	lst->_head = node;
}

//头删:O(1)
void listPopFront(list* lst)
{
	if (lst == NULL || lst->_head == NULL)
		return;

	struct listNode* next = lst->_head->_next;
	free(lst->_head);
	lst->_head = next;
}

void listInsertAfter(listNode* node, LDataType val)
{
	//给一个节点的后边插入
	if (node == NULL)
		return;
	struct listNode* newNode = creatListNode(val);
	//node newNode next
	struct listNode* next = node->_next;
	node->_next = newNode;
	newNode->_next = next;
}

void listEraseAfter(listNode* node)
{
	//删除node的下一个位置
	if (node == NULL || node->_next == NULL)
		return;
	struct listNode* next = node->_next;
	struct listNode* nextnext = next->_next;

	//释放next
	free(next);
	node->_next = nextnext;


}

struct listNode* listFind(list* lst, LDataType val)
{
	if (lst == NULL || lst->_head == NULL)
		return NULL;
	struct listNode* cur = lst->_head;
	while (cur)
	{
		if (cur->_data == val)
			return cur;

		cur = cur->_next;
	}
	return NULL;
}

void listDestroy(list* lst)//传了链表的一个指针
{
	//释放当前节点保存下一个的值

	if (lst == NULL)
		return NULL;
	//头空不空已经判断
	struct listNode* cur = lst->_head;
	while (cur)
	{
		struct listNode* next = cur->_next;
		free(cur);//释放当前
		cur = next;//更新到下一个节点的位置
	}
	lst->_head = NULL;
}

void test()
{
	list lst;
	listInit(&lst);
}
int main()
{
	test();
	return 0;
}

顺序表和链表的区别和联系

顺序表:

空间连续,支持连续访问

中间或前面部分的插入删除时间复杂度为O(n);并且增容的代价比较大

链表:

以节点为字节存储,不支持随机访问

任意位置插入删除时间复杂度都为O(1);插入一个开辟一个空间

带头双向循环链表

#include<stdio.h>
#include<stdlib.h>
typedef int LDataType;

//双向节点
typedef struct ListNode
{
	LDataType _data;
	struct ListNode* _prev;
	struct ListNode* _next;
}ListNode;


//双向带头循环链表
typedef struct List
{
	struct ListNode* _head;
}List;

void initList(List* lst)
{
	//空的带头循环双向链表
	//构建循环结构
	//创建头节点
	lst->_head = (struct ListNode*)malloc(sizeof(struct ListNode));
	//循环
	lst->_head->_prev = lst->_head->_next = lst->_head;
}

struct ListNode* creatNode(LDataType val)
{
	struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
	node->_data = val;
	node->_next = node->_prev = NULL;
}


void listPushBack(List* lst, LDataType val)//尾插
{
	struct ListNode* tail = lst->_head->_prev;
	struct ListNode* newNode = creatNode(val);

	//head ......tail  newNode
	tail->_next = newNode;
	newNode->_prev = tail;

	lst->_head->_prev = newNode;
	newNode->_next = lst->_head;
}

//空链表不能删除,会破坏双向带头循环的结构
void listPopBack(List* lst)//尾删:O(1)
{
	if (lst->_head->_prev == lst->_head)
		return;//空链表
	struct ListNode* tail = lst->_head->_prev;
	struct ListNode* prev = tail->_prev;

	free(tail);
	prev->_next = lst->_head;
	lst->_head->_prev = prev;

}
void listPushFront(List* lst, LDataType val)//头插
{
	struct ListNode* newNode = creatNode(val);
	struct ListNode* next = lst->_head->_next;

	lst->_head->_next = newNode;
	newNode->_prev= lst->_head;

	next->_prev = newNode;
	newNode->_next = next;
}

void listPopFront(List* lst)//头删
{
	if (lst->_head->_next == lst->_head)
		return;
	struct ListNode* next = lst->_head->_next;
	struct ListNode* nextnext = next->_next;
	free(next);
	lst->_head->_next = nextnext;
	nextnext->_prev = lst->_head;
	//listErase(lst->_head->_next);
}


void listInsert(ListNode* node, LDataType val)
{
	//从node前插入一个节点
	struct ListNode* newNode = creatNode(val);
	struct ListNode* prev = node->_prev;
	//     prev  newNode node
	node->_prev = newNode;
	newNode->_next = node;

	prev->_next = newNode;
	newNode->_prev = prev;
}
//头插:insert(head->next,val)
//尾插:insert(head,val)

void listErase(ListNode* node)
{ 
	struct ListNode* prev = node->_prev;
	struct ListNode* next = node->_next;
	free(node);
	prev->_next = next;
	next->_prev = prev;

}
listDestroy(List* lst)
{
	struct ListNode* cur = lst->_head->_next;
	while (cur != lst->_head)
	{
		struct ListNode* next = cur->_next;
		free(cur);
		cur = next;
	}
	free(lst->_head);
	lst->_head = NULL;
}

void printList(List* lst)
{
	struct ListNode* cur = lst->_head->_next;//第一个值
	while (cur!=lst->_head)
	{
		printf("%d ", cur->_data);
		cur = cur->_next;
	}
	printf("\n");
}
void test()
{
	struct List lst;
	initList(&lst);
}
int main()
{
	test();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值