DS线性表之顺序表

news2024/12/23 17:27:45

前言 

上一期我们介绍了什么是数据结构和算法,以及介绍了算法效率问题即什么是时空复杂度~和时空复杂度的计算方式以及常见的时空复杂度的例题详解,本期我们来介绍一下线性表中的顺序表~!

本期内容介绍

什么是线性表?

顺序表的概念以及分类

静态顺序表的实现

动态顺序表的实现

顺序表存在问题

目录

前言 

本期内容介绍

一、什么是线性表?

二、顺序表

顺序表的概念以及结构分类

三、静态顺序表的实现

静态顺序表的申明

静态顺序表的初始化

静态顺序表的销毁

静态顺序表的打印

静态顺序表的尾插

静态顺序表的尾删

静态顺序表的头插

静态顺序表的头删

静态顺序表的排序

静态顺序表在pos位置插入

静态顺序表在pos位置删除

静态顺序表查找指定元素x

静态顺序表中修改指定pos位置的元素

全部源码:

四、动态顺序表的实现

动态顺序表的申明

动态顺序表的初始化

动态顺序表的销毁

动态顺序表的打印

动态顺序表的尾插

动态顺序表的尾删

动态顺序表的头插

动态顺序表的头删

动态顺序表的排序

动态顺序表在pos位置插入

动态顺序表在pos位置删除

动态顺序表中查找指定元素x

动态顺序表中修改指定pos位置的元素

全部源码:

五、顺序表的问题


一、什么是线性表?

线性表(Linear List)是N个具有相同特性的数据元素的集合!线性表在逻辑上是线性结构,即是一条连续的线,但在其物理结构上是不一定连续的~!常见的线性表有:顺序表、链表、栈、队列、串等

为什么说是逻辑结构上是连续的,物理结构上是不一定连续的呢?我们在C语言自定义类型详解的那一期介绍过一个东西叫结构体的自引用,我们当时说过他就是链表,他的开辟在堆上要动态开辟(由操作系统随机分配)~!所以整个链表可能不是连续的!而在当时写通讯录的时候他那个实际上就是顺序表!我在来画个图解释一下上面这句话:

二、顺序表

顺序表的概念以及结构分类

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构!一般情况下使用数组存储,在数组上进行各种运算即增删查改(通过各个接口)!

顺序表可以分为:静态顺序表和动态顺序表

静态顺序表:存储数据的个数是固定的

动态顺序表:存储数据的个数不是固定的不够了可以扩容

OK,我们各写一个来look一look:

静态顺序表:

#define MAX 10
typedef int SLDataType;
typedef struct SeqList
{
	SLDataType data[MAX];
	int size;//顺序表的元素个数
}SeqList;

动态顺序表:

typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* data;
	int size;//顺序表的元素个数
	int capacity;//顺序表的容量
}SeqList;

这里你可能还有一些疑问,例如不就是在数组上增删查改吗?我直接在数组上进行操作不就行啦吗?OK,那你在进行操作的时候你如何知道你的数组中目前有几个元素?用什么判定???好像没有办法吧!你只能在定义一个变量来记录他当当中的元素个数,那这样为什么不直接用顺序表呢???对吧,一步到位,你只管操作其他不用担心~!另外还有就是:顺序表的存储一定是连续的,你只能挨着存而数组不一定你可以在不同位置存!!!!

三、静态顺序表的实现

OK,我们还是采用多文件编程:申明等都放在.h文件,实现在.c文件,测试在test.c中

一开始我们得先有一个顺序表吧:

静态顺序表的申明

#define MAX 15
typedef int SeqDataType;

typedef struct SeqList
{
	SeqDataType data[MAX];
	int size;
}SL;

静态顺序表的初始化

这里它的底层是个数组嘛!我们可以用个循环做,也可以直接用memset来初始化,我们一开始把数组中的每个元素都置0,size也置0;

//初始化
void SLInit(SL* ps)
{
	assert(ps);

	memset(ps->data, 0, sizeof(int) * MAX);
	ps->size = 0;
}

这里你可能会想为什么形参要用指针呢?这里还是那个问题:形参和实参的问题,形参的改变不影响实参!要在外部改变实参则要要传指针!

静态顺序表的销毁

有初始化必有销毁嘛~!那这个静态顺序表如何销毁呢?由于它的开辟在栈上,不能用free所以我们只需要把它的数组内容置0,size置0即可~!(size置0也可以,不要让他访问了),这里直接调用初始化即可:

//销毁
void SLDestory(SL* ps)
{
	assert(ps);
	SLInit(ps);
}

为了后续的直接的看结果我们在这里可以再来实现一个打印函数:

静态顺序表的打印

//打印
void SLPrint(const SL* ps)
{
	assert(ps);

	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->data[i]);
	}

	printf("\n");
}

静态顺序表的尾插

