【C/C++】动态顺序表详解(附完整源码)

news2024/11/17 23:54:10

本章内容

写在前面

1.静态与动态是指什么?

2.动态顺序表结构的定义

3.动态顺序表的函数接口实现

4.动态顺序表的问题及思考

5.关于顺序表的OJ题

6.OJ答案及解析

1.移除元素

2.删除有序数组中的重复项

​3.合并两个有序数组

7.动态顺序表完整源码

1.SeqList.h

2.SeqList.c


写在前面

上一章我们学习了静态顺序表的增删查改,并认识了静态顺序表的存储结构与静态顺序表的在不同场景下的优劣。静态顺序表与动态顺序表都叫做顺序表,只不过我们平时口头所提的顺序表指的是动态顺序表。静态顺序表的局限性太高,无法应对各种复杂的情况所以我们几乎不用它。本章我们就来学习动态顺序表的实现。

1.静态与动态是指什么?

静态顺序表:顺序表的大小固定,存储的数据个数有限。

#define N 5
typedef int SLDataType;

//静态顺序表
typedef struct SeqList
{
	SLDataType a[N];//定长数组
	int size;//记录存储多少个有效数据
}SeqList;

动态顺序表:运用动态内存管理,可按需调整顺序表的大小。

typedef int SLDataType;

//动态顺序表
typedef struct SeqList
{
	SLDataType* a;
	int size;//记录有多少个有效数据
	int capacity;//记录顺序表的容量大小
}SeqList;

2.动态顺序表结构的定义

typedef int SLDataType;

//动态顺序表
typedef struct SeqList
{
	SLDataType* a;
	int size;//记录有多少个有效数据
	int capacity;//记录顺序表的容量大小
}SeqList;

3.动态顺序表的函数接口实现

//初始化顺序表
void SLInit(SeqList* ps);
//释放空间
void SLDestroy(SeqList* ps);
//检查顺序表是否已满
void CheakCapacity(SeqList* ps);
//动态顺序表的尾插
void SLPushBack(SeqList* ps, SLDataType data);
//动态顺序表的尾删
void SLPopBack(SeqList* ps);
//动态顺序表的头插
void SLPushFront(SeqList* ps, SLDataType data);
//动态顺序表的头删
void SLPopFront(SeqList* ps);
//pos位置插入数据
void SLInsert(SeqList* ps, int pos, SLDataType data);
//pos位置删除数据
void SLErase(SeqList* ps, int pos);
//查找数据
int SLFind(SeqList* ps, SLDataType data, int begin);
//判断数组是否已满
bool IsFull(SeqList* ps);
//打印存储的数据
void SLPrint(SeqList* ps);

以下是各个接口的实现:

