数据结构: 线性表(顺序表实现)

文章目录

  • 1. 线性表的定义
  • 2. 线性表的顺序表示:顺序表
    • 2.1 概念及结构
    • 2.2 接口实现
      • 2.2.1 顺序表初始化 (SeqListInit)
      • 2.2.2 顺序表尾插 (SeqListPushBack)
      • 2.2.3 顺序表打印 (SeqListPrint)
      • 2.2.6 顺序表销毁 (SeqListDestroy)
      • 2.2.5 顺序表尾删 (SeqListPopBack)
      • 2.2.6 顺序表头插 (SeqListPushFront)
      • 2.2.7 顺序表头删 (SeqListPopFront)
      • 2.2.8 顺序表查找元素 (SeqListFind)
      • 2.2.9 顺序表指定位置插入 (SeqListInsert)
      • 2.2.10 顺序表指定位置删除 (SeqListErase)
    • 2.3 完整代码

1. 线性表的定义

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

在这里插入图片描述

在这里插入图片描述

2. 线性表的顺序表示:顺序表

2.1 概念及结构

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

顺序表一般可以分为:

  1. 静态顺序表: 使用定长数组存储元素
//顺序表的静态存储
#define N 7
typedef int SLDatatype;
typedef struct SeqList
{
    SLDatatype array[N];    //定长数组
    size_t size;            //有效元素的个数
}SeqList;

在这里插入图片描述

静态顺序表的特点: 容量创建时就已经确定
静态顺序表的缺点: 不能确定是具体给多少容量,给过大过小都不合适

  1. 动态顺序表: 使用动态开辟的数组存储
typedef int SLDataType;
typedef struct SeqList
{
    SLDataType *array;      //指向动态开辟的数组
    size_t size;            //有效元素的个数
    size_t capacity;        //容量空间的大小
}SeqList;

在这里插入图片描述

2.2 接口实现

静态顺序表只适用于确定直到需要存多少数据的场景.静态顺序表的定长数组导致了N定大了.空间开多了浪费,空间开小了不够用.

所以现实中基本上是使用动态顺序表,根据需要的动态的分配空间大小.

typedef int SLDataType;

//顺序表的动态存储
typedef struct SeqList
{
	SLDataType* a;      //指向动态开辟的数组
	size_t size;        //有效数据的个数
	size_t capacity;    //容量空间的大小
}SeqList;

//基本增删查改接口

//顺序表初始化
void SeqListInit(SeqList* ps);
//顺序表销毁
void SeqListDestroy(SeqList* ps);
//顺序表打印
void SeqListPrint(const SeqList* ps);
//顺序表尾插
void SeqListPushBack(SeqList* ps, SLDataType x);
//顺序表头插
void SeqListPushFront(SeqList* ps, SLDataType x);
//顺序表尾删
void SeqListPopBack(SeqList* ps);
//顺序表头删
void SeqListPopFront(SeqList* ps);	
//顺序表查找
int SeqListFind(const SeqList* ps, SLDataType x);
//顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDataType x);
//顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos);

2.2.1 顺序表初始化 (SeqListInit)

  • Seqlist.h
#pragma once

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

typedef int SLDataType;

//顺序表的动态存储
typedef struct SeqList
{
	SLDataType* a;      //指向动态开辟的数组
	size_t size;        //有效数据的个数
	size_t capacity;    //容量空间的大小
}SeqList;

//顺序表初始化
void SeqListInit(SeqList* ps);
  1. 使用#pragma once确保头文件不会倍重复包含
  1. 使用typedef将类型命名为SLDatatype,确保后续修改数组元素类型
  1. 使用typedef将动态顺序表结构体类型命名为SeqList,方便后续使用结构体类型
  1. 最后定义初始化接口函数void SeqListInit(SeqList* ps).这里使用了传址调用,初始化对结构体内容进行修改,要作用到主程序实参上面,就需要传递地址.
  • Seqlist.c
//顺序表初始化
void SeqListInit(SeqList* ps)
{
	assert(ps);
	ps->a = (SLDataType*)malloc(4*sizeof(SLDataType));
	if (ps->a == NULL)
	{
		perror("SeqListInit");
		exit(-1);
	}

	ps->size = 0;
	ps->capacity = 4;
}
  1. assert(ps)确保传入的地址是有效地址
  1. ps->a = (SLDataType*)malloc(4*sizeof(SLDataType));动态开辟了能存放 4 个数据的数组空间
  1. ps->size = 0;ps->capacity = 4;对有效个数和容量空间进行初始化.
  • Test.c