尾插就比较简单了,因为size是记录数组的有效元素的个数的,在数组中加一个元素size就会++一下,所以size是下一个元素的下标,所以我么只需要在size位置插入即可,但由于存储的大小是固定的,你在插入前得判断能不能插万一顺序表已经满了,你还插什么插!

//尾插
void SLPushBack(SL* ps, SeqDataType x)
{
	assert(ps);
	
	if (ps->size == MAX)
	{
		printf("顺序表已满无法插入!\n");
		exit(-1);
	}
	//assert(MAX == ps->size);

	ps->data[ps->size] = x;
	ps->size++;
}

当然这里的判断满的情况也有两种,"温柔的检查"和"暴力检查",其实我个人比较喜欢暴力检查的,满了的话直接结束程序,而且我们前面也介绍过assert断言失败也会告诉你在哪个文件的第几行出问题了~!当然要是讲究人性化的话,if是更好的~!这里都可以!

静态顺序表的尾删

尾删和尾插一样简单,由于你访问的时候是小于size的所以你只需要,让size--,此时在访问就访问不到最后一个元素了~!这也就相当于尾删了~!当然你也要在删除时判断,如果顺序表已经为空了,你还删除、、、这好像不合理吧~!所以当顺序表为空时就不要删了,那什么时候是顺序表为空呢?我们说过size是下一个元素的下标,当下一个元素的下标为0时,就说明当前元素的下表为-1,但下标不可能为-1所以就只能说明当前有效元素的个数为0,size没有++,所以当szie == 0时就说明顺序表一空不能删除~!

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

	if (ps->size  == 0)
	{
		printf("顺序表为空无法删除!\n");
		exit(-1);
	}

	ps->size--;
}

当然这里也可以用暴力检查~!

OK,我们还是养成好习惯,写两个测试两个:

尾插没有问题,再来看看尾删:

静态顺序表的头插

既然是插入那就要考虑是否已经是满的情况,如果是满的情况直接退出不然插入~!

//头插
void SLPushFront(SL* ps, SeqDataType x)
{
	assert(ps);

	if (ps->size == MAX)
	{
		printf("顺序表已满无法插入!\n");
		exit(-1);
	}

	int i = 0;
	for (i = ps->size; i > 0; i--)
	{
		ps->data[i] = ps->data[i - 1];
	}

	ps->data[i] = x;
	ps->size++;
}

静态顺序表的头删

删除的时候和以前一样一定要考虑顺序表已经为空的情况~!为空直接退出不删了~!

//头删
void SLPopFront(SL* ps)
{
	assert(ps);

	if (ps->size == 0)
	{
		printf("顺序表为空无法删除!\n");
		exit(-1);
	}
	//assert(ps->size);

	for (int i = 0; i < ps->size; i++)
	{
		ps->data[i] = ps->data[i+1];
	}

	ps->size--;
}

OK,还是来测试一下:

OK,头插头删是没有问题了~!

静态顺序表的排序

我们这里的排序采用qsort当然用其他的排序也可以~!

int cmp(const void* a, const void* b)
{
	return *(int*)a - *(int*)b;
}

//排序
void SLSort(SL* ps)
{
	assert(ps);
	assert(ps->size);

	qsort(ps->data, ps->size, sizeof(int), cmp);
}

静态顺序表在pos位置插入

插入必须判断是否已经满的情况,这里还有注意pos的合理性,因为pos是位置不是下标所以pos和数组下标差1(如下图),如果pos的位置小于1,顺序表的位置是从1开始的怎么可能小于1?不合理直接退出,pos大于或等于顺序表的最大长度,这也不合理吧~!顺序表的位置和数组的下标差1,你都大于等于最大下标了(假设满了无法插入了,即使不满顺序表也不让随意插入)

//在pos位置插入x
void SLInsert(SL* ps, int pos, SeqDataType x)
{
	assert(ps);

	if (pos < 1 || pos >= MAX || ps->size >= MAX)
	{
		printf("pos非法或顺序表已满!\n");
		exit(-1);
	}

	int i = 0;
	for (i = ps->size; i >= pos; i--)
	{
		ps->data[i] = ps->data[i-1];
	}
	ps->data[pos-1] = x;
	ps->size++;
}

静态顺序表在pos位置删除

删除必须判断是否已经是空的情况~!以及和上面一样pos不可能是小于1的,删除顺序表之外的东西直接退出,还有就是所删的位置是数组和法但不是顺序表的有效数据区这也不能删除~!

//删除pos位置
void SLErase(SL* ps, int pos)
{
	assert(ps);

	
	if (ps->size == 0 || pos < 1 || pos >= MAX || pos >= ps->size)
	{
		printf("pos非法或顺序表为空无法删除!\n");
		exit(-1);
	}

	for (int i = pos-1; i < ps->size; i++)
	{
		ps->data[i] = ps->data[i + 1];
	}

	ps->size--;
}

OK,还是来测试一把:

OK,排序以及在pos位置插入删除都正常~!我们下来在写几个:

静态顺序表查找指定元素x

