数据结构学习——线性表、顺序表

news2025/1/13 13:49:08

1.线性表

线性表 ( linear list ) 是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使 用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。

image-20240331084641957

2.顺序表

2.1概念及结构

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

注:顺序表只能从头开始连续存储。

顺序表一般可以分为:

  1. 静态顺序表:使用定长数组存储元素

    image-20240304154453711

  2. 动态顺序表:使用动态开辟的数组存储。

    image-20240304154954903

    我们思考一个问题:为什么容量空间不够时候,我们经常扩容是进行2倍扩容呢?而不是其他倍数或者常数呢?

    因为2倍扩容相对合适,越扩频率越低而且c++库里面扩容也是2倍扩容。但是1.5倍扩容或者一倍扩容都可以。

2.2接口实现

2.2.1接口实现代码:

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间 大小,所以下面我们实现动态顺序表:

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

typedef int SLDataType;//数据类型最好typedef下,否则数据类型会有各种变化,变化以后所有都得修改

typedef struct SeqList
{
	//int* a;
	SLDataType* a; //指向动态开辟的数组
	int size;      //有效数据
	int capacity;  //空间容量
}SL;

void SLInit(SL* psl);//初始化
void SLDestroy(SL* psl);//释放空间

void SLPrint(SL* psl);//打印
void SLCheakCapacity(SL* psl);//检查空间容量是否够用,不够则扩容

//头尾插入删除
void SLPushBack(SL* psl, SLDataType x);//尾插
void SLPushFront(SL* psl, SLDataType x);//头插
void SLPopBack(SL* psl);//尾删
void SLPopFront(SL* psl);//头删

//任意下标位置的插入删除
void SLInsert(SL* psl, int pos, SLDataType x);//插入
void SLErase(SL* psl, int pos);//删除

//找到返回下标
//没有找到返回-1
int SLFind(SL* psl, SLDataType x);
#include"SeqList.h"

void SLInit(SL* psl)
{
	psl->a = NULL;
	psl->size = 0;
	psl->capacity = 0;
}

void SLDestroy(SL* psl)
{
	assert(psl);

	if (psl->a != NULL)
	{
		free(psl->a);
		psl->a != NULL;
		psl->size = 0;
		psl->capacity = 0;
	}
}

void SLPrint(SL* psl)
{
	assert(psl);
	for (int i = 0; i < psl->size ;i++)
	{
		printf("%d ", psl->a[i]);
	}
	printf("\n");
}

void SLCheakCapacity(SL* psl)
{
	assert(psl);

	if (psl->size == psl->capacity)
	{
		int newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("ralloc fail");
			return;
		}

		psl->a = tmp;
		psl->capacity = newCapacity;
	}
}

void SLPushBack(SL* psl, SLDataType x)
{
	assert(psl);

	SLCheakCapacity(psl);

	psl->a[psl->size] = x;
	psl->size++;
}

void SLPushFront(SL* psl, SLDataType x)
{
	assert(psl);

	SLCheakCapacity(psl);

	int end = psl->size - 1;
	while (end >= 0)
	{
		psl->a[end + 1] = psl->a[end];
		--end;
	}
	
	psl->a[0] = x;
	psl->size++;
}

void SLPopBack(SL* psl)
{
	assert(psl);

	if (psl->size == 0)
	{
		return;
	}
	assert(psl->size >0);

	psl->size--;
}
void SLPopFront(SL* psl)
{
	assert(psl);

	assert(psl->size > 0);

	
	for (int begin = 0; begin < psl->size; begin++)
	{
		psl->a[begin] = psl->a[begin+1];
	}
	psl->size--;
}

void SLInsert(SL* psl, int pos, SLDataType x)
{
	assert(psl);
	assert(pos >= 0 && pos <= psl->size);//如果等于则是头插尾插

	SLCheakCapacity(psl);

	int end = psl->size;
	while (end > pos)
	{
		psl->a[end] = psl->a[end - 1];
		end--;
	}

	psl->a[end] = x;
	psl->size++;
}

//void SLInsert(SL* psl, int pos, SLDataType x)
//{
//	assert(psl);
//	assert(pos >= 0 && pos <= psl->size);
//
//	SLCheakCapacity(psl);
//
//	// 挪动数据
//	int end = psl->size - 1;
//	while (end >= pos)
//	{
//		psl->a[end + 1] = psl->a[end];
//		--end;
//	}
//
//	psl->a[pos] = x;
//	psl->size++;
//}

void SLErase(SL* psl, int pos)
{
	assert(psl);
	assert(pos >= 0 && pos < psl->size);

	int begin = pos;
	for(;begin<psl->size ;begin++)
	{ 
		psl->a[begin] = psl->a[begin + 1];
	}
	psl->size--;
}

