数据结构修炼第二篇:顺序表和链表

这篇具有很好参考价值的文章主要介绍了数据结构修炼第二篇:顺序表和链表。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

系列文章目录

第一章 时间复杂度和空间复杂度

第二章 顺序表,列表

第三章 栈和队列

第四章 二叉树

第五章 排序


作者:🎈乐言🎈

简介:🎈大一学生,目前在致力于c/c++/python,高数的学习,有问题尽管问我,关注后私聊!

持续更新专栏:《c进阶》,《数据结构修炼》🚀(优质好文持续更新中)🎈

文章目录

目录

系列文章目录

文章目录

前言

线性表

各个接口的实现

1.初始化顺序表

2.销毁顺序表

3.检查顺序表容量是否满了

4.顺序表尾插

5.顺序表尾删

6.顺序表头插

7.顺序表头删

8.在顺序表中查找定值

9.在顺序表指定位置插入数据

链表

无头单向循环链表的实现

单链表定义:

 动态申请一个节点

销毁(释放)所有节点

打印单链表

单链表头插

单链表尾删

单链表头部删除

单链表在pos位置之后插入

单链表中删除位置为pos的节点

单链表删除指定pos位置之后的节点

求单链表的长度:

判断但俩表是否为空

总结



前言

在顺序表和链表的学习过程中,相信大家会有一些困惑,因为这里用到了大量结构体和数组的知识,乐言在这里将会对顺序表和单链表进行深刻的讲解,有代码问题,可以后台私信作者!!!!!!!!如果文章有错误,欢迎指正!!!!!!


🚀线性表

线性表(linear list)是n个具有相同特性的数据元素的 有限序列 。 线性表是一种在实际中广泛使
用的数据结构,常见的线性表: 顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在 物理结构上并不一定是连续的
线性表在物理上存储时,通常以 数组 链式结构 的形式存储

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存
储。在数组上完成数据的增删查改。
顺序表有静态的和动态的两种,首先我们来看 静态的顺序表的代码:
#define N 10000
typedef int sldataline;
struct Seqline
{
    sldataline a[N];
    int size;
};

我们在这里可以发现,静态的顺序表开辟的内存是固定的,给多了浪费空间,给少了又不够,还得重新调整代码,所以静态的顺序表十分不好用,在实际的生活场景中用的也不多。

所以我们通常使用动态的顺序表:

typedef int SLDataType;
typedef struct Seqlist
{
    SLDataTpye *a;
    size_t size;
    size_t capacity
}SeqList;

我们要加上容量空间,同时size为存储的有效数据的个数 

如果空间不够就可以扩容

🚀各个接口的实现

1.初始化顺序表

要防止传进来的指针为空,所以一定要提前加上断言

void SeqListInit(SeqList*psl)
{
    assert(psl!=NULL);
    psl->size=0;
    psl->capacity=0;
}

2.销毁顺序表

依然要记得加上断言,防止传进来的指针是空指针

void SeqListDestory(SeqList*psl)
{
    assert(psl!=NULL);
    free(psl->a);
    psl->size=0;
    psl->capacity=0;
}

首先释放开辟的空间,然后将指针置为空,将数据和空间容量大小都重置为0

3.检查顺序表容量是否满了

如果我们一个个加入数据,这样重复操作对我们来说太过麻烦,所以我们可以一次性扩容两倍,这样能应付大多数情况,两倍是一个折中的方式

void CheckCapacity(SeqList*psl)
{
    assert(psl!=NULL);
    if(psl->size == psl->capacity)
    {
        size_t newcapacity;
        if(psl->capacity == 0)
            newcapacity = psl->capacity = 4;
        else
            newcapacity = 2 * psl->capacity;
    }
    SLDataTpye*p=(SLDataType*)realloc(psl->a,newcapacity*sizeof(SLDataType));//扩容
    if(p == NULL)
          {
               perror("realloc");
                exit(-1);
            }
    psl->p;
    psl->capacity = newcapacity;
}

4.顺序表尾插

void SeqListPushBack(Seqlist*psl,SLDataType x)
{
    assert(psl !=NULL);
    CheckCapacity(psl);
    psl->a[psl->size] =x;
    psl->size++;
}

5.顺序表尾删

void SeqListPopBack(SeqList*psl)
{
    assert(psl->NULL);
    assert(psl->size >0);
    psl->size --;
}