#include "SeqList.h"

voit SeqListTest1()
{
	SeqList sl;
	SeqListInit(&sl);
}

int main(void)
{
	SeqList sl;

	return 0;
}
  1. 将测试代码放入函数中,可以方便进行选择性调试信息
  1. 程序运行结束后,通过调试得到以下信息:
    在这里插入图片描述

sl的成员都通过调用初始化程序创建成功了

2.2.2 顺序表尾插 (SeqListPushBack)

顺序表尾插,就是在数组末尾添加元素,可能会出现如下情况:
在这里插入图片描述

在这里插入图片描述

  • SeqList.h
void SeqListPushBack(SeqList* ps, SLDataType x);
  1. 要修改顺序表的内容,同样是传址调用.
  1. x为要插入的数据,类型为SLDataType
  • SeqList.c
//顺序表尾插
void SeqListPushBack(SeqList* ps, SLDataType x)
{
	assert(ps);
	//若容量不够且扩容失败, 直接结束程序
	if (SeqListCheckCapacity(ps) == 0)
	{
		exit(-1);
	}

	ps->a[ps->size] = x;
	ps->size++;
}	
  1. 首先先判断容量是否足够,因为其他插入方式也需要使用到这个功能,所以我将其封装成了一个函数SeqListCheckCapacity,具体如下:
//检查容量是否满,若满则扩容,失败返回0,成功或容量未满返回1
static int SeqListCheckCapacity(SeqList* ps)
{
	//如果容量已满,则进行尝试扩容
	if (ps->size == ps->capacity)
	{
		//使用realloc将原空间扩大为 2 倍
		SLDataType* tmp = (SLDataType*)realloc(ps->a, 2 * ps->capacity * sizeof(SLDataType));
		//如果开辟失败,则提示后返回 0
		if (tmp == NULL)
		{
			printf("realloc 扩容失败.\n");
			return 0;
		}
		
		//如果开辟成功,则更新容量
		ps->a = tmp;
		ps->capacity *= 2;
	}
	return 1;
}

若容量已满,则使用realloc()函数进行扩容,每次将空间扩大为2倍.如果扩容失败,直接返回 0;如果扩容成功,则返回 1.

同时开辟成功的话,更新容量.并且和空间容量足够的情况下一样,返回 1

  1. 确保容量足够的情况下,将要插入的值直接赋值到 size 位置上,同时 size++
  • “Test.c”
void SeqListTest1()
{
	SeqList sl;
	SeqListInit(&sl);

	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
}
  1. 第一次插入,容量足够,if判断容量足够,直接插入:
    在这里插入图片描述
  1. 因为初始化的时候赋予了顺序表容量为4,第2,3,4次插入都不需要扩容,直接插入
  1. 第5次插入,容量不够,扩充完容量后才插入数据:
    在这里插入图片描述
  1. 同时,这个时候观察顺序表成员,容量已经扩容了两倍,size 已经变成了 5
    在这里插入图片描述

2.2.3 顺序表打印 (SeqListPrint)

顺序表打印,之间遍历数组元素并且打印出来就行了,注意这里只是适用于int类型的数据

  • SeqList.h
void SeqListPrint(const SeqList* ps);

使用const修饰形参常变量,保护ps所指向的数据,因为这里只需要打印操作,并不需要进行修改.

  • SeqList.c
//顺序表打印
void SeqListPrint(const SeqList* ps)
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

遍历整个顺序表,简单使用for循环就可以了

  • Test.c
void SeqListTest1()
{
	SeqList sl;
	SeqListInit(&sl);

	SeqListPrint(&sl);
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	SeqListPrint(&sl);
}

程序运行结果打印正确:
在这里插入图片描述

2.2.6 顺序表销毁 (SeqListDestroy)

动态顺序表是通过动态申请内存空间来存储数据的,在不需要该顺序表,需要将这块空间归还过去,否则会出现内存泄漏

  • SeqList.h
void SeqListDestroy(SeqList* ps);
  • SeqList.c
//顺序表销毁
void SeqListDestroy(SeqList* ps)
{
	assert(ps);
	free(ps->a);		//将空间还给操作系统
	ps->a = NULL;		//置空防止野指针
	ps->size = 0;		//空间置零
	ps->capacity = 0;
}