int SLFind(SL* psl, SLDataType x)
{
	assert(psl);

	for (int j = 0; j < psl->size; j++)
	{
		if (psl->a[j] == x)
		{
			return j;
		}
	}

	return -1;
}
#include"SeqList.h"

void TestSL1()
{
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPrint(&sl);
	SLPushFront(&sl, 10);
	SLPushFront(&sl, 20);
	SLPushFront(&sl, 30);
	SLPrint(&sl);
	SLPopBack(&sl);
	SLPopBack(&sl);
	SLPrint(&sl);
	SLPopFront(&sl);
	SLPopFront(&sl);
	SLPrint(&sl);
}


void TestSL2()
{
	SL sl;
	SLInit(&sl);
	SLPushBack(&sl, 1);
	SLPushBack(&sl, 2);
	SLPushBack(&sl, 3);
	SLPushBack(&sl, 4);
	SLPushBack(&sl, 5);
	SLPrint(&sl);
	SLInsert(&sl, 3, 50);
	SLInsert(&sl, 3, 40);
	SLPrint(&sl);
	SLErase(&sl, 5);
	SLPrint(&sl);

	int pos = SLFind(&sl, 3);
	if (pos != -1)
	{
		SLErase(&sl , pos);
	}
	SLPrint(&sl);

}

int main()
{
	TestSL2();

	// 越界一定报错吗?
	//int a[10];
	
	// 越界读基本不会报错
	//printf("%d\n", a[10]);
	//printf("%d\n", a[11]);

	// 越界写可能会报错
	//a[10] = 1;
	//a[11] = 1;
	//因为越界的检查是一种抽查行为

	return 0;
}

free错误:

1.free位置不对

2.存在越界访问

2.2.2 接口实现思路

SList.c :

void SLCheakCapacity(SL* psl);

检查空间是否足够,若不够则:空间为0则开辟4个空间,若不为零,则将原有的空间的大小乘以2

void SLCheakCapacity(SL* psl)
{
	assert(psl);

	if (psl->size == psl->capacity)
	{
		int newCapacity = psl->capacity == 0 ? 4 : psl->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(psl->a, sizeof(SLDataType) * newCapacity);
		if (tmp == NULL)
		{
			perror("ralloc fail");
			return;
		}

		psl->a = tmp;
		psl->capacity = newCapacity;
	}
}

image-20240319160311346

尾插:

void SLPushBack(SL* psl, SLDataType x)

先判断psl链表是否为空,为空直接结束,不为空继续执行。再检查空间是否够用,不够则开辟,够则把x的值给数组a,然后size++。

void SLPushBack(SL* psl, SLDataType x)
{
	assert(psl);

	SLCheakCapacity(psl);

	psl->a[psl->size] = x;
	psl->size++;
}

头插:

void SLPushFront(SL* psl, SLDataType x);
void SLPushFront(SL* psl, SLDataType x)
{
	assert(psl);

	SLCheakCapacity(psl);

	int end = psl->size - 1;
	while (end >= 0)
	{
		psl->a[end + 1] = psl->a[end];
		--end;
	}
	
	psl->a[0] = x;
	psl->size++;
}

思路如下:

image-20240319163811791

尾删:

void SLPopBack(SL* psl);

void SLPopBack(SL* psl)
{
	assert(psl);

	if (psl->size == 0)
	{
		return;
	}
	assert(psl->size >0);

	psl->size--;
}

image-20240319211228190

头删:

void SLPopFront(SL* psl);
void SLPopFront(SL* psl)
{
	assert(psl);

	assert(psl->size > 0);

	
	for (int begin = 0; begin < psl->size; begin++)
	{
		psl->a[begin] = psl->a[begin+1];
	}
	psl->size--;
}

image-20240319211511721

任意下标位置的插入:

void SLInsert(SL* psl, int pos, SLDataType x);//插入
void SLInsert(SL* psl, int pos, SLDataType x)
{
	assert(psl);
	assert(pos >= 0 && pos <= psl->size);//如果等于则是头插尾插

	SLCheakCapacity(psl);

	int end = psl->size;
	while (end > pos)
	{
		psl->a[end] = psl->a[end - 1];
		end--;
	}

	psl->a[end] = x;
	psl->size++;
}

image-20240319212326944

任意下标位置的删除:

void SLErase(SL* psl, int pos);//删除
void SLErase(SL* psl, int pos)
{
	assert(psl);
	assert(pos >= 0 && pos < psl->size);

	int begin = pos;
	for(;begin<psl->size ;begin++)
	{ 
		psl->a[begin] = psl->a[begin + 1];
	}
	psl->size--;
}

image-20240319213303535