这个接口就实现起来很简单了,只需要遍历一遍数组即可(在没有排序的情况下),如果是排了序的情况直接二分查找效率最高~!找到返回下标,找不到返回-1


//查找指定元素x
int SLFind(SL* ps, SeqDataType x)
{
	assert(ps);

	for (int i = 0; i < ps->size; i++)
	{
		if (x == ps->data[i])
			return i;
	}

	return -1;
}

静态顺序表中修改指定pos位置的元素

这里要考虑好边界即可,你不能超过最大存储范围也不能超过顺序表的有效数据范围,以及pos本身的合法性~!

//修改顺序表pos位置的元素
void SLModif(SL* ps, int pos, SeqDataType x)
{
	assert(ps);

	if (pos >= 1 && pos <= MAX && pos <= ps->size)
	{
		ps->data[pos - 1] = x;
	}
}

OK,测试一下:

全部源码:

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>

#define MAX 5
typedef int SeqDataType;

typedef struct SeqList
{
	SeqDataType data[MAX];
	int size;
}SL;

//初始化
void SLInit(SL* ps);

//销毁
void SLDestory(SL* ps);

//打印
void SLPrint(const SL* ps);

//尾插
void SLPushBack(SL* ps, SeqDataType x);

//尾删
void SLPopBack(SL* ps);

//头插
void SLPushFront(SL* ps, SeqDataType x);

//头删
void SLPopFront(SL* ps);

//排序
void SLSort(SL* ps);

//在pos位置插入x
void SLInsert(SL* ps, int pos, SeqDataType x);

//删除pos位置
void SLErase(SL* ps, int pos);

//查找指定元素x
int SLFind(SL* ps, SeqDataType x);

//修改顺序表pos位置的元素
void SLModif(SL* ps,int pos, SeqDataType x);
#include "SeqList.h"

//初始化
void SLInit(SL* ps)
{
	assert(ps);

	memset(ps->data, 0, sizeof(int) * MAX);
	ps->size = 0;
}

//销毁
void SLDestory(SL* ps)
{
	assert(ps);
	SLInit(ps);
}

//打印
void SLPrint(const SL* ps)
{
	assert(ps);

	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->data[i]);
	}

	printf("\n");
}


//尾插
void SLPushBack(SL* ps, SeqDataType x)
{
	assert(ps);
	
	if (ps->size == MAX)
	{
		printf("顺序表已满无法插入!\n");
		exit(-1);
	}
	//assert(MAX == ps->size);

	ps->data[ps->size] = x;
	ps->size++;
}


//尾删
void SLPopBack(SL* ps)
{
	assert(ps);

	if (ps->size  == 0)
	{
		printf("顺序表为空无法删除!\n");
		exit(-1);
	}

	ps->size--;
}


//头插
void SLPushFront(SL* ps, SeqDataType x)
{
	assert(ps);

	if (ps->size == MAX)
	{
		printf("顺序表已满无法插入!\n");
		exit(-1);
	}

	int i = 0;
	for (i = ps->size; i > 0; i--)
	{
		ps->data[i] = ps->data[i - 1];
	}

	ps->data[i] = x;
	ps->size++;
}

//头删
void SLPopFront(SL* ps)
{
	assert(ps);

	if (ps->size == 0)
	{
		printf("顺序表为空无法删除!\n");
		exit(-1);
	}
	//assert(ps->size);

	for (int i = 0; i < ps->size; i++)
	{
		ps->data[i] = ps->data[i+1];
	}

	ps->size--;
}


int cmp(const void* a, const void* b)
{
	return *(int*)a - *(int*)b;
}

//排序
void SLSort(SL* ps)
{
	assert(ps);
	assert(ps->size);

	qsort(ps->data, ps->size, sizeof(int), cmp);
}


//在pos位置插入x
void SLInsert(SL* ps, int pos, SeqDataType x)
{
	assert(ps);

	if (pos < 1 || pos >= MAX || ps->size >= MAX)
	{
		printf("pos非法或顺序表已满!\n");
		exit(-1);
	}

	int i = 0;
	for (i = ps->size; i >= pos; i--)
	{
		ps->data[i] = ps->data[i-1];
	}
	ps->data[pos-1] = x;
	ps->size++;
}

//删除pos位置
void SLErase(SL* ps, int pos)
{
	assert(ps);

	
	if (ps->size == 0 || pos < 1 || pos >= MAX || pos >= ps->size)
	{
		printf("pos非法或顺序表为空无法删除!\n");
		exit(-1);
	}

	for (int i = pos-1; i < ps->size; i++)
	{
		ps->data[i] = ps->data[i + 1];
	}

	ps->size--;
}


//查找指定元素x
int SLFind(SL* ps, SeqDataType x)
{
	assert(ps);

	for (int i = 0; i < ps->size; i++)
	{
		if (x == ps->data[i])
			return i;
	}

	return -1;
}


