首页 > 其他分享 >链表

链表

时间:2023-04-10 17:22:52浏览次数:42  
标签:head ListNode pos phead next 链表 SLNode

1. 顺序表的缺陷

1. 在顺序表头部中间插入数据,需要挪动数据效率低下

2. 容量不够时需要扩容,所以可能存在空间浪费

链表可以很好的解决这些问题,下面讲解链表

 

2. 链表概念及基本结构

链表是一种逻辑上连续,物理内存空间非连续存储的线性结构

typedef int SLDataType;
typedef struct SListNode
{
	SLDataType data;
	struct SListNode* next;
}SLNode;

链表实际在内存中的存储:

链表的逻辑结构:

如图, 链表本身由一个个节点组成,每一个节点都可以通过一个next指针找到下一个节点的地址,以此来实现数据元素之间的逻辑顺序

 

3. 链表的分类

1. 单向 / 双向

2. 带头或者不带头

3. 循环或者非循环

总共8种,在实际中最常用的只有两种无头单向非循环链表(单链表)和带头双向循环链表(双向链表), 下面分别实现

 

4. 单链表

单链表结构

实现

SList.h

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

typedef int SLDataType;
typedef struct SListNode
{
	SLDataType data;
	struct SListNode* next;
}SLNode;

// 创建节点
SLNode* BuyListNode(SLDataType x);
// 尾插
void SLPushBack(SLNode** pphead, SLDataType x);
// 打印
void SLPrint(SLNode* phead);
// 尾删
void SLPopBack(SLNode** pphead);
// 头插
void SLPushFront(SLNode** pphead, SLDataType x);
// 头删
void SLPopFront(SLNode** pphead);
// 查找
SLNode* SLFind(SLNode* phead, SLDataType x);
// pos之前插入
void SLInsertFront(SLNode** pphead, SLNode* pos, SLDataType x);
// pos之后插入
void SLInsertBack(SLNode* pos, SLDataType x);
// pos位置删除
void SLErace(SLNode** pphead, SLNode* pos);
// pos之后删除
void SLEraceAfter(SLNode* pos);

SList.c