由于只有数组空间是动态申请的,只需要free(ps->a),归还后将有效个数和容量置为 0

  • Test.c
void SeqListTest1()
{
	SeqList sl;
	SeqListInit(&sl);

	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);

	SeqListDestroy(&sl);
}

2.2.5 顺序表尾删 (SeqListPopBack)

顺序表尾删,不需要移动元素,直接将 size–.

并不需要将这块空间真正归还给操作系统:使用free()函数

在这里插入图片描述

  • SeqList.h
void SeqListPopBack(SeqList* ps, SLDataType x);
  • SeqList.c
//顺序表尾删
void SeqListPopBack(SeqList* ps)
{
	assert(ps);
	//确保顺序表有数据可删
	assert(ps->size > 0);

	//进行删除操作
	ps->size--;
}

在确保顺序表有数据可删的情况下, 直接size减1就行

  • test.c
	SeqList sl;
	SeqListInit(&sl);

	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	SeqListPrint(&sl);

	SeqListPopBack(&sl);
	SeqListPrint(&sl);
	SeqListPopBack(&sl);
	SeqListPopBack(&sl);
	SeqListPrint(&sl);
	
	SeqListDestroy(&sl);

测试运行结果如下:
在这里插入图片描述

通过调试可以看出, 顺序表删除元素仅仅是将 size 减一, 而不需要修改容量, 甚至试图 free 删去元素所在的空间(释放非动态申请空间时指向的空间,这是会造成内存泄露的!):
在这里插入图片描述

2.2.6 顺序表头插 (SeqListPushFront)

顺序表头插, 需要先将下标 0 - ps->size - 1 的元素全都向后移动一个元素的位置, 再将需要插入的数据插入到顺序表头部位置
在这里插入图片描述

  • SeqList.h
void SeqListPushFront(SeqList* ps, SLDataType x);
  • SeqList.c
//顺序表头插
void SeqListPushFront(SeqList* ps, SLDataType x)
{
	assert(ps);	
	//若容量不够且扩容失败, 直接结束程序
	if (SeqListCheckCapacity(ps) == 0)
	{
		exit(-1);
	}

	//从后开始将每个元素向后移动一个位置,直到下标到0
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	
	//第一个位置放入对应元素
	ps->a[0] = x;
	ps->size++;
}
  1. 首先进行容量判断, 使用SeqListCheckCapacity()函数进行判断, 并扩容
  1. 容量足够时, 一次向后移动元素, 需要从后往前移动才能不覆盖已有的元素. 首先定义一个 end 变量,记录最后一个元素的下标.
  1. 随后不断将元素后移, 直至 end指向了第一个元素.
  1. 所有的元素移动完毕后, 顺序表第一个空间是空出来的, 这时候将要插入的数据头插到第一个空间内.并且更新有效元素个数.
  • Test.c
	SeqList sl;
	SeqListInit(&sl);

	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	SeqListPrint(&sl);

	SeqListPushFront(&sl, -1);
	SeqListPushFront(&sl, -2);
	SeqListPushFront(&sl, -3);
	SeqListPushFront(&sl, -4);
	SeqListPushFront(&sl, -5);
	SeqListPrint(&sl);

	SeqListDestroy(&sl);

在头插元素之前, 顺序表中已经有了 5 个元素, 随后进行头插, 测试结果如下:
在这里插入图片描述

2.2.7 顺序表头删 (SeqListPopFront)

顺序表头删和顺序表头插相反, 将第二个元素至最后一个元素全部前移一个元素空间就可以了.

在这里插入图片描述

  • SeqList.h
void SeqListPopFront(SeqList* ps);
  • SeqList.c
//顺序表头删
void SeqListPopFront(SeqList* ps)
{
	assert(ps);
	//确保顺序表有数据可删
	assert(ps->size > 0);
	
	//从前向后将每个元素向前移动一个位置,直到下标为size-1
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}
	
	ps->size--;
}
  1. 和顺序表尾删一样, 也需要断言表中有数据存在, 否则直接结束程序.
  1. 直接定义一个 begin 第二个元素, 将该元素向前移动一个元素空间后, begin++,直到第三个元素, 直至将所有的元素(除了要删除的第一个元素)都前移一个元素空间.
  1. 最后 size 减一表示删除成功
  • Test.c
	SeqList sl;
	SeqListInit(&sl);

	SeqListPushFront(&sl, -1);
	SeqListPushFront(&sl, -2);
	SeqListPushFront(&sl, -3);
	SeqListPushFront(&sl, -4);
	SeqListPushFront(&sl, -5);
	SeqListPrint(&sl);

	SeqListPopFront(&sl);
	SeqListPopFront(&sl);
	SeqListPrint(&sl);
	SeqListPopFront(&sl);
	SeqListPopFront(&sl);
	SeqListPrint(&sl);

	SeqListDestroy(&sl);