//修改顺序表pos位置的元素
void SLModif(SL* ps, int pos, SeqDataType x)
{
	assert(ps);

	if (pos >= 1 && pos <= MAX && pos <= ps->size)
	{
		ps->data[pos - 1] = x;
	}
}
#include "SeqList.h"

//测试尾插、尾删以及初始化、销毁、打印
void test1()
{
	SL s;
	SLInit(&s);

	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPrint(&s);
	SLPushBack(&s, 5);
	SLPrint(&s);

	SLPopBack(&s);
	SLPrint(&s);
	SLPopBack(&s);
	SLPrint(&s);
	SLPopBack(&s);
	SLPrint(&s);
	SLPopBack(&s);
	SLPrint(&s);
	SLPopBack(&s);
	SLPrint(&s);

	//检查是否满的时候插入是否有Bug
	SLPopBack(&s);
	SLPrint(&s);

	SLDestory(&s);
}

//测试头插、头删
void test2()
{
	SL s;
	SLInit(&s);

	SLPushFront(&s, 1);
	SLPushFront(&s, 2);
	SLPushFront(&s, 3);
	SLPushFront(&s, 5);
	SLPushFront(&s, 6);
	SLPrint(&s);

	//检查是在满的情况下是否有Bug
	//SLPushFront(&s, 7);
	//SLPrint(&s);

	SLPopFront(&s);
	SLPrint(&s);
	SLPopFront(&s);
	SLPrint(&s);
	SLPopFront(&s);
	SLPrint(&s);
	SLPopFront(&s);
	SLPrint(&s);
	SLPopFront(&s);
	SLPrint(&s);

	//检查是否空的时候删除是否有Bug
	SLPopFront(&s);
	SLPrint(&s);

	SLDestory(&s);
}

//测试排序、在pos位置插入、删除pos位置
void test3()
{
	SL s;
	SLInit(&s);

	SLPushFront(&s, 1);
	SLPushFront(&s, 2);
	SLPushFront(&s, 3);
	SLPushFront(&s, 5);
	SLPrint(&s);

	SLSort(&s);
	SLPrint(&s);

	SLInsert(&s, 3, 4);
	SLPrint(&s);

	SLErase(&s, 1);
	SLPrint(&s);
	SLErase(&s, 3);
	SLPrint(&s);

	SLDestory(&s);
}

//测试查找指定元素和修改pos位置的元素的值
void test4()
{
	SL s;
	SLInit(&s);

	SLPushFront(&s, 1);
	SLPushFront(&s, 2);
	SLPushFront(&s, 3);
	SLPushFront(&s, 5);
	SLPrint(&s);

	int ret = SLFind(&s, 4);
	if (ret != -1)
	{
		printf("找到了,下标为: %d ", ret);
	}
	else
	{
		printf("没找到\n");
	}

	SLModif(&s, 2, 20);
	SLPrint(&s);
	SLModif(&s, 1, 10);
	SLPrint(&s);

	SLModif(&s, 4, 40);
	SLPrint(&s);

	SLDestory(&s);
}


int main()
{
	test4();
	return 0;
}

静态顺序表的缺点就是它的存储个数是固定的,这个缺陷导致他的应用场景并不多,你一开始给他开多大的空间?这个是不是很为难呀?开太大了浪费了,开太小了又不够......所以它的应用场景不是很多!针对这种情况,我们进行优化:一开始给一点空间(或者不开)用的时候或不够了进行扩容(每次扩1.5倍或2倍),这样就有效的降低了浪费内存的情况~!OK,下面我们就来look一look顺序表的动态版本

四、动态顺序表的实现

动态顺序表的申明

typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* data;
	int size;//顺序表的元素个数
	int capacity;//顺序表的容量
}SeqList;

动态顺序表的初始化

//初始化
void SLInit(SL* ps)
{
	assert(ps);
	ps->data = (SLDataType*)malloc(sizeof(SLDataType) * 4);
	if (!ps->data)
	{
		perror("malloc failed");
		exit(-1);//异常终止
	}

	ps->size = 0;
	ps->capacity = 4;
}

这里一定要在malloc开辟后检查,否则一旦开辟失败就玩完了~!你都数组都没开出来你还在那里增删查改啥???所以,如果开辟失败直接异常退出~!

动态顺序表的销毁

//销毁
void SLDestory(SL* ps)
{
	assert(ps);

	free(ps->data);
	ps->data = NULL;
	ps->size = ps->capacity = 0;
}

要记得在最后把ps-data置空,否则就是野指针~!

动态顺序表的打印


//打印
void SLPrint(SL* ps)
{
	assert(ps);

	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->data[i]);
	}
	printf("\n");
}

动态顺序表的尾插

//尾插
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);

	//判断扩容
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->data, sizeof(SLDataType) * newcapacity);
		if (!tmp)
		{
			perror("realloc failed");
			exit(-1);
		}
		
		ps->data = tmp;
		ps->capacity = newcapacity;
	}

	ps->data[ps->size] = x;
	ps->size++;
}

