数据结构(链表)(3)

数据结构(链表)(3)

  • 这节的内容对于上一节理解的比较好的同学是非常简单的. 这节的主要任务就是接着完善几个函数接口. 单链表这个内容就差不多了.
  • 数据结构之间的功能都是差不多的, 不过区别多是效率上的区别, 所以剩下的这些接口无非就是查找数据, 还有指定位置插入数据. 还有指定位置删除数据.
  • 不过实现起来肯定是会有所不同, 毕竟是不同的结构.
  • 那么第一个函数, 查找数据:

  • SLTNode* SLTFind(SLTNode* phead, SLTDataType x)
    {
    	SLTNode* cur = phead;
    	while (cur)
    	{
    		if (cur->data == x)
    		{
    			return cur;
    		}
    
    		cur = cur->next;
    	}
    
    	return NULL;
    }
    

请添加图片描述

  • 然后就是指定位置插入数据, 这里链表说的更直接点, 其实就是指定位置之前插入数据, 其实顺序表里面的指定位置插入数据也是一个意思, 也是指定位置之前插入数据.
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		SLTNode* newnode = BuySListNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

请添加图片描述

  • 然后链表还多了一个就是指定位置之后插入数据
// 在pos以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDataType x)
{
	assert(pos);

	SLTNode* newnode = BuySListNode(x);
	pos->next = newnode;
	newnode->next = pos->next;
}

请添加图片描述

  • 有插入数据就有删除数据, 一样的两种, 先是第一种:
// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		free(pos);
		//pos = NULL;
	}
}

请添加图片描述

  • 最后一个函数, 指定位置之后删除数据:
// 删除pos的后一个位置
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);

	// 检查pos是否是尾节点
	assert(pos->next);

	SLTNode* posNext = pos->next;

	pos->next = posNext->next;

	free(posNext);
	posNext = NULL;
}

请添加图片描述

  • 链表头文件: SList.h
#pragma once
#include<stdio.h>
#include<stdlib.h>

typedef int SLTDatatype;
//链表节点的结构:
typedef struct SListNode
{
	SLTDatatype data;
	struct SListNode* next;

}SLTNode;

//循环遍历打印链表
void PrintSList(SLTNode* phead);

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

//尾部插入数据
void SLTPushBack(SLTNode** pphead, SLTDatatype x);
//头部插入数据
void SLTPushFront(SLTNode** pphead, SLTDatatype x);

//尾部删除数据
void SLTPopBack(SLTNode** pphead);
//头部删除数据
void SLTPopFront(SLTNode** pphead);

//查找数据
SLTNode* SLTFind(SLTNode* phead, SLTDatatype x);

// 在pos之前插入x
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDatatype x);

// 在pos以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDatatype x);

// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos);

// 删除pos的后一个位置
void SLTEraseAfter(SLTNode* pos);

  • 函数实现文件, SList.c
#include"SList.h"

//循环遍历打印链表
void PrintSList(SLTNode* phead)
{
	//把头结点的值赋给临时变量cur,不要用phead来遍历,
	//因为等会可能还会用到phead,结果被改了,就不好弄了.
	SLTNode* cur = phead;

	while (cur != NULL)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}

	printf("NULL\n");
}

//动态申请一个节点
SLTNode* BuySListNode(SLTDatatype x)
{
	//动态开辟一个节点的结构体空间
	SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
	//判断有没有创建成功
	if (newnode == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	//创建成功之后就赋值
	newnode->data = x;
	newnode->next = NULL;
	//返回结构体指针
	return newnode;
}

//尾部插入数据
void SLTPushBack(SLTNode** pphead, SLTDatatype x)
{
	assert(pphead);

	SLTNode* newnode = SLTBuyNode(x);

	//链表为空,新节点作为phead
	if (*pphead == NULL) {
		*pphead = newnode;
		return;
	}
	//链表不为空,找尾节点
	SLTNode* ptail = *pphead;
	while (ptail->next)
	{
		ptail = ptail->next;
	}
	//ptail就是尾节点
	ptail->next = newnode;
}
//头部插入数据
void SLTPushFront(SLTNode** pphead, SLTDatatype x)
{
	SLTNode* newnode = BuySListNode(x);

	newnode->next = *pphead;
	*pphead = newnode;
}

//尾部删除数据
void SLTPopBack(SLTNode** pphead)
{
	assert(pphead);
	// 1、空
	assert(*pphead);

	// 2、一个节点
	// 3、一个以上节点
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		//SLTNode* tailPrev = NULL;
		//SLTNode* tail = *pphead;
		//while (tail->next)
		//{
		//	tailPrev = tail;
		//	tail = tail->next;
		//}

		//free(tail);
		tail = NULL;
		//tailPrev->next = NULL;

		SLTNode* tail = *pphead;
		while (tail->next->next)
		{
			tail = tail->next;
		}

		free(tail->next);
		tail->next = NULL;
	}
}
//头部删除数据
void SLTPopFront(SLTNode** pphead)
{
	assert(pphead);

	// 空
	assert(*pphead);

	// 非空
	SLTNode* newhead = (*pphead)->next;
	free(*pphead);
	*pphead = newhead;
}

SLTNode* SLTFind(SLTNode* phead, SLTDatatype x)
{
	SLTNode* cur = phead;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;
}

void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDatatype x)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		SLTPushFront(pphead, x);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		SLTNode* newnode = BuySListNode(x);
		prev->next = newnode;
		newnode->next = pos;
	}
}

// 在pos以后插入x
void SLTInsertAfter(SLTNode* pos, SLTDatatype x)
{
	assert(pos);

	SLTNode* newnode = BuySListNode(x);
	pos->next = newnode;
	newnode->next = pos->next;
}

// 删除pos位置
void SLTErase(SLTNode** pphead, SLTNode* pos)
{
	assert(pphead);
	assert(pos);

	if (pos == *pphead)
	{
		SLTPopFront(pphead);
	}
	else
	{
		SLTNode* prev = *pphead;
		while (prev->next != pos)
		{
			prev = prev->next;
		}

		prev->next = pos->next;
		free(pos);
		//pos = NULL;
	}
}

// 删除pos的后一个位置
void SLTEraseAfter(SLTNode* pos)
{
	assert(pos);

	// 检查pos是否是尾节点
	assert(pos->next);

	SLTNode* posNext = pos->next;

	pos->next = posNext->next;

	free(posNext);
	posNext = NULL;
}
  • 到这里, 咱们的单链表的内容就告一段落了.
  • 其实可以发现, 单链表的做事效率也不咋高, 就好比如, 尾部插入个数据还得遍历链表找尾巴, 指定位置之前插入, 等等都需要遍历链表, 什么的, 所以说效率不高, 咱们如果到实际应用上, 这种简单的单链表使用起来也不会很多, 这部分主要是让大家学习链表这个数据结构用的.
  • 后面还会有带头双向循环链表这些更好用的链表, 功能实现起来也很简单, 这就放到后面再讲解了.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值