测试结果如下:
在这里插入图片描述

2.2.8 顺序表查找元素 (SeqListFind)

思路就是遍历顺序表, 并且判断是否有符合要求的元素, 如果有返回该元素下标, 如果没有返回 -1

  • SeqList.h
int SeqListFind(const SeqList* ps, SLDataType x);
  • SeqList.c
//顺序表查找
int SeqListFind(const SeqList* ps, SLDataType x)
{	
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
	}
	
	return -1;
}
  • Test.c
	SeqList sl;
	SeqListInit(&sl);

	SeqListPushBack(&sl, 0);
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	SeqListPushBack(&sl, 6);
	SeqListPushBack(&sl, 7);
	SeqListPushBack(&sl, 8);
	SeqListPushBack(&sl, 9);
	SeqListPushBack(&sl, 10);
	
	int pos = 0;
	pos = SeqListFind(&sl, 2);
	printf("%d\n", pos);
	pos = SeqListFind(&sl, 10);
	printf("%d\n", pos);
	pos = SeqListFind(&sl, 11);
	printf("%d\n", pos);
		
  1. 首先我插入了从 0 - 10 的 11 个元素,接着使用SeqListFind()分别查找 2, 10, 11, 并将结果打印出来
  2. 测试运行结果如下:
    在这里插入图片描述
    查找到的元素返回下标, 未查找到的元素返回 -1

2.2.9 顺序表指定位置插入 (SeqListInsert)

实现方式和头插差不多, 只是添加了个 pos 参数.
注意 pos 的合法性

在这里插入图片描述

  • SeqList.h
void SeqListInsert(SeqList* ps, int pos, SLDataType x);
  • SeqList.c
//在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDataType x)
{
	assert(ps);
	//确保插入在合法位置
	assert(pos >= 0 && pos <= ps->size);

	//若空间不够则进行扩容
	if (SeqListCheckCapacity(ps) == 0)
	{
		exit(-1);
	}
	
	//从后向前将pos后面的元素都移动一次
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	
	//将pos位置的值设置
	ps->a[pos] = x;
	ps->size++;
}
  1. 第一步先判断pos的合法性, 如果插入位置不合法, 直接结束程序
  1. 接着使用 SeqListCheckCapacity() 函数判断容量是否充足, 不充足则扩充容量
  1. 随后定义一个 end 变量指向顺序表最后一个元素位置, 将该元素后移一个元素空间, 同时end--, end指向顺序表倒数第二个元素位置
  1. 重复操作直至end指向pos处的位置, 并将pos处的元素后移一个元素空间
  1. 这时pos处的空间已经空闲, 将需要插入的元素插入到空闲空间, 同时size++, 有效个数加一
  • Test.c
	SeqList sl;
	SeqListInit(&sl);

	SeqListPushBack(&sl, 0);
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	SeqListPushBack(&sl, 6);
	SeqListPushBack(&sl, 7);
	SeqListPushBack(&sl, 8);
	SeqListPushBack(&sl, 9);
	SeqListPushBack(&sl, 10);

	pos = 2;
	SeqListInsert(&sl, pos, 100);
	SeqListPrint(&sl);

	pos = 10;
	SeqListInsert(&sl, pos, 100);
	SeqListPrint(&sl);

	pos = sl.size + 1;
	SeqListInsert(&sl, pos, 100);
	SeqListPrint(&sl);

	SeqListDestroy(&sl);

  1. 这里我插入了三次, 位置分别为 2, 10, sl.size + 1
  1. 测试结果如下:
    在这里插入图片描述
    前两次 pos 合法,插入成功; 最后一次 pos 值不合法, 程序直接退出

这样头插和尾插的功能可以直接复用SeqListInsert():

//顺序表尾插
void SeqListPushBack(SeqList* ps, SLDataType x)
{
	assert(ps);
	//若容量不够且扩容失败, 直接结束程序
	if (SeqListCheckCapacity(ps) == 0)
	{
		exit(-1);
	}

	SeqListInsert(ps, ps->size, x);
}	   