由于是动态开辟的我们无需考虑满了插入不下的问题,我们只需要考虑是否扩容的问题,如果顺序表的有效数据个数==容量那就得扩容了~!

动态顺序表的尾删

//尾删
void SLPopBack(SL* ps)
{
	assert(ps);

	//判断是否为空
	if (ps->size == 0)
	{
		printf("顺序表为空无法删除!\n");
		exit(-1);
	}

	ps->size--;
}

尾删得考虑当前顺序表已经是空的情况~!这里你也可以用断言一旦为空直接终止,我这里稍微人性化了一点,提示了一下,异常退出了~!哈哈~

OK,老样子!写两个测两个:

OK,尾插尾删是没有问题了~!Let' s go on ~!

动态顺序表的头插

这里判断是否扩容会出现多次,而且代码相对较长,所以我么可以考虑封装成一个函数~!

//判断扩容
void JudExpan(SL* ps)
{
	assert(ps);

	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->data, sizeof(SLDataType) * newcapacity);
		if (!tmp)
		{
			perror("realloc failed");
			exit(-1);
		}

		ps->data = tmp;
		ps->capacity = newcapacity;
	}
}
//头插
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);

	JudExpan(ps);
	for (int i = ps->size; i > 0; i--)
	{
		ps->data[i] = ps->data[i - 1];
	}
	ps->data[0] = x;
	ps->size++;
}

动态顺序表的头删

头删判断为空时的判断,当然也可以封装成一个函数,但这里就两句所以我没有封装成函数~!

//头删
void SLPopFront(SL* ps)
{
	assert(ps);

	//判断是否为空
	if (ps->size == 0)
	{
		printf("顺序表为空无法删除!\n");
		exit(-1);
	}

	for (int i = 0; i < ps->size; i++)
	{
		ps->data[i] = ps->data[i + 1];
	}
	ps->size--;
}

OK,测试一下:

OK,没有问题~!我们继续!

动态顺序表的排序

我们还是采用C语言库里面提供的qsort进行排序

//比较函数
int cmp(const void* a, const void* b)
{
	return *(int*)a - *(int*)b;
}

//排序
void SLSort(SL* ps)
{
	assert(ps);
	qsort(ps->data, ps->size, sizeof(SLDataType), cmp);
}

这个我们就把感刚的头插修改一下测试一下即可:

OK,没问题!

动态顺序表在pos位置插入

//在pos位置插入
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);

	JudExpan(ps);
	//判断pos的合法性
	if (pos > ps->capacity || pos < 1)
	{
		printf("pos非法!");
		exit(-1);
	}

	for (int i = ps->size; i >= pos; i--)
	{
		ps->data[i] = ps->data[i - 1];
	}
	ps->data[pos - 1] = x;
	ps->size++;
}

记得判断pos的合法性~!!!

动态顺序表在pos位置删除

//删除pos位置的元素
void SLErase(SL* ps, int pos)
{
	assert(ps);

	//判断是否为空
	if (ps->size == 0)
	{
		printf("顺序表为空无法删除!\n");
		exit(-1);
	}
	//判断pos的合法性
	if (pos < 1 || pos > ps->size)
	{
		printf("pos非法!");
		exit(-1);
	}

	for (int i = pos - 1; i < ps->size; i++)
	{
		ps->data[i] = ps->data[i + 1];
	}
	ps->size--;
}

这里还是注意pos的合法性!!!

OK,测试一下:

OK,没有问题~!

动态顺序表中查找指定元素x

这个还是和上面的一样找到了返回下标,否则返回-1(如果是已经有序的就可以采用二分查找)

//查找指定元素x
int SLFind(SL* ps, SLDataType x)
{
	assert(ps);

	for (int i = 0; i < ps->size; i++)
	{
		if (x == ps->data[i])
		{
			return i;
		}
	}

	return -1;
}

动态顺序表中修改指定pos位置的元素

//修改pos位置的值
void SLModif(SL* ps, int pos, SLDataType x)
{
	assert(ps);

	if (pos >=1 && pos <= ps->size)
	{
		ps->data[pos - 1] = x;
	}
	else
	{
		printf("pos非法!\n");
		exit(-1);
	}
}

这里也是注意判断pos的合法性~!!!否则会出问题!!!

OK,测试一下:

OK,没有问题~!

全部源码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>


typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* data;
	int size;//顺序表的元素个数
	int capacity;//顺序表的容量
}SL;

//初始化
void SLInit(SL* ps);

//销毁
void SLDestory(SL* ps);

//打印
void SLPrint(SL* ps);

//尾插
void SLPushBack(SL* ps, SLDataType x);

//尾删
void SLPopBack(SL* ps);

//头插
void SLPushFront(SL* ps, SLDataType x);

//头删
void SLPopFront(SL* ps);

//排序
void SLSort(SL* ps);

//在pos位置插入
void SLInsert(SL* ps, int pos, SLDataType x);