找下标:

//找到返回下标
//没有找到返回-1
int SLFind(SL* psl, SLDataType x);
int SLFind(SL* psl, SLDataType x)
{
	assert(psl);

	for (int j = 0; j < psl->size; j++)
	{
		if (psl->a[j] == x)
		{
			return j;
		}
	}

	return -1;
}

image-20240319213814208

2.3 数组相关面试题

  1. 删除排序数组中的重复项。

    26. 删除有序数组中的重复项 - 力扣(LeetCode)

    代码:

    int removeDuplicates(int* nums, int numsSize) 
    {
        int src=1;
        int dst=0;
        while(src<numsSize)
        {
            if(nums[src]==nums[dst])
            {
                src++;
            }
            else
            {
                dst++;
                nums[dst]=nums[src];
                src++;
            }
        }
        return dst+1;
    }
    
    

    解题思路:

    image-20240305191118302

  2. 合并两个有序数组。

    88. 合并两个有序数组 - 力扣(LeetCode)

    代码如下:

    void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
    {
        int j = m+n - 1;
        int n1 = m - 1;
        int n2 = n - 1;
        while(n1>=0 && n2>=0)
        {
            if(nums1[n1]>nums2[n2])
            {
                nums1[j--]=nums1[n1--];
            }
            else
            {
                nums1[j--]=nums2[n2--];
            }
        }
        while(n2>=0)
        {
            nums1[j--]=nums2[n2--];
        }
    }
    
    
    

    解题思路:

    image-20240305193102132

2.4 顺序表的问题及思考

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




解题思路:

[外链图片转存中...(img-Iu1JfGs6-1712583978516)]

## 2.4 顺序表的问题及思考

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1654155.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

多线程【阻塞队列】(生产者消费者模型代码实现)

阻塞队列 解耦合削峰填谷生产者消费者模型&#xff1a; 解耦合 削峰填谷 生产者消费者模型&#xff1a; 正常来说&#xff0c;wait通过notify唤醒&#xff0c;其他线程调用了take,在take的最后一步进行notify. package thread; class MyBlockingQueue{private String [] data…

jmeter控制器讲解

1&#xff0c;随机顺序控制器和随机控制器的区别&#xff1a;随机顺序控制器下所有的接口都会执行&#xff0c;只是执行顺序是随机的&#xff0c;随机控制器下所有的接口中随机执行一个接口&#xff0c;其余接口不执行。

idea 项目 修改项目文件名 教程

文章目录 目录 文章目录 修改流程 小结 概要流程技术细节小结 概要 原项目名 修改流程 关掉当前项目的idea页面 修改之后的文件名 重新打开idea。选择项目打开项目页面 技术细节 出现下面这个问题&#xff0c;可以参考作者新的一编文章idea开发工具 项目使用Spring框架开发解…

【iOS】-- 内存五大分区

【iOS】-- 内存五大分区 内存五大分区1.栈区优点&#xff1a; 2.堆区优点&#xff1a; 3.全局区4.常量区5.代码区 验证static、extern、const关键字比较1.static关键字static关键字的作用&#xff1a;全局静态变量局部静态变量 2.extern关键字对内的全局变量对外的全局变量 3.c…

[报错解决]SpringBoot子项目打jar包启动报 XXX--1.0-SNAPSHOT.jar中没有主清单属性

目录 报错信息解决原因原因分析解决方案 报错信息 解决 原因 在使用SpringBoot架构搭建父子工程时&#xff0c;使用IDEA可以正常启动&#xff0c;对子项目打成jar包后使用jar方式启动时&#xff0c;会报错xx.jar中没有主清单属性。 原因分析 原因主要是在使用jar方式启动时…

2024年第六届世界软件工程研讨会(WSSE 2024)即将召开!

2024年第六届世界软件工程研讨会&#xff08;WSSE 2024&#xff09;将于2024年9月13-15日在日本京都举行。软件工程领域的发展离不开各位专家学者和业界精英的共同努力和贡献。WSSE 2024将就软件工程领域的最新研究成果、实践经验和发展趋势进行深入交流和探讨&#xff0c;汇聚…

VxTerm使用教程:连接SSH服务端设备,什么是SSH

一、什么是SSH&#xff1f; <摘自百度> 安全外壳协议 SSH&#xff0c;即安全外壳协议&#xff08;Secure Shell&#xff09;&#xff0c;是一种网络协议&#xff0c;用于在计算机网络上提供安全的远程登录和命令执行功能。 SSH通过加密通信通道来保护数据传输&#xff0c…

企业车辆管理系统参考论文(论文 + 源码)