//顺序表头插
void SeqListPushFront(SeqList* ps, SLDataType x)
{
	assert(ps);	
	//若容量不够且扩容失败, 直接结束程序
	if (SeqListCheckCapacity(ps) == 0)
	{
		exit(-1);
	}

	SeqListInsert(ps, 0, x);
}

2.2.10 顺序表指定位置删除 (SeqListErase)

实现方式和顺序表头删差不多, 只是多了个 pos 参数

要注意pos的合法性: pos >=0 && pos < ps->size

  • SeqList.h
void SeqListErase(SeqList* ps, int pos, SLDataType x);
  • SeqList.c
//删除pos位置上的值
void SeqListErase(SeqList* ps, int pos)
{
	assert(ps);
	//确保pos值合法
	assert(pos >= 0 && pos < ps->size);
	//确保有空间可删
	assert(ps->size > 0);

	//从前将从pos+1的元素向前移动一次
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}
	
	ps->size--;
}
  • Test.c
	SeqList sl;
	SeqListInit(&sl);

	SeqListPushBack(&sl, 0);
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	SeqListPushBack(&sl, 6);
	SeqListPushBack(&sl, 7);
	SeqListPushBack(&sl, 8);
	SeqListPushBack(&sl, 9);
	SeqListPushBack(&sl, 10);
	
	int pos;
	pos = 1;
	SeqListErase(&sl, pos);
	SeqListPrint(&sl);

	pos = 3;
	SeqListErase(&sl, pos);
	SeqListPrint(&sl);
	SeqListDestroy(&sl);	

测试运行结果如下:
在这里插入图片描述


同样这样头删和尾删的功能可以直接复用SeqListErase():

//顺序表尾删
void SeqListPopBack(SeqList* ps, SLDataType x)
{
	assert(ps);
	//确保顺序表有数据可删
	assert(ps->size > 0);

	SeqListErase(ps, ps->size - 1);
}	   

//顺序表头删
void SeqListPopFront(SeqList* ps, SLDataType x)
{
	assert(ps);	
	//确保顺序表有数据可删
	assert(ps->size > 0);
	
	SeqListErase(ps, 0);
}

2.3 完整代码

  • SeqList.h: 头文件以及函数申明
#pragma once

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

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

// 对数据的管理:增删查改
void SeqListInit(SeqList* ps);
void SeqListDestroy(SeqList* ps);


void SeqListPrint(const SeqList* ps);
void SeqListPushBack(SeqList* ps, SLDataType x);
void SeqListPushFront(SeqList* ps, SLDataType x);
void SeqListPopBack(SeqList* ps);
void SeqListPopFront(SeqList* ps);	

// 顺序表查找
int SeqListFind(const SeqList* ps, SLDataType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDataType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos);
  • SeqList.c: 接口实现
#include "SeqList.h"

//检查容量是否满,若满则扩容,失败返回0,成功或容量未满返回1
static int SeqListCheckCapacity(SeqList* ps)
{
	//如果容量已满,则进行尝试扩容
	if (ps->size == ps->capacity)
	{
		//使用realloc将原空间扩大为 2 倍
		SLDataType* tmp = (SLDataType*)realloc(ps->a, 2 * ps->capacity * sizeof(SLDataType));
		//如果开辟失败,则提示后返回 0
		if (tmp == NULL)
		{
			printf("realloc 扩容失败.\n");
			return 0;
		}
		
		//如果开辟成功,则更新容量
		ps->a = tmp;
		ps->capacity *= 2;
	}
	return 1;
}

