其他分享
首页 > 其他分享> > 线性表之链表

线性表之链表

作者:互联网

目录

1.链表表示和实现(单链表+双向链表)

2.单链表的实现

接口实现(SList.h)

接口函数实现(SList.c)

主文件(text.c)

3. 双链表的实现 

接口实现(List.h)

接口函数实现(List.c)

主文件(text.c)

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


1.链表表示和实现(单链表+双向链表)

 

 

2.单链表的实现

// 1、无头+单向+非循环链表增删查改实现

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

typedef int SLTDateType;
typedef struct SListNode
{
    SLTDateType data;       // 存放节点的数据
    struct SListNode* next; // 下一个节点的位置
};
typedef struct SListNode SLTNode;

// 动态申请一个节点
SLTNode* BuySListNode(SLTDataType x);

/*(不会改变链表的头指针,传一级指针)*/
// 单链表打印
void SListPrint(SLTNode* phead);

/*(可能会改变链表的头指针,传二级指针)*/
// 单链表尾插
void SListPushBack(SLTNode** pphead, SLTDataType x);
// 单链表的头插
void SListPushFront(SLTNode** pphead, SLTDataType x);
// 单链表的尾删
void SListPopBack(SLTNode** pphead);
// 单链表头删
void SListPopFront(SLTNode** pphead);

// 单链表查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x);
// 单链表在pos位置之前插入x
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDataType x);
// 单链表删除pos位置的值
void SListErase(SLTNode** phead, SLTNode* pos);


// 有些地方也有这样的
 在pos的前面插入x
//void SListInsert(SLTNode** phead, int i, SLTDataType x);
 删除pos位置的值
//void SListErase(SLTNode** phead, int i);
#include "SList.h"

// 动态申请一个节点
SLTNode* BuySListNode(SLTDataType x)
{
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
    //当引发了异常: 写入访问权限冲突,data无法读取内存
    //错误是没有引用包含 malloc 的头文件
	newnode->data = x; 
	newnode->next = NULL;

	return newnode;
}


/*(不会改变链表的头指针,传一级指针)*/
// 单链表打印
void SListPrint(SLTNode* phead)
{
	SLTNode* cur = phead;
	while (cur != NULL)      // 可以理解为看自己为不为空
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("NULL\n");
}

/*(可能会改变链表的头指针,传二级指针)*/
// 单链表尾插
// pphead 存放的是 plist的地址,要改变plist,需要解引用(*pphead)
void SListPushBack(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySListNode(x);

	if (*pphead == NULL)
	{
		*pphead = newnode;
	}
	else
	{
		// 找尾节点的指针
		SLTNode* tail = *pphead;
		while (tail->next != NULL)  // 可以理解为看下一个为不为空
		{
			tail = tail->next;
		}

		// 尾节点,链接新节点
		tail->next = newnode;
	}
}

// 单链表的头插
void SListPushFront(SLTNode** pphead, SLTDataType x)
{
	SLTNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

// 单链表的尾删
void SListPopBack(SLTNode** pphead)
{
	// 1. 空
	// 2. 一个节点
	// 3. 一个以上节点
	if (*pphead == NULL)
	{
		return;
	}
	else if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		SLTNode* pre = NULL;
		SLTNode* tail = *pphead;
		while (tail->next != NULL)
		{
			pre = tail;
			tail = tail->next;
		}
		pre->next = NULL;
	}
}

// 单链表头删
void SListPopFront(SLTNode** pphead)
{
	// 先保存,后free
	SLTNode* next = (*pphead)->next;
	free(*pphead);
	*pphead = next;
}

// 单链表查找
SLTNode* SListFind(SLTNode* phead, SLTDataType x)
{
	SLTNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	*pphead = newnode;
}