【免费】关于企业车辆管理系统.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89282550 企业车辆管理系统 摘 要 随着经济的日益增长,车辆作为最重要的交通工具,在企事业单位中得以普及,单位的车辆数目已经远远不止简单的几辆,与此同时就产生了车辆资源的合理…

Django-新冠疫情数据分析系统-67684

目 录 摘要 1 绪论 1.1 研究背景 1.2论文结构与章节安排 2 新冠疫情数据分析系统系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据增加流程 2.2.2 数据修改流程 2.2.3 数据删除流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系统用例分析…

Nacos单机模式集成MySQL

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站&#xff0c;这篇文章男女通用&#xff0c;看懂了就去分享给你的码吧。 Nacos支持三种部署…

射频无源器件之电桥

一. 电桥的定义及作用 电桥主要用于实现微波大功率功放系统的功率合成分配,信号采集等功能,被广泛应用于中国及全球4G/5G基站、5G网络覆盖、北斗导航天线、车载高精度导航(无人驾驶)天线等。可将信号分成有相位差的两路,90度电桥相位差90,180度电桥相位差180。 常说的3d…

Failed to get DISPLAY: Error: All configured authentication methods failed 解决方法

Vscode一连接远程服务器就报错&#xff1a; 这个时候我们是无法使用Xming显示图像的。 尝试后发现&#xff0c;Windows电脑能够ping通服务器ip&#xff0c;但是服务器ping不通Windows电脑&#xff1a; 在网上查攻略&#xff0c;设置Windows电脑ip地址白名单&#xff0c;但…

STC8增强型单片机开发——库函数

一、使用库函数点灯 导入库函数。 下载STC8H的库函数&#xff1a;&#x1f4ce;STC8G-STC8H-LIB-DEMO-CODE_2023.07.17_优化版.zip 来到库函数的目录下&#xff0c;拷贝以下文件&#xff1a; Config.hType_def.hGPIO.hGPIO.c 新建项目&#xff0c;将拷贝的4个文件放到项目目录…

[iOS]从拾遗到Runtime(上)

[iOS]从拾遗到Runtime(上) 文章目录 [iOS]从拾遗到Runtime(上)写在前面名词介绍instance 实例对象class 类对象meta-class 元类对象为什么要有元类&#xff1f; runtimeMethod(objc_method)SEL(objc_selector)IMP 类缓存(objc_cache)Category(objc_category) 消息传递消息传递的…

嘎嘎好用的虚拟键盘第二弹之中文输入法

之前还在为不用研究输入中文而暗自窃喜 这不新需求就来了&#xff08;新需求不会迟到 它只是在路上飞一会儿&#xff09; 找到了个博主分享的代码 是好使的 前端-xyq 已经和原作者申请转载了 感谢~~ 原作者地址&#xff1a;https://www.cnblogs.com/linjiangxian/p/16223681.h…

初识C++ · 模板初阶

目录 1 泛型编程 2 函数模板 3 类模板 1 泛型编程 模板是泛型编程的基础&#xff0c;泛型我们碰到过多次了&#xff0c;比如malloc函数返回的就是泛型指针&#xff0c;需要我们强转。 既然是泛型编程&#xff0c;也就是说我们可以通过一个样例来解决类似的问题&#xff0c…

通过AOP实现项目中业务服务降级功能

最近项目中需要增强系统的可靠性&#xff0c;比如某远程服务宕机或者网络抖动引起服务不可用&#xff0c;需要从本地或者其它地方获取业务数据&#xff0c;保证业务的连续稳定性等等。这里简单记录下业务实现&#xff0c;主要我们项目中调用远程接口失败时&#xff0c;需要从本…

Springboot 单体thymeleaf极简门户网站

企业门户网站&#xff0c;基于Springboot和layui 1、原介绍 使用技术&#xff1a;后端框架&#xff1a;SpringBoot&#xff0c;Mybatisplus ### 数据库&#xff1a;MySQL,redis ## 前端框架&#xff1a;Layui ## 权限框架&#xff1a;shiro ## 网页模板引擎&#xff1a;thyme…

Python基础详解三

一&#xff0c;函数的多返回值 def methodReturn():return 1,2x,ymethodReturn() print(x,y) 1 2 二&#xff0c;函数的多种参数使用形式 缺省参数&#xff1a; def method7(name,age,address"淄博"):print("name:"name",age"str(age)&quo…

案例分享:BACnet转Modbus提升暖通系统互操作性

现代智能建筑中系统的集成与互操作性是决定其智能化程度的关键因素。随着技术的发展&#xff0c;不同标准下的设备共存成为常态&#xff0c;而BACnet与Modbus作为楼宇自动化领域广泛采用的通讯协议&#xff0c;它们之间的无缝对接显得尤为重要。本文将通过一个实际案例&#xf…