//顺序表初始化
void SeqListInit(SeqList* ps)
{
	assert(ps);
	ps->a = (SLDataType*)malloc(4*sizeof(SLDataType));
	if (ps->a == NULL)
	{
		perror("SeqListInit");
		exit(-1);
	}

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

//顺序表销毁
void SeqListDestroy(SeqList* ps)
{
	assert(ps);
	free(ps->a);		//将空间还给操作系统
	ps->a = NULL;		//置空防止野指针
	ps->size = 0;		//空间置零
	ps->capacity = 0;
}

//顺序表打印
void SeqListPrint(const SeqList* ps)
{
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}
	
//顺序表尾插
void SeqListPushBack(SeqList* ps, SLDataType x)
{
	assert(ps);
	//若容量不够且扩容失败, 直接结束程序
	if (SeqListCheckCapacity(ps) == 0)
	{
		exit(-1);
	}

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

//顺序表尾删
void SeqListPopBack(SeqList* ps)
{
	assert(ps);
	//确保顺序表有数据可删
	assert(ps->size > 0);

	//进行删除操作
	ps->size--;
}

//顺序表头插
void SeqListPushFront(SeqList* ps, SLDataType x)
{
	assert(ps);	
	//若容量不够且扩容失败, 直接结束程序
	if (SeqListCheckCapacity(ps) == 0)
	{
		exit(-1);
	}

	//从后开始将每个元素向后移动一个位置,直到下标到0
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	
	//第一个位置放入对应元素
	ps->a[0] = x;
	ps->size++;
}

//顺序表头删
void SeqListPopFront(SeqList* ps)
{
	assert(ps);
	//确保顺序表有数据可删
	assert(ps->size > 0);
	
	//从前向后将每个元素向前移动一个位置,直到下标为size-1
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}
	
	ps->size--;
}
		
//顺序表查找
int SeqListFind(const SeqList* ps, SLDataType x)
{	
	assert(ps);
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
	}
	
	return -1;
}

//在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDataType x)
{
	assert(ps);
	//确保插入在合法位置
	assert(pos >= 0 && pos <= ps->size);

	//若空间不够则进行扩容
	if (SeqListCheckCapacity(ps) == 0)
	{
		exit(-1);
	}
	
	//从后向前将pos后面的元素都移动一次
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		--end;
	}
	
	//将pos位置的值设置
	ps->a[pos] = x;
	ps->size++;
}

//删除pos位置上的值
void SeqListErase(SeqList* ps, int pos)
{
	assert(ps);
	//确保pos值合法
	assert(pos >= 0 && pos < ps->size);
	//确保有空间可删
	assert(ps->size > 0);

	//从前将从pos+1的元素向前移动一次
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		++begin;
	}
	
	ps->size--;
}

  • Test.c: 测试用例
#include "SeqList.h"

void SeqListTest1()
{
	SeqList sl;
	SeqListInit(&sl);
	
	SeqListPrint(&sl);
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	SeqListPopBack(&sl);	
	SeqListPopBack(&sl);	
	SeqListPopBack(&sl);	
	SeqListPopBack(&sl);	
	SeqListPopBack(&sl);	
	SeqListPopBack(&sl);	
	SeqListPopBack(&sl);	
	SeqListPrint(&sl);
	
	SeqListDestroy(&sl);
}

void SeqListTest2()
{
	SeqList sl;
	SeqListInit(&sl);

	SeqListPushFront(&sl, 1);
	SeqListPushFront(&sl, 2);
	SeqListPushFront(&sl, 3);
	SeqListPushFront(&sl, 4);
	SeqListPushFront(&sl, 5);
	SeqListPopFront(&sl);
	SeqListPrint(&sl);
	SeqListPopFront(&sl);
	SeqListPrint(&sl);
	SeqListPopFront(&sl);
	SeqListPrint(&sl);
	SeqListPopFront(&sl);
	SeqListPrint(&sl);

	SeqListDestroy(&sl);
}

void SeqListTest3()
{
	SeqList sl;
	SeqListInit(&sl);

	SeqListPushBack(&sl, 0);
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	SeqListPushBack(&sl, 6);
	SeqListPushBack(&sl, 7);
	SeqListPushBack(&sl, 8);
	SeqListPushBack(&sl, 9);
	SeqListPushBack(&sl, 10);
	
	int pos = 0;
	pos = SeqListFind(&sl, 2);
	printf("%d\n", pos);
	pos = SeqListFind(&sl, 10);
	printf("%d\n", pos);
	pos = SeqListFind(&sl, 11);
	printf("%d\n", pos);
		
	pos = 2;
	SeqListPrint(&sl);
	SeqListInsert(&sl, pos, 100);
	SeqListPrint(&sl);

	pos = 10;
	SeqListPrint(&sl);
	SeqListInsert(&sl, pos, 100);
	SeqListPrint(&sl);

	pos = sl.size + 1;
	SeqListInsert(&sl, pos, 100);
	SeqListDestroy(&sl);
}