//删除pos位置的元素
void SLErase(SL* ps, int pos);

//查找指定元素x
int SLFind(SL* ps, SLDataType x);

//修改pos位置的值
void SLModif(SL* ps, int pos, SLDataType x);
#include "SeqList.h"

//初始化
void SLInit(SL* ps)
{
	assert(ps);
	ps->data = (SLDataType*)malloc(sizeof(SLDataType) * 4);
	if (!ps->data)
	{
		perror("malloc failed");
		exit(-1);//异常终止
	}

	ps->size = 0;
	ps->capacity = 4;
}

//销毁
void SLDestory(SL* ps)
{
	assert(ps);

	free(ps->data);
	ps->data = NULL;
	ps->size = ps->capacity = 0;
}

//打印
void SLPrint(SL* ps)
{
	assert(ps);

	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->data[i]);
	}
	printf("\n");
}

//判断扩容
void JudExpan(SL* ps)
{
	assert(ps);

	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->data, sizeof(SLDataType) * newcapacity);
		if (!tmp)
		{
			perror("realloc failed");
			exit(-1);
		}

		ps->data = tmp;
		ps->capacity = newcapacity;
	}
}

//尾插
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);

	JudExpan(ps);
	ps->data[ps->size] = x;
	ps->size++;
}

//尾删
void SLPopBack(SL* ps)
{
	assert(ps);

	//判断是否为空
	if (ps->size == 0)
	{
		printf("顺序表为空无法删除!\n");
		exit(-1);
	}

	ps->size--;
}

//头插
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);

	JudExpan(ps);
	for (int i = ps->size; i > 0; i--)
	{
		ps->data[i] = ps->data[i - 1];
	}
	ps->data[0] = x;
	ps->size++;
}

//头删
void SLPopFront(SL* ps)
{
	assert(ps);

	//判断是否为空
	if (ps->size == 0)
	{
		printf("顺序表为空无法删除!\n");
		exit(-1);
	}

	for (int i = 0; i < ps->size; i++)
	{
		ps->data[i] = ps->data[i + 1];
	}
	ps->size--;
}

//比较函数
int cmp(const void* a, const void* b)
{
	return *(int*)a - *(int*)b;
}

//排序
void SLSort(SL* ps)
{
	assert(ps);
	qsort(ps->data, ps->size, sizeof(SLDataType), cmp);
}

//在pos位置插入
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);

	JudExpan(ps);
	//判断pos的合法性
	if (pos > ps->capacity || pos < 1)
	{
		printf("pos非法!");
		exit(-1);
	}

	for (int i = ps->size; i >= pos; i--)
	{
		ps->data[i] = ps->data[i - 1];
	}
	ps->data[pos - 1] = x;
	ps->size++;
}

//删除pos位置的元素
void SLErase(SL* ps, int pos)
{
	assert(ps);

	//判断是否为空
	if (ps->size == 0)
	{
		printf("顺序表为空无法删除!\n");
		exit(-1);
	}
	//判断pos的合法性
	if (pos < 1 || pos > ps->size)
	{
		printf("pos非法!");
		exit(-1);
	}

	for (int i = pos - 1; i < ps->size; i++)
	{
		ps->data[i] = ps->data[i + 1];
	}
	ps->size--;
}


//查找指定元素x
int SLFind(SL* ps, SLDataType x)
{
	assert(ps);

	for (int i = 0; i < ps->size; i++)
	{
		if (x == ps->data[i])
		{
			return i;
		}
	}

	return -1;
}

//修改pos位置的值
void SLModif(SL* ps, int pos, SLDataType x)
{
	assert(ps);

	if (pos >=1 && pos <= ps->size)
	{
		ps->data[pos - 1] = x;
	}
	else
	{
		printf("pos非法!\n");
		exit(-1);
	}
}
#include "SeqList.h"

//测试尾插、尾删
void test()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPrint(&s);
	//检查扩容
	SLPushBack(&s, 5);
	SLPrint(&s);

	SLPopBack(&s);
	SLPrint(&s);
	SLPopBack(&s);
	SLPrint(&s);
	SLPopBack(&s);
	SLPrint(&s);
	SLPopBack(&s);
	SLPrint(&s);
	SLPopBack(&s);
	SLPrint(&s);

	//检查是否已经为空是的删除是否有Bug
	SLPopBack(&s);
	SLPrint(&s);

	SLDestory(&s);
}

//测试头插、头删
void test2()
{
	SL s;
	SLInit(&s);

	SLPushFront(&s, -1);
	SLPushFront(&s, 3);
	SLPushFront(&s, 0);
	SLPushFront(&s, 1);
	SLPrint(&s);
	//判断扩容
	SLPushFront(&s, 10);
	SLPrint(&s);

	SLSort(&s);
	SLPrint(&s);

	SLPopFront(&s);
	SLPrint(&s);
	SLPopFront(&s);
	SLPrint(&s);
	SLPopFront(&s);
	SLPrint(&s);
	SLPopFront(&s);
	SLPrint(&s);
	SLPopFront(&s);
	SLPrint(&s);
	//检查为空时删除是否有Bug
	SLPopFront(&s);
	SLPrint(&s);

	SLDestory(&s);
}