// 单链表在pos位置之前插入x
void SListInsert(SLTNode** phead, SLTNode* pos, SLTDataType x)
{
	// 当只有一个节点
	if (pos == *pphead) 
	{
		SListPushFront(pphead, x);
	}
	else
	{
		SLTNode* newnode = BuySListNode(x);
		SLTNode* pre = *pphead;
		while (pre->next != pos)
		{
			pre = pre->next;
		}
		pre->next = newnode;
		newnode->next = pos;
	}

}

// 单链表删除pos位置的值
void SListErase(SLTNode** phead, SLTNode* pos)
{
	if (pos == *pphead)
	{
		SListPopFront(pphead, pos);
	}
	else
	{
		SLTNode* pre = *pphead;
		while (pre->next != pos)
		{
			pre = pre->next;
		}
		pre->next = pos->next;
		free(pos);
	}
}
#include "SList.h"

void TestSList1()
{
	SLTNode* plist = NULL;
    SListPushBack(&plist, 1); // 这里要传地址,因为要改变plist
	SListPushBack(&plist, 2); // plist又是一个指针,所以后面函数要用二级指针
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPushFront(&plist, 0);
	SListPrint(plist);

	SListPopFront(&plist);
	SListPopFront(&plist);
	SListPopFront(&plist);
	SListPrint(plist);

	SListPopFront(&plist);
	SListPopFront(&plist);
	SListPrint(plist);
}

void TestSList2()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPrint(plist);
}

void TestSList3()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPrint(plist);

	// 想在3的前面插入一个30 ,需要先找到要删除节点的位置pos
	SLTNode* pos = SListFind(plist, 1);
	if (pos)
	{
		SListInsert(&plist, pos, 10);
	}
	SListPrint(plist);

	pos = SListFind(plist, 3);
	if (pos)
	{
		SListInsert(&plist, pos, 30);
	}
	SListPrint(plist);
}

void TestSList4()
{
	SLTNode* plist = NULL;
	SListPushBack(&plist, 1);
	SListPushBack(&plist, 2);
	SListPushBack(&plist, 3);
	SListPushBack(&plist, 4);
	SListPrint(plist);

	SLTNode* pos = SListFind(plist, 1);
	if (pos)
	{
		SListErase(&plist, pos);
	}
	SListPrint(plist);

	pos = SListFind(plist, 4);
	if (pos)
	{
		SListErase(&plist, pos);
	}
	SListPrint(plist);

	pos = SListFind(plist, 3);
	if (pos)
	{
		SListErase(&plist, pos);
	}
	SListPrint(plist);

	pos = SListFind(plist, 2);
	if (pos)
	{
		SListErase(&plist, pos);
	}
	SListPrint(plist);
}


int main()
{
	TestSList4();

	return 0;
}

3. 双链表的实现 

// 2、带头+双向+循环链表增删查改实现

// 带头双向循环 -- 最优链表结构,任意位置插入删除都是O(1)
typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	LTDataType data;
}ListNode;
// 创建节点
ListNode* BuyListNode(LTDataType x)
// 双向链表初始化
ListNode* ListInit();
// 双向链表销毁
void ListDestory(ListNode* phead);
// 双向链表打印
void ListPrint(ListNode* phead);
// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* phead);
// 双向链表头插
void ListPushFront(ListNode* phead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* phead);
// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

//bool ListEmpty(ListNode* phead);
//int ListSize(ListNode* phead);
// 创建节点
ListNode* BuyListNode(LTDataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;

	return newnode;
}

// 双向链表初始化
ListNode* ListInit()
{
	ListNode* phead = BuyListNode(0);
	phead->next = phead;  // 哨兵节点指向自己
	phead->prev = phead;  // 哨兵节点指向自己

	return phead;
}

// 双向链表销毁
void ListDestory(ListNode* phead)
{
	assert(phead);  // 断言传入的phead是否为空
	ListNode* cur = phead->next;
	while (cur != phead)   // 双向循环链表有一个哨兵节点
	{
        // 要先记录下一个节点,然后释放当前节点,否者会找不到下一个节点
		ListNode* next = cur->next; 
		free(cur);
		cur = next;
	}
    // 释放哨兵节点
	free(phead);
	phead = NULL;
}