void SeqListTest4()
{
	SeqList sl;
	SeqListInit(&sl);

	SeqListPushBack(&sl, 0);
	SeqListPushBack(&sl, 1);
	SeqListPushBack(&sl, 2);
	SeqListPushBack(&sl, 3);
	SeqListPushBack(&sl, 4);
	SeqListPushBack(&sl, 5);
	SeqListPushBack(&sl, 6);
	SeqListPushBack(&sl, 7);
	SeqListPushBack(&sl, 8);
	SeqListPushBack(&sl, 9);
	SeqListPushBack(&sl, 10);
	
	int pos;
	pos = 1;
	SeqListErase(&sl, pos);
	SeqListPrint(&sl);

	pos = 3;
	SeqListErase(&sl, pos);
	SeqListPrint(&sl);
	SeqListDestroy(&sl);	
	}
int main(void)
{
	//SeqListTest1();
	//SeqListTest2();
	//SeqListTest3();
	SeqListTest4();

	return 0;
}

本章完.

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

晋级榜单揭晓!华秋第九届硬创大赛-华南分赛区路演成功举办

7月21日&#xff0c;第十五届深创赛福田预选赛区暨华秋第九届硬创大赛华南分赛区决赛路演活动在深圳华强科创广场成功举办。活动由深圳华秋电子有限公司&#xff08;以下简称 华秋 &#xff09;、深圳市福田区新一代信息技术产业链党委、深圳新一代产业园、微纳研究院、华强科创…

【嵌入式学习笔记】嵌入式入门1——GPIO

1.什么是GPIO General Purpose Input Output&#xff0c;即通用输入输出端口&#xff0c;简称GPIO&#xff0c;作用是负责采集外部器件的信息或者控制外部器件工作&#xff0c;即输入输出。 2.STM32 GPIO简介 2.1.GPIO特点 不同型号&#xff0c;IO口数量可能不一样&#x…

中小学分班查询系统0成本制作方法公布了,人人可用

传统的学生分班查询平台通常需要进行专业的技术开发&#xff0c;以实现学生查询和查看分班信息的功能。这个过程涉及到软件开发、数据库设计、系统集成等多个环节&#xff0c;需要有一支专业的技术团队来完成。 然而&#xff0c;这样的技术开发和维护过程需要耗费大量的经济成…

HBase有写入数据,页面端显示无数据量

写了一个测试类&#xff0c;插入几条数据&#xff0c;测试HBase的数据量。很简单的功能&#xff0c;这就出现问题了。。网页端可以看到&#xff0c;能够看到读写请求&#xff0c;但是不管是内存、还是磁盘&#xff0c;都没有数据。 于是就想到去HDFS查看&#xff0c;也是有数据…

Python简要复习

Python程序设计复习 Python基础知识 python的特点 兼具编译型和解释型特性&#xff0c;兼顾过程式、函数式和面向对象编程范式的通用编程语言 解释型语言无需像编译型需要一次性的编译成机器码&#xff0c;然后运行&#xff0c;而是由名叫解释器的程序动态的将源代码逐句转…

热备份路由协议原理

热备份路由协议原理 HSRP协议/VRRP协议热备份协议 热备份协议&#xff08;Hot Standby Protocol&#xff09; 是一种基于冗余设计的协议&#xff0c;用于提高网络的可靠性和冗余性。它允许多个设备共享同一个IP地址&#xff0c;其中一个设备被选为主设备&#xff0c;其他设备…

Tinkercad 建模21个小技巧

21个Tinkercad 建模小技巧 原文 参考文章&#xff1a;在 Tinkercad 中加快设计的 22 个技巧 一起来了解一下21个Tinkercad 3D建模小技巧&#xff0c;让你快人一步。 技巧1 Copy & Paste 文件&#xff0c;整合设计 想把文件A里面的模型拷贝到文件B里面&#xff1f; 很容…

【Linux】Centos的一些快捷操作

Centos的一些快捷操作 一个窗口多个终端GVIM 一个窗口多个文件 一个窗口多个终端 GVIM 一个窗口多个文件

idealC-2020.1.4免费下载(附安装教程)

下载地址 [软件名称]: IntelliJ IDEA 2020 [软件大小]: 560MB [安装环境]: Windows [下载链接]: 链接: https://pan.baidu.com/s/1yGOWqfRVE6cPsAe0qHpnbg https://pan.baidu.com/s/1yGOWqfRVE6cPsAe0qHpnbg 提取码: zt88 软件介绍 idea 2020.是由捷克IntelliJ公司而…

python_day16_设计模式