//测试在pos位置插入、删除
void test3()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPrint(&s);

	SLInsert(&s, 4, 10);
	SLPrint(&s);
	SLInsert(&s, 4, -1);
	SLPrint(&s);
	//SLInsert(&s, 20, 7);
	//SLPrint(&s);

	SLErase(&s, 1);
	SLPrint(&s);
	SLErase(&s, 5);
	SLPrint(&s);
	//SLErase(&s, -0);
	//SLPrint(&s);

	SLDestory(&s);
}

//测试查找指定元素和修改pos位置的值
void test4()
{
	SL s;
	SLInit(&s);
	SLPushBack(&s, 1);
	SLPushBack(&s, 2);
	SLPushBack(&s, 3);
	SLPushBack(&s, 4);
	SLPrint(&s);

	int ret = SLFind(&s, -1);
	if (ret != -1)
	{
		printf("找到了,下标是: %d  顺序表位置是: %d ", ret, ret + 1);
	}
	else
	{
		printf("没找到!\n");
	}

	SLModif(&s, 1, -10);
	SLPrint(&s);
	SLModif(&s, -1, -10);
	SLPrint(&s);
}

int main()
{
	test4();
	return 0;
}

OK~!两个版本的顺序表就完成了~!

五、顺序表的问题

1、在顺序表中间或头部进行插入或删除时的时间复杂度为O(N)

2、扩容时如果到数据多的时候会异地扩容,会增加空间复杂度~!

3、无论静态还是动态顺序表其实都会都空间浪费的现象,这是避免不了的~!

关于这些问题如何解决,请期待下一期:链表~!!!

OK,本期内容就分享到这里~!好兄弟,我们下期再见!!!

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

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

相关文章

Llama2-Chinese项目:2.2-大语言模型词表扩充

因为原生LLaMA对中文的支持很弱&#xff0c;一个中文汉子往往被切分成多个token&#xff0c;因此需要对其进行中文词表扩展。思路通常是在中文语料库上训练一个中文tokenizer模型&#xff0c;然后将中文tokenizer与LLaMA原生tokenizer进行合并&#xff0c;最终得到一个扩展后的…

Linux网络编程:网络编程套接字

目录 一. 端口号的概念 二. 对于UDP和TCP协议的认识 三. 网络字节序 3.1 字节序的概念 3.2 网络通信中的字节序 3.3 本地地址格式和网络地址格式 四. socket编程的常用函数 4.1 sockaddr结构体 4.2 socket编程常见函数的功能和使用方法 五. UDP协议实现网络通信 5.…

c语言练习题60:模拟实现atoi

模拟实现atoi 代码&#xff1a; #include<assert.h> #include<stdio.h> #include<ctype.h> #include<limits.h> int my_atoi(const char* str) {assert(str ! NULL);if (*str \0){return 0;}//isspace 判断一个字符是不是空格while (isspace(*str)){…

视频监控系统/安防监控/视频AI智能分析:小动物识别算法场景汇总

随着人们对生态环境的关注日益提升&#xff0c;大家对动物保护意识也逐渐增强。旭帆科技智能分析网关小动物识别算法应运而生。除了对保护动物的识别以外&#xff0c;旭帆科技AI智能分析网关还可以识别常见的老鼠等动物&#xff0c;助力明厨亮灶监管&#xff0c;保卫食品安全。…

一键集成prometheus监控微服务接口平均响应时长

一、效果展示 二、环境准备 prometheus + grafana环境 参考博文:https://blog.csdn.net/luckywuxn/article/details/129475991 三、导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter

JAVAEE:采用HTML和JavaScript实现几个基本的页面

1.实现效果&#xff1a; <html><title>学生信息确认</title><body><Form>用户名:<input typetext id"usename"><br>密码: <input typepassword id"userpwd"><br>性别:<input typeradio id"…

华为云云耀云服务器L实例评测|手把手教你搭建MySQL数据库

1. 前言 本文将为读者介绍华为云的云耀云服务器L实例&#xff0c;并提供一份详细的手把手教程&#xff0c;帮助您快速搭建MySQL数据库。 MySQL是一款常用的关系型数据库管理系统&#xff0c;搭建与配置它对于许多业务应用和网站来说都是必需的。本文将以华为云【云耀云服务器L实…

模电2023.9.16

1、放大电路模型 根据功能基本可分为四大类&#xff1a; 电压放大、电流放大、互阻放大和互导放大。 注&#xff1a;互阻放大&#xff1a;电流信号转换为电压信号 互导放大&#xff1a;电压信号转换为电流信号 菱形的电源受控源&#xff0c;圆形的为非受控源 如何区分? 看输入…