// 双向链表打印
void ListPrint(ListNode* phead)
{
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x)
{
	/// 断言的好处,当代码有几十万行代码时,这里phead传入错误时
	/// 代码运行后里面会显示某个.c某行出现问题了
	assert(phead);
	              

	//ListNode* tail = phead->prev;       //找尾节点
	//ListNode* newnode = BuyListNode(x); //创建新的节点
	//tail->next = newnode;               //尾节点指向新的节点
	//newnode->prev = tail;               //新的节点要指向前面的节点
	//newnode->next = phead;              //新创建的尾节点要指向第一个节点
	//phead->prev = newnode;              //第一个节点要重新指向新创建的最后的节点
	
	ListInsert(phead, x);  /// 尾插就是在phead前面
}

// 双向链表尾删
void ListPopBack(ListNode* phead)
{
	assert(phead);
	//assert(phead->next != phead); // 当没有节点时,不能删
	//ListNode* tail = phead->prev;
	//ListNode* prev = tail->prev;

	//prev->next = phead;
	//phead->prev = prev;

	//free(tail);
	//tail = NULL;
	ListErace(phead->prev);
}

// 双向链表头插(不推荐写法,顺序不能变不易掌控)
//void ListPushFront(ListNode* phead, LTDataType x)
//{
//	assert(phead);
//
//	//ListNode* first = phead->next;
//	///在不创建first,后面的顺序就不能变
//	///这种方法不推荐
//	ListNode* newnode = BuyListNode(x);
//
//	newnode->next = phead->next;
//	phead->next->prev = newnode;
//
//	phead->next = newnode;
//	newnode->prev = phead;
//}

// 双向链表头插(推荐写法)
void ListPushFront(ListNode* phead, LTDataType x)
{
	assert(phead);

	//ListNode* first = phead->next;
	//ListNode* newnode = BuyListNode(x);

	//phead->next = newnode;
	//newnode->next = first;
	//first->prev = newnode;
	//newnode->prev = phead;

	ListInsert(phead->next, x);
}

// 双向链表头删
void ListPopFront(ListNode* phead)
{
	assert(phead);
	//assert(phead->next!=phead); // 当没有节点时,不能删
	//ListNode* first = phead->next;
	//ListNode* second = first->next;
	//phead->next = second;
	//second->prev = phead;

	//free(first);
	//first = NULL;
	ListErace(phead->next);
}

// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x)
{
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;
}

// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode* prev = pos->prev;
	ListNode* newnode = BuyListNode(x);

	// prev newnode pos
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
	assert(pos);

	ListNode* prev = pos->prev;
	ListNode* next = pos->next;
	prev->next = next;
	next->prev = prev;
	free(pos);
}

//bool ListEmpty(ListNode* phead);
//int ListSize(ListNode* phead);
#include "List.h"

void TestList1()
{
	ListNode* plist = ListInit();
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPrint(plist);

	ListPushFront(plist, 0);
	ListPushFront(plist, -1);
	ListPrint(plist);

	ListPopFront(plist);
	ListPopFront(plist);
	ListPopFront(plist);
	ListPrint(plist);

	ListPopBack(plist);
	ListPrint(plist);

	ListDestory(plist);
}

void TestList2()
{
	ListNode* plist = ListInit();
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPrint(plist);

	ListNode* pos = ListFind(plist, 3);
	if (pos)
	{
		// 查找,附带着修改的作用
		pos->data *= 10;
		printf("找到了,并且节点的值乘以10\n");
	}
	else
	{
		printf("没有找到\n");
	}

	ListPrint(plist);

	ListInsert(pos, 300);
	ListPrint(plist);

	ListErase(pos);
	ListPrint(plist);
}

int main()
{
	TestList1();

	return 0;
}

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


 

标签:ListNode,线性表,pos,next,链表,phead,SLTNode,plist
来源: https://blog.csdn.net/weixin_54183294/article/details/123031153