“”“单例模式”“” “”“工厂模式”“” class Person:passclass Worker(Person):passclass Student(Person):passclass Teacher(Person):passclass Factory:def get_person(self, p_type):if p_type w:return Worker()elif p_type s:return Student()else:return Te…

51单片机学习--按键控制流水灯模式定时器时钟

TMOD负责确定T0和T1的工作模式&#xff0c;TCON控制T0和T1的启动或停止计数&#xff0c;同时包含定时器状态 TF1&#xff1a;定时器1溢出标志 TF0&#xff1a;定时器0溢出标志 0~65535 每隔1微秒计数器1&#xff0c;总时间65535微秒&#xff0c;赋上初值64535&#xff0c;则只…

webstorm格式化代码后单引号转成了双引号

webStorm格式化js代码时单引号变成了双引号&#xff0c;问题如下&#xff1a; 格式化前&#xff1a; 格式化后&#xff1a; 解决办法&#xff1a; window: File -> Settings -> Editor -> Code Style -> Javascript&#xff1b; mac: webStorm -> Preference …

Flink回撤流

1.回撤流定义&#xff08;RetractStream&#xff09; Flink 的回撤流是指在 Flink 的流处理算法中&#xff0c;撤回已经发送到下游节点的数据。这是因为在实际应用场景中&#xff0c;有些错误数据可能会发送到下游节点&#xff0c;因此需要回撤流以保证数据的准确性。 回撤流…

【Docker】Docker应用部署之Docker容器安装Tomcat

目录 一、搜索镜像 二、拉取镜像 三、创建容器 四、测试使用 一、搜索镜像 docker search tomcat 二、拉取镜像 docker pull tomcat:版本 三、创建容器 首先在宿主机创建数据卷的目录 mkdir /root/tomcat # 创建目录 cd /root/tomcat # 进入目录 docker run -id -…

【腾讯云 Cloud Studio 实战训练营】永不宕机的IDE,Coding Everywhere

【腾讯云 Cloud Studio 实战训练营】永不宕机的IDE&#xff0c;随时随地写代码&#xff01; 写在最前视频讲解&#xff1a;Cloud Studio活动简介何为腾讯云 Cloud Studio?Cloud Studio简介免费试用&#xff0c;上手无忧Cloud Studio 特点及优势云端开发多种预制环境可选metawo…

怎么学习C语言,才能快速掌握?

有多年软件行业经验&#xff0c;期间参与过多个C语言项目。要掌握一门编程语言&#xff0c;仅仅投入时间学习是不够的&#xff0c;关键在于实际项目经验。在没有真正实战经验之前&#xff0c;不宜轻易声称掌握某种编程语言&#xff0c;因为编程是积累性的工作&#xff0c;理论知…

本地Git仓库和GitHub仓库SSH传输

SSH创建命令解释 ssh-keygen 用于创建密钥的程序 -m PEM 将密钥的格式设为 PEM -t rsa 要创建的密钥类型&#xff0c;本例中为 RSA 格式 -b 4096 密钥的位数&#xff0c;本例中为 4096 -C “azureusermyserver” 追加到公钥文件末尾以便于识别的注释。 通常以电子邮件地址…

微服务契约测试框架Pact-Python实战

Pact是一个契约测试框架&#xff0c;有多种语言实现&#xff0c;本文以基于pact-python探究契约测试到底是什么&#xff1f;以及如何实现 官网&#xff1a;自述文件 |契约文档 (pact.io) 契约测试步骤 1、为消费者写一个单元测试&#xff0c;让它通过&#xff0c;并生成契约…

中小企业如何低成本实施MES管理系统

中小企业在市场竞争中需要有高效的管理体系来支持其运营和发展。中小企业MES管理系统是一种先进的管理系统&#xff0c;可以提升工厂智能化水平&#xff0c;提高生产效率&#xff0c;是中小企业必须采取的有效管理工具。然而&#xff0c;由于资金和技术的限制&#xff0c;中小企…

Git分布式版本控制工具和GitHub(二)--Git指令入门

一.指令入门前的准备 1.Git全局设置 2.获取Git仓库 例如&#xff1a;将我GitHub上的first_resp仓库克隆到本地。 点击进入first_rep&#xff0c;后面本地仓库操作的学习就是在这个界面右键打开Git Bash 3.工作区&#xff0c;暂存区&#xff0c;版本库概念 注&#xff1a;如果空…
最新文章