因为我们无法得知SLDataType是什么类型,他随时都可以改,因此我们不能单纯直接将随意赋值为0(psl->a[psl->size-1]=0)

6.顺序表头插

因为顺序表是连续存储的,所以我们在顺序表头部插入的时候,我们要依次挪动数据

void SeqListPushFront(SeqList*psl,SLDType x)
{
    assert(psl);
    CheckCapacity(psl);
    int i = 0;
    for(i=psl->size-1;i>=0;i--)
    {
        psl->a[i+1] = psl->a[i];
    }
    psl->a[0] = x;
    psl->size++;
}

7.顺序表头删

因为顺序表是连续存储的,所以要依次挪动数据

void SeqListPopFront(SeqList* psl)
{
    assert(psl);
    assert(psl->size >0);
    int i =0;
    for(i=1;i<psl->size;i++)
        {
            psl->a[i-1]=psl->a[i];
        }
        psl->size--;
}

依次覆盖即可,然后再把有效数据-1;

8.在顺序表中查找定值

int SeqListFind(const SeqList*psl,SLDataType x)
{
    assert(psl);
    int i = 0;
    for(i=0;i<psl->size;i++)
    {
        if(psl->[i]==x)
        {
            return i;
        }
    }
     return -1;
}

9.在顺序表指定位置插入数据

void SeqListInsert(SeqList*psl,size_t pos,SLDataType x)
{
    assert(psl);
    assert(pos>=0 && pos <=psl->size);
    CheckCapacity(psl);
    size_t i =0;
    for(i=psl->size;i>pos;i--)
        {
            psl->a[i]=psl->a[i-1];
        }
    psl->a[pos]=x;
    psl->size++;
}

🚀链表

顺序表我们在使用时,虽然下标可以随时访问,但是会出现一系列问题会引起我们的思考

1. 中间/头部的插入删除,时间复杂度为O(N)
2. 增容需要申请新空间,拷贝数据,释放旧空间。会有不小的消耗。
3. 增容一般是呈2倍的增长,势必会有一定的空间浪费。例如当前容量为100,满了以后增容       到 200,我们再继续插入了5个数据,后面没有数据插入了,那么就浪费了95个数据空           间。
思考:如何解决以上问题呢?下面给出了链表的结构来看看

🚀无头单向循环链表的实现

第一步我们依然是先新建一个工程:

数据结构修炼第二篇:顺序表和链表

  • SList.h(单链表的类型定义,函数接口声明,引用头文件)
  • SList.c(单链表各个接口函数的实现)
  • test.c(主函数,测试函数的各个接口功能) 

单链表定义:

单链表类似如图结构

数据结构修炼第二篇:顺序表和链表

一部分储存结构体的数据,另一部分储存下一块结构体空间的地址

代码如下:

typedef int SLDatatype;//定义单链表数据类型

typedef struct SListNode//定义单链表节点
{
    SLDataType data;//数据空间
    struct SListNode*next;//指针空间
}SListNode;

 动态申请一个节点

代码如下:

SListNode* BuySListNode(SLTDataType x)
{
    SListNode* node=(SListNode*)malloc(sizeof(SListNode));
    if(node == NULL)
    {
        perror("malloc");
        return;
    }
}

销毁(释放)所有节点

我们在这里有一个遍历链表的操作

{
	SListNode* node = (SListNode*)malloc(sizeof(SListNode));
	if (node == NULL)
	{
		perror("malloc");
		return;
	}
	node->data = x;
	node->next = NULL;
}
void SListDestory(SListNode** pphead)
{
	assert(pphead);
	SListNode* cur = *pphead;
	while (cur != NULL)
	{
		SListNode* next = cur->next;
		free(cur);
	}
	*pphead = NULL;
}

最后不能忘了置空指针

打印单链表

void SListPrint(SListNode* phead)
{
	SListNode* ptr = phead;
	while (ptr != NULL)
	{
		printf("%d->", ptr->data);
		ptr = ptr->next;
	}
	printf("NULL\n");
}

单链表尾插

下面是一个明显的错误

数据结构修炼第二篇:顺序表和链表

 代码看似逻辑清晰,其实问题暴露无遗。此处的指针传参,相当于把plist指针变量的值拷贝给phead给phead赋值newhead,phead的值改变是不会影响plist