【入门篇】ClickHouse 的安装与配置

文章目录 0. 前言ClickHouse的安装1. 添加 ClickHouse 的仓库2. 安装 ClickHouse3. 启动 ClickHouse 服务器4. 使用 ClickHouse 客户端 ClickHouse的配置 1. 详细安装教程1.1. 系统要求1.1. 可用安装包 {#install-from-deb-packages}1.1.1. DEB安装包1.1.1. RPM安装包 {#from-r…

C++中的导入include,头文件,extern,main函数入口及相关编译流程

结论&#xff1a; 1&#xff1a;#include就是复制粘贴 2&#xff1a;C编译的时候&#xff0c;在链接之前&#xff0c;各个文件之间实际上没有联系&#xff0c;只有到了链接的阶段&#xff0c;系统才会到各个cpp文件中去找需要的文件&#xff1b; 一&#xff1a;include的作用…

vue2必备知识点

1、生命周期钩子是如何实现的? 生命周期描述beforeCreatevue实例初始化后&#xff0c;数据观测&#xff08;data observer&#xff09;和事件配置之前。data、computed、watch、methods都无法访问。createdvue实例创建完成后立即调用 &#xff0c;可访问 data、computed、wat…

前端JavaScript中MutationObserver:监测DOM变化的强大工具

&#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 引言 1. MutationObserver简介 2. MutationObserver的属性 3. MutationObserver的应用场景 3.1 动态内容加载 …

动手学深度学习_个人笔记01_李沐(更新中......)

序言 神经网络——本书中关注的DL模型的前身&#xff0c;被认为是过时的工具。 深度学习在近几年推动了CV、NLP和ASR等领域的快速发展。 关于本书 让DL平易近人&#xff0c;教会概念、背景和代码。 一种结合了代码、数学和HTML的媒介 测试深度学习&#xff08;DL&#xf…

FL Studio21.1.1.3750中文破解百度网盘下载地址含Crack补丁

FL Studio21.1.1.3750中文破解版是最优秀、最繁荣的数字音频工作站 (DAW) 之一&#xff0c;日新月异。它是一款录音机和编辑器&#xff0c;可让您不惜一切代价制作精美的音乐作品并保存精彩的活动画廊。 为方便用户&#xff0c;FL Studio 21提供三种不同的版本——Fruity 版、…

TrOCR——基于transformer模型的OCR手写文字识别

前期我们使用大量的篇幅介绍了手写数字识别与手写文字识别,当然那里主要使用的是CNN卷积神经网络,利用CNN卷积神经网络来训练文字识别的模型。 这里一旦提到OCR相关的技术,肯定第一个想到的便是CNN卷积神经网络,毕竟CNN卷积神经网络在计算机视觉任务上起到了至关重要的作用…

打造“共富果园” 广东乳源推动茶油全产业链高质量发展

新华网广州9月13日电&#xff08;李庆招&#xff09;金秋九月&#xff0c;瓜果飘香&#xff0c;油茶也将迎来采摘期。13日&#xff0c;一场以“中国健康油 茶油新势力”为主题的乳源茶油12221市场体系之产业大会暨供销对接会在广州举行。来自茶油行业的专家、企业家齐聚一堂&am…

Python模块之time中时间戳、时间字符与时间元组之间的相互转换

时间的三种形式 时间戳&#xff0c;根据1970年1月1日00:00:00开始按秒计算的偏移量。 1694868399 时间格式字符串&#xff0c;字符串形式的时间。 2023-09-16 20:46:39 时间元组&#xff08;struct_time&#xff09;&#xff0c;包含9个元素。 time.struct_time(tm_year2023, …

2023 IDC 中国未来企业大奖优秀奖公布,神策数据助力中信建投获“未来运营领军者”优秀奖称号...

今日&#xff0c;全球领先的 IT 市场研究和咨询公司 IDC 正式公布 2023 IDC 中国未来企业大奖优秀奖名单&#xff0c;神策数据的合作客户中信建投证券股份有限公司&#xff08;简称“中信建投”&#xff09;荣获“未来运营领军者”优秀奖称号。 该奖项是 ICT 领域最具权威的奖项…

直线导轨滑块的固定方式

直线导轨滑块是要安装到导轨上的&#xff0c;利用压力使得滑块固定到导轨上&#xff0c;并调整间隙精度&#xff0c;当机械中有振动或冲击力浸染时&#xff0c;滑块和滑轨很有可能发生松动&#xff0c;从而偏离原来的固定地位&#xff0c;影响运行精度与操作寿命&#xff0c;甚…

RADIUS协议基础原理

RADIUS简介 Radius概述Radius架构(c/s模式)Radius特点Radius报文Radius认证报文Radius计费报文Radius授权报文 Radius工作原理 Radius概述 RADIUS&#xff08;Remote Authentication Dial-In User Server&#xff0c;远程认证拨号用户服务&#xff09;是一种分布式的、C/S架构…