void SLInit(SeqList* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}
void SLDestroy(SeqList* ps)
{
	assert(ps);
	//若ps->a不为NULL,则释放空间
	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capacity = ps->size = 0;
	}
}
void CheakCapacity(SeqList* ps)
{
	assert(ps);
	if (ps->capacity == ps->size)
	{
		//若是第一次扩容,则大小为4;
		//若不是第一次,则每次扩容为之前容量的2倍
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("malloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
	//检查是否做到扩容
	printf("扩容成功,现在的容量为:%d\n", ps->capacity);
}
void SLPushBack(SeqList* ps, SLDataType data)
{
	assert(ps);
	CheakCapacity(ps);
	//插入数据
	ps->a[ps->size] = data;
	ps->size++;
}
void SLPopBack(SeqList* ps)
{
	assert(ps);
	ps->size--;
}
void SLPushFront(SeqList* ps, SLDataType data)
{
	assert(ps);
	CheakCapacity(ps);
	//挪动数据
	for (int i = ps->size - 1; i >= 0; i--)
	{
		ps->a[i + 1] = ps->a[i];
	}
	//插入数据
	ps->a[0] = data;
	ps->size++;
}
void SLPopFront(SeqList* ps)
{
	assert(ps);
	assert(ps->size > 0);
	//挪动数据
	for (int i = 0; i < ps->size - 1; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->size--;
}
void SLInsert(SeqList* ps, int pos, SLDataType data)
{
	assert(ps);
	CheakCapacity(ps);
	//挪动数据
	for (int i = ps->size - 1; i >= pos; i--)
	{
		ps->a[i + 1] = ps->a[i];
	}
	//插入数据
	ps->a[pos] = data;
	ps->size++;
}
void SLErase(SeqList* ps, int pos)
{
	assert(ps);
	assert(ps->size > 0);
	//挪动数据
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->size--;
}
int SLFind(SeqList* ps, SLDataType data, int begin)
{
	assert(ps);
	assert(begin < ps->size);
	for (int i = begin; i < ps->size; i++)
	{
		if (ps->a[i] == data)
		{
			return i;
		}
	}
	return -1;
}
void SLPrint(SeqList* ps)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

4.动态顺序表的问题及思考

动态顺序表与静态顺序表的差别仅仅在与一个容量是固定的,一个可以按需扩容。

动态顺序表看似灵活性有所提高但仍存在着空间浪费,特别是扩容次数越多,越容易造成空间浪费。那么有没有其他的数据结构可以解决这个问题呢?答案是肯定的。这个神奇的数据结构叫做链表,它是一种基于节点的数据结构。下一章我们就会见到它。

动态顺序表和静态顺序表都称为顺序表,它们的性能是相同的。关于顺序表的性能优劣我已经在上一章介绍过了,点击小卡片即可跳转。

静态顺序表详解icon-default.png?t=MBR7http://t.csdn.cn/zwU7I

5.关于顺序表的OJ题

1.移除元素

原地移除数组中所有的元素val,要求时间复杂度为O(N),空间复杂度为O(1)。 

移除元素icon-default.png?t=MBR7https://leetcode.cn/problems/remove-element/

2.删除有序数组中的重复项

删除有序数组中的重复项icon-default.png?t=MBR7https://leetcode.cn/problems/remove-duplicates-from-sorted-array/

3.合并两个有序数组

合并两个有序数组icon-default.png?t=MBR7https://leetcode.cn/problems/merge-sorted-array/

6.OJ答案及解析

1.移除元素

题目要求不能开辟新的数组,所以我们的做法是遇到等于val的元素时,将数组末尾的元素依次倒着覆盖掉等于val的元素。

代码实现;

int removeElement(int* nums, int numsSize, int val){
    int i=0;
    while(i<numsSize)
    {
        while(val==nums[i]&&i<numsSize)
        {
           nums[i]=nums[numsSize-1];
           numsSize--;
        }
        i++;
    }
    return numsSize;

}

以数组nums={3,2,5,6,3,5},val=3为例。

第一步,检查索引为0处的值是否等于val;

 第二步,将索引为5处的值拷贝到索引为0处;

 第三步,检查索引为1处的值;

第四步,检查索引为2处的值;

 第五步,检查索引为3处的值;

 第六步,检查索引为4处的值;

 第七步,将索引为4处的值拷贝到索引为4处;

此时i已经大于numsSize,循环结束。 

2.删除有序数组中的重复项

本题我采用的是双指针的方式,p1来记录当前位置,p2来寻找与p1处的值不相同的元素。

代码实现:

int removeDuplicates(int* nums, int numsSize){
    int* p1=nums;
    int* p2=nums+1;
    int i=0;
    int returnSize=1;//至少有一个元素
    for(i=0;i<numsSize-1;i++)
    {
        if(*p1!=*(p2+i))
        {
            *(++p1)=*(p2+i);
            returnSize++;
        }
    }
    return returnSize;
}

以数组nums={0,0,1,1,1,2,2,3,3,4}为例。

 

3.合并两个有序数组

这道题目我采用的是双指针的方式。p1指向nums1,p2指向nums2。另外需要额外开辟一个数组arr,当合并完成之后再将arr里的内容拷贝到nums1中。

代码实现:

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n){
    int i=0;
    int j=0;
    int arr[200]={0};
    int* p1=nums1;
    int* p2=nums2;
    int k=0;//记录arr数组里元素的个数
    //检查数组是否为空
    if(nums2Size==0)
        return;
    //将nums1或nums2任何一个数组拷贝完成之后就结束
    while(i<nums1Size-nums2Size&&j<nums2Size)
    {
        if(p1[i]<p2[j])
        {
            arr[k]=p1[i];
            i++;
        }
        else
        {
            arr[k]=p2[j];
            j++;
        }
        k++;
    }
    //如果nums1先结束,将nums2中剩余的元素全部拷贝到arr
    if(i==nums1Size-nums2Size)
    {
        for(;j<nums2Size;j++)
        {
            arr[k++]=p2[j];
        }
    }
    //如果nums2先结束,将nums1中剩余的元素全部拷贝到arr
    if(j==nums2Size)
    {
        for(;i<nums1Size-nums2Size;i++)
        {
            arr[k++]=p1[i];
        }
    }
    //将arr中的元素拷贝回nums1
    for(k=0;k<nums1Size;k++)
    {
        nums1[k]=arr[k];
    }
}

7.动态顺序表完整源码

1.SeqList.h

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

typedef int SLDataType;

//动态顺序表
typedef struct SeqList
{
	SLDataType* a;
	int size;//记录有多少个有效数据
	int capacity;//记录顺序表的容量大小
}SeqList;

//初始化顺序表
void SLInit(SeqList* ps);
//释放空间
void SLDestroy(SeqList* ps);
//检查顺序表是否已满
void CheakCapacity(SeqList* ps);
//动态顺序表的尾插
void SLPushBack(SeqList* ps, SLDataType data);
//动态顺序表的尾删
void SLPopBack(SeqList* ps);
//动态顺序表的头插
void SLPushFront(SeqList* ps, SLDataType data);
//动态顺序表的头删
void SLPopFront(SeqList* ps);
//pos位置插入数据
void SLInsert(SeqList* ps, int pos, SLDataType data);
//pos位置删除数据
void SLErase(SeqList* ps, int pos);
//查找数据
int SLFind(SeqList* ps, SLDataType data, int begin);
//判断数组是否已满
bool IsFull(SeqList* ps);
//打印存储的数据
void SLPrint(SeqList* ps);

2.SeqList.c

#define _CRT_SECURE_NO_DEPRECATE 1
#include"SeqList.h"

void SLInit(SeqList* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

void SLDestroy(SeqList* ps)
{
	assert(ps);
	//若ps->a不为NULL,则释放空间
	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capacity = ps->size = 0;
	}
}

void CheakCapacity(SeqList* ps)
{
	assert(ps);
	if (ps->capacity == ps->size)
	{
		//若是第一次扩容,则大小为4;
		//若不是第一次,则每次扩容为之前容量的2倍
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("malloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
	//检查是否做到扩容
	printf("扩容成功,现在的容量为:%d\n", ps->capacity);
}

void SLPushBack(SeqList* ps, SLDataType data)
{
	assert(ps);
	CheakCapacity(ps);
	//插入数据
	ps->a[ps->size] = data;
	ps->size++;
}

void SLPopBack(SeqList* ps)
{
	assert(ps);
	ps->size--;
}

void SLPushFront(SeqList* ps, SLDataType data)
{
	assert(ps);
	CheakCapacity(ps);
	//挪动数据
	for (int i = ps->size - 1; i >= 0; i--)
	{
		ps->a[i + 1] = ps->a[i];
	}
	//插入数据
	ps->a[0] = data;
	ps->size++;
}

void SLPopFront(SeqList* ps)
{
	assert(ps);
	assert(ps->size > 0);
	//挪动数据
	for (int i = 0; i < ps->size - 1; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->size--;
}

void SLInsert(SeqList* ps, int pos, SLDataType data)
{
	assert(ps);
	CheakCapacity(ps);
	//挪动数据
	for (int i = ps->size - 1; i >= pos; i--)
	{
		ps->a[i + 1] = ps->a[i];
	}
	//插入数据
	ps->a[pos] = data;
	ps->size++;
}

void SLErase(SeqList* ps, int pos)
{
	assert(ps);
	assert(ps->size > 0);
	//挪动数据
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->size--;
}

int SLFind(SeqList* ps, SLDataType data, int begin)
{
	assert(ps);
	assert(begin < ps->size);
	for (int i = begin; i < ps->size; i++)
	{
		if (ps->a[i] == data)
		{
			return i;
		}
	}
	return -1;
}

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

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

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

相关文章

《mSystems》最新研究| 李香真老师亲临凌波微课LorMe云讲堂解读贡嘎山反硝化菌群组装模式和驱动因素

2021年11月2日&#xff0c;李香真团队在《mSystems》期刊正式发表了题为“Patterns and Drivers of nirK-Typeand nirS-Type Denitrifier Community Assembly along an Elevation Gradient”的研究论文。该研究以青藏高原东部边界最高山——贡嘎山作为研究平台&#xff0c;比较…

小程序与普通网页开发有什么区别?

小程序的开发同普通的网页开发相比有很大的相似性&#xff0c;小程序的主要开发语言也是 JavaScript&#xff0c;但是二者还是有些差别的。 普通网页开发可以使用各种浏览器提供的 DOM API&#xff0c;进行 DOM 操作&#xff0c;小程序的逻辑层和渲染层是分开的&#xff0c;逻…

[前端笔记] 1.WEB基本概念

[前端笔记] 1.WEB基本概念基本概念1.资源 resourse2.链接3.HTTP 协议4.网页的真实样子&#xff1a;HTML静态网页与动态网页1.静态网页2.动态网页现代网站架构1.网站架构当我们访问一个网站时&#xff0c;后台都会发生什么事情捏?www——万维网 www&#xff1a;World Wide Web …

JAVA中医舌诊接口使用示例代码,JAVA舌象图特征人工智能识别代码,JAVA实现舌象特征检测与识别

中医舌诊接口使用示例-Java示例项目 中医舌诊健康状态检测API 1&#xff0e;此文档适用于集成中国中医舌诊开放平台功能的用户。 2&#xff0e;此文档说明了与中国中医舌诊开放平台的数据交互方法&#xff08;restful接口&#xff09;&#xff0c;按需使用体质健康API、脏腑健…

盖子的c++小课堂——第十讲:字符

前言 我呢&#xff0c;早上刚发布第九讲&#xff0c;心里想的是马桶盖终于保住了&#xff0c;结果…… 生产队的刘同学&#xff1a;快&#xff0c;继续更&#xff0c;不然你下次吃泡面没调料包 其他粉丝&#xff1a;啊……啊对对对 啊&#xff01;&#xff01;&#xff01;&…

OPTEE TA介绍

前言 本文主要介绍OPTEE的TA(Trusted Applications)&#xff0c;翻译自官方文档&#xff1a;Trusted Applications — OP-TEE documentation documentation (optee.readthedocs.io) 有两种方法可以实现可信应用程序 &#xff08;TA&#xff09;&#xff1a;伪 TA 和用户模式 T…

Speedoffice(word)如何输入特殊符号?

Word文档有时需要输入人民币单位“元”&#xff08;&#xff09;的符号&#xff0c;那么怎么打出来了&#xff0c;以我最常用的Speedoffice为例和大家分享一下方法。步骤&#xff1a;1&#xff0c;首先运行office软件&#xff0c;新建一份word&#xff0c;找到“插入”菜单栏里…

再学C语言33:函数——地址运算符

C中最重要、最复杂的概念之一就是指针&#xff08;pointer&#xff09; 指针是用于存储地址的变量 例如&#xff1a;scanf()函数中使用地址作为参数 当需要改变调用函数中的某个值时&#xff0c;任何被调用的无返回值的C函数都需要使用地址参数完成该任务 一、地址运算符&a…

c语言重点

1、以下代码循环几次&#xff1f;&#xff08;面试题&#xff09; void test(){int i; // 局部变量 i 的值是不确定的for(;i<10;i){;} }答案是---------不确定&#xff0c;在 c 语言中局部变量 i 没有初始化&#xff0c;值是不确定的&#xff0c;所以 i 的值可能是…

GeoHash 的编码方法

对一组经纬度进行 GeoHash 编码时&#xff0c;我们要先对经度和纬度分别编码&#xff0c;然后再把经纬度各自的编码组合成一个最终编码。 对于一个地理位置信息来说&#xff0c;它的经度范围是[-180,180]。GeoHash 编码会把一个经度值编码成一个 N 位的二进制值&#xff0c;我…

MySQL进阶篇之存储引擎

01、存储引擎 1.1、MySQL体系结构 连接层 最上层是一些客户端和链接服务&#xff0c;主要完成一些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。 服务层 第二层架构主要完成大多数的核心服务功能&#xff0c;如SQL…

重要的字符(串)函数的使用及其实现

目录 字符串函数注意点 1、\0 2、适当使用const修饰 3、多使用assert断言 4、库函数不可能完全安全 1、求字符串长度strlen 1、计数实现 2、递归实现 3、指针相减求元素个数 2、长度不受限制的字符串函数 1、strcpy 2、strcat 3、strcmp 3、长度受限制的字符串函数…

ESP32设备驱动-MLX90614红外测温传感器驱动

MLX90614红外测温传感器驱动 1、MLX90614介绍 MLX90614 是一款用于非接触式温度测量的红外温度计。IR 敏感型热电堆检测器芯片和信号调节 ASIC 都集成在同一 TO-39 罐封装中。MLX90614 集成有低噪声放大器、17 位 ADC 和强大的 DSP 单元,因此温度计兼具高精度和高分辨率。 …

少儿Python每日一题(22):杨辉三角

原题解答 本次的题目如下所示: 杨辉三角形又称Pascal三角形,它的第i+1i+1行是的展开式的系数。 它的一个重要性质是:三角形中的每个数字等于它两肩上的数字相加。 下面给出了杨辉三角形的前4行: 1 1 1 1 2 1 1 3 3 1 给出n,输出它的前n行。 输入: 输入包含一个数n。 输出…

学习CSS3,使用双旋转实现福到了的迎春喜庆特效

春节快到了&#xff0c;因为疫情已经好久没有回老家了&#xff0c;今年终于可以回家过年了&#xff0c;我已经抑制不住自己激动的心情了。因此&#xff0c;我利用css3的旋转做了一个福到了的特效&#xff0c;而且是双旋转哦。 目录 1、实现思路 2、大红纸的渲染过程 3、错误…

【自学Python】Python运算符优先级

Python运算符优先级 Python运算符优先级教程 我们知道&#xff0c;在数学运算中&#xff0c;有 “先乘除后加减” 的运算规则&#xff0c;在我们程序语言中一样有运算符的优先级问题&#xff0c;来决定我们运算的顺序问题&#xff0c;这就是运算符的优先级。 即所谓运算符的…

C++STL之set与map的使用

本文目录前言一、关联式容器二、键值对(pair)三、树形结构的关联式容器1.set1.1set的介绍1.2set的使用1.2.1set的模板参数列表1.2.2set的构造(1)构造空的set(2)以数组的形式构造(3)用迭代器区间进行构造(4)拷贝构造1.2.3set的迭代器(1)iterator begin()(2)iterator end()(3)con…

一天变现100w就是这么简单!呆头鹅批量剪辑软件批量剪辑带货视频

呆头鹅剪辑是一款全自动的视频剪辑软件&#xff0c;包含剪辑、合成、去重、特效、配音、字幕、水印、后期处理、自动生成片头等功能&#xff0c;可以用于视频批量搬运&#xff0c;给视频增加特效&#xff0c;图片合成视频&#xff0c;视频混剪&#xff0c;自动加配音字幕&#…

Linux一看就会——make/Makefile

Linux一看就会——make/Makefile 背景 1.会不会写makefile&#xff0c;从一个侧面说明了一个人是否具备完成大型工程的能力。 2.一个工程中的源文件不计数&#xff0c;其按类型、功能、模块分别放在若干个目录中&#xff0c;makefile定义了一系列的 规则来指定&#xff0c;哪…

分享126个ASP源码,总有一款适合您

ASP源码 分享126个ASP源码&#xff0c;总有一款适合您 126个ASP源码DownLoad链接&#xff1a;https://pan.baidu.com/s/1wekzBbNE6JSFWtyLb_CdQg?pwdu1e0 提取码&#xff1a;u1e0 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下..…