这种写法会导致一个问题:         

  • 当链表为空时,我们需要改变plist的指向,让他指向第一个节点,然而初始值plist和phead都指向NULL,调用函数后,phead指向了新的节点,但是plist还是指向NULL
  • 问题出现,我们又该如何解决?

plist指向的是第一个节点的指针,想要在函数中改变plist的值(指向),必须把plist指针进行指针传参,因为plist原本就是一级指针,我们要想改变他,我们只能取他的地址进行传参

在函数中,我们想要改变int的值,就要传递int*,当我们想要传递int*的时候,我们就要传递int**......

正确写法是通过二级指针传参,改变plist的指向

单链表为空的时候,plist直接指向新节点

单链表不为空,会找到尾部节点,然后将尾部节点的next指向新节点!!!

//单链表尾插
void SListPushBack(SListNode** pphead, SLTDataType x)
{
	assert(pphead);
	SListNode* newnode = BuyListNode(x);
	if (*pphead == NULL)
		*pphead = newnode;
	else if (**pphead != NULL)
	{
		SListNode* tail = *phead;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

单链表头插

头插具体实现算法如图:

新数据的next指向plist指向的节点数据结构修炼第二篇:顺序表和链表

 代码实现:

void SListPushFront(SListNode* pphead, SLTDataType x)
{
	assert(pphead);
	SListNode* newnode = BuySListNode(x);
	newnode->next = *pphead;
	pphead = *newnode;
}

单链表尾删

思想:

1:找到链表尾节点的上一个节点。

2:双指针,分别找到链表尾节点和它上一个节点

如图所示:

数据结构修炼第二篇:顺序表和链表

 数据结构修炼第二篇:顺序表和链表

  •  当单链表只有一个节点时,删除节点,plist指向NULL
  • 而当单链表有多个节点时,先找到单链表尾节点的前一个节点,删除,然后将该节点的next指向NULL
  • 可能会改变plist的指向,所以我们要用二级指针接受

代码如下:

void SListPopBack(SListNode** pphead)
{
	assert(pphead);
	assert(*pphead);
	SListNode* tail = *pphead;
	if ((*pphead)->next == NULL)
	{
		free(*pphead);
		*pphead = NULL;
	}
	else
	{
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

如果pphead为空,那么直接释放+置空

如果pphead不为空,那么直接判断下一个节点是否为空为空则置空,不为空则继续判断

思路2代码实现:

void SListPopBack(SListNode** phead)
{
	assert(pphead);
	assert(*pphead);
	SListNode* tail = *pphead;
	SListNode* prev = *pphead;
	while (tail->next)
	{
		prev = tail;
		tail = tail->next;
	}
	free(tail);
	prev->next = NULL;
}

单链表头部删除

void SListPopFront(SListNode** pphead)
{
	assert(pphead);
	assert(**pphead);
	SListNode* cur = *pphead;
	*pphead = cur->next;
	free(cur);
	cur = NULL;
}

将*pphead移向第二个节点即可。

在单链表中查找指定点的节点

SListNode* SListFind(SListNode** pphead, SLDataType x)
{
	SListNode* cur = *pphead;
	while (cur != NULL)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

单链表在pos位置之后插入

数据结构修炼第二篇:顺序表和链表

 我们只需在pos位置的next指向一个新开辟的地址,新地址的next指向原来的下一个地址

代码实现:

void SListInsertAfter(SListNode* pos, SLDataType x)
{
	assert(pos);
	SListNode* newnode = BuySListNode(x);
	newnode->next = pos->next;
	pos->next = newnode;
}

单链表中删除位置为pos的节点

考虑到pos位置可能为第一个节点,所以我们有必要选择进行语句

代码如下:

void SListDel(SListNode** pphead,SListNode* pos)
{
	assert(pphead);
	assert(*pphead);
	assert(pos);
	if (pos == *pphead)
	{
		SListPopFront(pphead);
	}
	else
	{
		SListNode* prev = *pphead;
		while (prev->next != pos)
			prev = prev->next;
	}
	prev->next = pos->prev;
	free(pos);
	pos = NULL;
}

单链表删除指定pos位置之后的节点

图示:

数据结构修炼第二篇:顺序表和链表

代码实现: 

此处我们要定义一个新的指针,来存放pos的下一个节点的地址

void SListDelAfter(SListNode* pos)
{
	assert(pos);
	assert(pos->next);
	SListNode* posnext = pos->next;
	pos->next = pos->next->next;
	free(posnext);
}

求单链表的长度:

遍历寻找是否为空即可

int SListSize(SListNode* phead)
{
	int size = 0;
	SListNode* cur = phead;
	while (cur != NULL)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

判断但俩表是否为空

bool SListEmpty(SListNode* phead)
{
	return phead == NULL;
}

🚀总结

本文具体写了顺序表和单链表增删查改的一系列操作,希望各位同志们也要动手操作一下,这样才能融会贯通,熟稔于心文章来源地址https://www.toymoban.com/news/detail-432322.html

到了这里,关于数据结构修炼第二篇:顺序表和链表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构顺序表和链表(超详细)

    线性表 ( linear list ) 是 n 个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构, 常见的线性表:顺序表、链表、栈、队列、字符串 ... 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的, 线性表在

    2024年02月13日
    浏览(31)
  • 【手撕数据结构】(三)顺序表和链表

    🎗️线性表是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串… 🎗️线性表在逻辑上是线性结构,也就说是一条连续的直线。但是在物理结构上并不一定是连续的,线性表在物理上存储

    2024年02月05日
    浏览(28)
  • 【数据结构初阶】顺序表和链表(1)

    线性表(linear list) 是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串… 线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,线性表在物理上

    2024年02月08日
    浏览(31)
  • 数据结构奇妙旅程之顺序表和链表

    ꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶ 个人主页:xiaoxieʕ̯

    2024年02月05日
    浏览(41)
  • Mysql第二篇---InnoDB数据存储结构

    索引结构给我们提供了高效的索引方式, 不过索引信息以及数据记录都是保存在文件上的(innodb的ibd文件, MyISAM的MyI和MyD文件), 确切的说是存储在页结构中. 另一方面, 索引是在 存储引擎 中实现的, MySQL服务器上的存储引擎负责对表中数据的读取和写入工作. 不同存储引擎中存放

    2024年02月07日
    浏览(41)
  • [ 数据结构 -- 手撕排序算法第二篇 ] 冒泡排序

    手撕排序算法系列之:冒泡排序。 从本篇文章开始,我会介绍并分析常见的几种排序,大致包括 插入排序 , 冒泡排序 ,希尔排序,选择排序,堆排序,快速排序,归并排序等。 大家可以点击此链接阅读其他排序算法:排序算法_大合集(data-structure_Sort) 本篇主要来手撕冒

    2024年02月11日
    浏览(36)
  • 初阶数据结构之---顺序表和链表(C语言)

    线性表: 线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构。线性表在逻辑上是线性结构,也就是说是连续的一条直线。但在物理上并不一定是连续的。线性表在物理上存储时,通常以 数组 和 链式结构 的形式存储。

    2024年02月22日
    浏览(38)
  • 【数据结构篇C++实现】- 线性表 - 顺序表和链表

    友情链接:C/C++系列系统学习目录 故事导入: 幼儿园放学时,一个班级的小朋友,一个跟着一个排队出校,有一个打头,有一个收尾,当中的小朋友每一个都知道他前面和后面的一个是谁,就如同一根线把他们串联了起来。如此具有像线一样的性质的表就叫线性表 线性表(

    2024年02月07日
    浏览(34)
  • <数据结构>顺序表和链表的比较|缓存命中率

    💭前言:通过之前对顺序表和链表的实现,我们可以发现在增删查改某些操作上两者的效率前言有所差异,本篇文章将总结二者的异同。 顺序表的实现 http://t.csdn.cn/Lxyg2 单链表的实现 http://t.csdn.cn/rHgjG 双链表的实现 http://t.csdn.cn/j3amO 📚顺序表通过数组来实现的,所以在物理

    2024年02月05日
    浏览(29)
  • 【数据结构.C】顺序表和单链表的增删查改

    宝子,你不点个赞吗?不评个论吗?不收个藏吗? 最后的最后,关注我,关注我,关注我,你会看到更多有趣的博客哦!!! 喵喵喵,你对我真的很重要。 目录 单链表增删查改 c1.h sqlist.c number.c 单链表的增删查改  c1.h stuscore.c c1.h sqlist.c number.c  c1.h stuscore.c   宝子,你不点

    2024年02月11日
    浏览(87)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包