#include "SList.h"
// 创建节点
SLNode* BuyListNode(SLDataType x)
{
	SLNode* node = (SLNode*)malloc(sizeof(SLNode));
	if (NULL == node)
	{
		perror("BuyListNode::malloc");
		return;
	}
	else
	{
		node->data = x;
		node->next = NULL;
		return node;
	}
	return NULL;
}
// 尾插
void SLPushBack(SLNode** pphead, SLDataType x)
{
	assert(pphead);
	SLNode* newnode = BuyListNode(x);
	if (NULL == *pphead)
	{
		*pphead = newnode;
	}
	else
	{
		// 找尾
		SLNode* tail = *pphead;
		while (tail->next)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}
// 打印
void SLPrint(SLNode* phead)
{
	SLNode* cur = phead;
	printf("HEAD -> ");
	while (cur)
	{
		printf("%d -> ", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}
// 尾删
void SLPopBack(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	if (NULL == (*pphead)->next)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLNode* tail = *pphead, *prev = NULL;
		while (tail->next)
		{
			prev = tail;
			tail = tail->next;
		}
		free(tail);
		prev->next = NULL;
	}
}
// 头插
void SLPushFront(SLNode** pphead, SLDataType x)
{
	assert(pphead);
	SLNode* newnode = BuyListNode(x);
	SLNode* next = *pphead;
	*pphead = newnode;
	newnode->next = next;
}
// 头删
void SLPopFront(SLNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SLNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}
// 查找
SLNode* SLFind(SLNode* phead, SLDataType x)
{
	SLNode* cur = phead;
	while (cur)
	{
		if (x == cur->data)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
// pos之前插入
void SLInsertFront(SLNode** pphead, SLNode* pos, SLDataType x)
{
	assert(pphead);
	assert(pos);
	SLNode* newnode = BuyListNode(x);
	if (*pphead == pos)
	{
		SLNode* next = *pphead;
		*pphead = newnode;
		newnode->next = next;
	}
	else
	{
		SLNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = newnode;
		newnode->next = pos;
	}
}
// pos之后插入
void SLInsertBack(SLNode* pos, SLDataType x)
{
	assert(pos);
	SLNode* newnode = BuyListNode(x);
	SLNode* next = pos->next;
	pos->next = newnode;
	newnode->next = next;
}
// pos位置删除
void SLErace(SLNode** pphead, SLNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	if (*pphead == pos)
	{
		SLNode* next = pos->next;
		free(pos);
		*pphead = next;
	}
	else
	{
		SLNode* prev = *pphead, *next = pos->next;
		while (prev->next != pos)
		{
			prev = prev->next;
		}
		prev->next = next;
		free(pos);
	}
}
// pos之后删除
void SLEraceAfter(SLNode* pos)
{
	assert(pos);
	assert(pos->next);
	SLNode* next = pos->next->next;
	free(pos->next);
	pos->next = next;
}

 

test.c

#include "SList.h"
void t1()
{
	// 测试打印, 尾插, 尾删
	SLNode* head = NULL;
	SLPushBack(&head, 1);
	SLPushBack(&head, 2);
	SLPushBack(&head, 3);
	SLPopBack(&head);
	SLPrint(head);
}
void t2()
{
	// 测试头插,头删
	SLNode* head = NULL;
	SLPushFront(&head, 1);
	SLPushFront(&head, 2);
	SLPushFront(&head, 3);
	SLPopFront(&head);
	SLPrint(head);
}
void t3()
{
	// 测试查找,pos前/后插入
	SLNode* head = NULL;
	SLPushBack(&head, 1);
	SLPushBack(&head, 2);
	SLPushBack(&head, 3);
	SLPushBack(&head, 4);
	SLPushBack(&head, 5);
	/*SLInsertFront(&head, SLFind(head, 1), 6);
	SLInsertFront(&head, SLFind(head, 4), 7);*/
	SLInsertBack(SLFind(head, 3), 6);
	SLInsertBack(SLFind(head, 5), 7);
	SLPrint(head);
}
void t4()
{
	// 测试pos位置,之后删除
	SLNode* head = NULL;
	SLPushBack(&head, 1);
	SLPushBack(&head, 2);
	SLPushBack(&head, 3);
	SLPushBack(&head, 4);
	SLPushBack(&head, 5);
	//SLErace(&head, SLFind(head, 3));
	SLEraceAfter(SLFind(head, 1));
	SLPrint(head);

}
int main()
{
	//t1();
	//t2();
	//t3();
	t4();
}

 

6. 双向链表

双向链表结构

实现

List.h

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

typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode* prev;
	LTDataType data;
	struct ListNode* next;
}ListNode;

// 初始化
ListNode* LTInit();
// 创建节点
ListNode* BuyListNode(LTDataType x);
// 打印
void LTPrint(ListNode* phead);
// 尾插
void LTPushBack(ListNode* phead, LTDataType x);
// 头插
void LTPushFront(ListNode* phead, LTDataType x);
// 尾删
void LTPopBack(ListNode* phead);
// 头删
void LTPopFront(ListNode* phead);
// 查找
ListNode* LTFind(ListNode* phead, LTDataType x);
// pos之前插入
void LTInsert(ListNode* pos, LTDataType x);
// pos位置删除
void LTErace(ListNode* pos);
// 销毁
void LTDestroy(ListNode* phead);

 

List.c

#include "List.h"
// 创建节点
ListNode* BuyListNode(LTDataType x)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	if (NULL == node)
	{
		perror("BuyListNode::malloc fail");
		return;
	}
	node->prev = node->next = NULL;
	node->data = x;
	return node;
}
// 初始化
ListNode* LTInit()
{
	ListNode* Guard = BuyListNode(-1);
	Guard->prev = Guard->next = Guard;
	Guard->data = -1;
	return Guard;
}
// 打印
void LTPrint(ListNode* phead)
{
	assert(phead);
	ListNode* cur = phead->next;
	printf("GUARD <-> ");
	while (cur != phead)
	{
		printf("%d <-> ", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}
// 尾插
void LTPushBack(ListNode* phead, LTDataType x)
{
	assert(phead);
	ListNode* newnode = BuyListNode(x);
	ListNode* tail = phead->prev;

	phead->prev = newnode;
	tail->next = newnode;
	newnode->next = phead;
	newnode->prev = tail;
}
// 头插
void LTPushFront(ListNode* phead, LTDataType x)
{
	assert(phead);
	ListNode* newnode = BuyListNode(x);
	ListNode* next = phead->next;

	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = next;
	next->prev = newnode;
}
// 判断链表是否为空
bool isLTEmpty(ListNode* phead)
{
	assert(phead);
	return phead->next == phead;
}
// 尾删
void LTPopBack(ListNode* phead)
{
	assert(phead);
	assert(!isLTEmpty(phead));
	ListNode* tail = phead->prev, *Pretail = tail->prev;

	Pretail->next = tail->next;
	phead->prev = Pretail;
	free(tail);
}
// 头删
void LTPopFront(ListNode* phead)
{
	assert(phead);
	assert(!isLTEmpty(phead));
	ListNode* first = phead->next, * next = first->next;

	phead->next = next;
	next->prev = phead;
	free(first);
	
}
// 查找
ListNode* LTFind(ListNode* phead, LTDataType x)
{
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		if (x == cur->data)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
// pos之前插入
void LTInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode* newnode = BuyListNode(x);
	ListNode* Prevpos = pos->prev;

	Prevpos->next = newnode;
	newnode->prev = Prevpos;
	newnode->next = pos;
	pos->prev = newnode;
}
// pos位置删除
void LTErace(ListNode* pos)
{
	assert(pos);
	ListNode* next = pos->next, * prev = pos->prev;
	prev->next = next;
	next->prev = prev;
	free(pos);
}
void LTDestroy(ListNode* phead)
{
	assert(phead);
	ListNode* cur = phead->prev;
	while (cur != phead)
	{
		ListNode* prev = cur->prev;
		free(cur);
		cur = prev;
	}
	free(phead);
}

 

test.c

#include "List.h"
void t1()
{
	// 测试打印,尾插,头插
	ListNode* head = LTInit();
	/*LTPushBack(head, 1);
	LTPushBack(head, 2);
	LTPushBack(head, 3);*/

	LTPushFront(head, 1);
	LTPushFront(head, 2);
	LTPushFront(head, 3);
	LTPrint(head);
}
void t2()
{
	// 测试尾删,头删
	ListNode* head = LTInit();
	LTPushBack(head, 1);
	LTPushBack(head, 2);
	LTPushBack(head, 3);
	/*LTPopBack(head);*/
	LTPopFront(head);
	LTPrint(head);
}
void t3()
{
	// 测试查找, pos之前插入, pos位置删除, 销毁
	ListNode* head = LTInit();
	LTPushBack(head, 1);
	LTPushBack(head, 2);
	LTPushBack(head, 3);
	//LTInsert(LTFind(head, 2), 4);
	LTErace(LTFind(head, 2));
	LTPrint(head);
	LTDestroy(head);
}
int main()
{
	//t1();
	//t2();
	t3();
}

 

标签:head,ListNode,pos,phead,next,链表,SLNode
From: https://www.cnblogs.com/xumu11291/p/17302915.html

相关文章

  • 5、链表
    1、链表我们可以用虚拟头结点dummyHead来简化添加、删除的操作publicclassLinkedList<E>{privateclassNode{publicEe;publicNodenext;publicNode(Ee,Nodenext){this.e=e;this.next=next;......
  • 6、递归链表
    链表具有天然的递归结构,下面我们将用递归实现链表的所有操作publicclassLinkedListR<E>{privateclassNode{publicEe;publicNodenext;publicNode(Ee,Nodenext){this.e=e;this.next=next;......
  • 线性表之静态链表实现(数组cur实现)
    main.cpp#include"StaticList.h"intmain(){StaticListSL;InitSList(SL);for(inti=0;i<5;++i){Insert(SL,'A'+i);}ShowSList(SL);DeleteSList(SL);ShowSList(SL);return0;}Stati......
  • 线性表之单循环链表实现
    main.cpp#include"SCList.h"intmain(){Listmylist;InitList(mylist);intselect=1;ElemTypeItem;Node*p=NULL;while(select){printf("************************************\n");printf("......
  • 搜索二叉树转换成双向链表
    搜索二叉树:每个节点的左子树的值都小于当前节点,右子树的节点值都大于当前节点。其中序遍历就是一个有序的序列转化成双向链表,需要记录一下头节点,和前一个节点,将前一个节点和当前节点相连preheadconvert(pRoot){if(pRoot==null)returnnull;convert(pRoot.left);......
  • 剑指 Offer 36. 二叉搜索树与双向链表
    题目链接:剑指Offer36.二叉搜索树与双向链表方法一:回溯解题思路代码classSolution{private:intmx=INT_MIN,mi=INT_MAX;Node*start=NULL,*end=NULL;public:voidLrotate(Node*root,Node*last_root){//针对左子树,左旋if(!ro......
  • PAT Basic 1075. 链表元素分类
    PATBasic1075.链表元素分类1.题目描述:给定一个单链表,请编写程序将链表元素进行分类排列,使得所有负值元素都排在非负值元素的前面,而[0,K]区间内的元素都排在大于K的元素前面。但每一类内部元素的顺序是不能改变的。例如:给定链表为18→7→-4→0→5→-6→10→11→-2,K为......
  • 链表中的节点每k个一组翻转
    classSolution{publicListNodereverseKGroup(ListNodehead,intk){ListNodedummy=newListNode(0);//定义虚拟节点dummy.next=head;ListNodeprev=dummy;//定义一个前置节点prev,用于保存已经翻转完成的部分的尾部节点......
  • 带头节点的单链表的思路及代码实现
    带头节点的单链表的思路及代码实现(JAVA)一、什么是的单链表①标准定义单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象)+指针(指示后继元素存储位置,元素就是存储数据的存储单......
  • 节点加入到单链表时按需求排序
    JAVA实现节点加入到单链表时按需求排序回顾在上文《带头节点的单链表的思路及代码实现(JAVA)》中我们想要去实现让数据节点不考虑加入顺序实现数据节点排序效果。那么我们要如何实现这一需求呢?一、实现思路①理论思路假设我们要根据数据节点的ID进行排序,那么我们可以通过使用......