学习顺序表相关知识,制作一个简易操作(增删查改)
//test.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
int main()
{
TestSeqList();
return 0;
}
SeqList.c
#define _CRT_SECURE_NO_WARNINGS 1
#include //assert
#include"SeqList.h"
#include//malloc
#include//printf NULL
// 需要改变的顺序表 初始容量大小
void SeqListInit(SeqList* s, int initCapacity) //初始化
{
assert(s);
initCapacity = initCapacity <= 3 ? 3 : initCapacity;
//10个 SDataType------>总的字节数:sizeof(SDataType)*10
s->array = (SDataType*)malloc(sizeof(SDataType)*initCapacity);//initCapacity这个不要忘记乘,如果没有这个,则在插入的过程中会崩溃。
if (NULL == s->array)//判断空间是否申请成功
return;
s->capacity = initCapacity;
s->size = 0;
}
//检查是否需要进行扩容
int CheckCapacity(SeqList* s)
{
assert(s);
if (s->size == s->capacity)
{
//将新空间按照两倍的方式进行扩容
int newCapacity = s->capacity * 2;
s->array - (SDataType*)realloc(s->array, newCapacity*sizeof(SDataType));
if (NULL == s->array)
return 0;
s->capacity = newCapacity;
/*
//申请更大的空间
SDataType* temp = (SDataType*)malloc(newCapacity*sizeof(SDataType));
if (NULL == temp)
return 0;
//拷贝元素
memcpy(temp, s->array, s->capacity*sizeof(SDataType));
//释放旧空间
free(s->array);
//使用新空间
s->array = temp;
s->capacity = newCapacity;
*/
}
return 1;
}
//图例解释:
// size
//【1 2 3 4 5 6】
// capacity
//此时size和capacity在同一个位置
//给出更大的空间:将新空间的大小给成旧空间大小的2倍(常规)
//给大了,浪费空间,给小了,以后可能多次申请空间,所以常规定为2倍。
//【 】
//将旧空间的元素搬移到新空间中,然后将旧空间释放掉
//总结过程:
//1.开辟新空间
//2.拷贝元素
//3.释放旧空间
//4.使用新空间
//延申:malloc calloc realloc之间的区别
//相同点:1.都是c标准库提供的动态内存申请的库函数
// 2.返回值都是void*,因此在使用时必须进行强制类型转换
// 3.申请空间成功,返回值空间的首地址,失败则返回NULL,因此在使用时必须进行判空
// 4.申请的空间都在堆上,使用完成后必须free进行释放,否则就会存在内存泄漏
//不同:
//malloc:参数申请空间大小的字节数,直接将空间申请成功返回给用户
//calloc:有两个函数--参数1:表示元素个数,参数二:表示元素类型
// 功能:根据所给的参数来申请孔家,并将空间初始化为零
// size_t 无符号整形
//void* realloc(void* ptr,size_t size) :将ptr所指向的空间调整到size个字节
//1.ptr == NULL realloc函数就相当于malloc
//2.ptr != NULL,realloc将ptr指向的空间调整到size字节
// 假设:ptr指向的空间大小为oldsize个字节
// (1).size<=oldsize:将ptr指向的空间缩小到size字节,然后返回空间的首地址
// (2).size>oldsize:将ptr指向的空间扩大到size个字节
// 1)大一点点
// 原来的空间后面还有空余位置
// 对空间进行延申,然后返回原空间的首地址
// 2)大了许多
// 1.申请新空间
// 2.拷贝元素
// 3.释放旧空间
// 4.返回新空间的首地址
//
//
//【 原来的空间 】
//【 20 】
//【 】
//【别人申请的空间但是已经归还了】
//【 10 】
//【 】
//【别人的空间 】
//【 】
//【 】
//尾插 顺序表里面插入一些元素
//1.不需要扩容,直接将元素插入在size的位置--->O(1)
//2.需要扩容
void SeqListPushBack(SeqList* s, SDataType data)
{
//assert(s);
//s->array[s->size] = data;
//s->size++;//一般情况我们将这行代码和上一行进行合并
//即:
assert(s);
if (!CheckCapacity(s))
return;
s->array[s->size++] = data;
}
//这样写有点问题,我们必须保证空间足够,才能一直插入。
//举例:2
//array size = 5
//【1 2 3 4 5 】capacity(总空间大小)
// 0 1 2 3 4
//尾插:将新元素放到5后面,此时新元素在的位置时size,然后size加一,
//尾删 //方法:将最后一个元素访问不到,(不让他在size的范围之内),时间复杂度O(1)
void SeqListPopBack(SeqList* s)
{
if (SeqListEmpty(s))
return;
s->size--;
}
//头插
//1.先检测是否需要扩容--如果需要则扩容
//2.将顺序表中所有元素整体往后移一位
//3.将data插入顺序表的起始位置
//时间复杂度O(N)
void SeqListPushFront(SeqList* s, SDataType data)
{
//插入都需要检查是否需要扩容
if (!CheckCapacity(s))
return;
//1.将顺序表中的所有元素向后搬移一个位置
for (int i = s->size - 1; i >= 0; i--)
s->array[i+1] = s->array[i];
//在0号位置插入data
s->array[0] = data;
s->size++;
}
//头删--除第一个元素外,剩余元素整体往搬移一个位置--时间复杂夫O(N)
//顺序表不适合做头插和头删的工作,效率太低------链表适合做
void SeqListPopFront(SeqList* s)
{
if (SeqListEmpty(s))
return;
for (int i = 1; i < s->size; i++)
s->array[i - 1] = s->array[i];
s->size--;
}
//任意位置的插入
//1.必须保证pos在[0,size]
//2.检测是否需要扩容
//3.将[pos,size]位置的元素整体往后搬移一个位置
//4.在pos的位置插入新元素
void SeqListInsert(SeqList* s, int pos, SDataType data)
{
assert(s);
if (pos >= 0 && pos > s->size)
return;
//检测是否需要扩容
if (!CheckCapacity(s))
return;
//将[pos,size]位置的元素整体往后搬移一个位置
//i:表示需要搬移元素的下标
for (int i = s->size - 1; i >= pos; --i)
s->array[i+1] = s->array[i];
s->array[pos] = data;
s->size++;
}
//图像演示:
//(1)
//【1 2 3 4 5 6 7 】
// 丨
// pos
//我们需要在pos指向的位置插入一个元素0
//不能直接插入,直接插,会导致元素0直接覆盖掉3这个数字
//我们需要:将2以后的所有数字往后搬移一个位置,然后将0插入进去
//(2)
//【1 2 3 4 5 6 7 _ _ 】
// 丨
// pos
//我们需要在pos指向的位置插入一个元素0
//这种插入没有意义
//没有插入的时候size在7的后一位
//你在这个位置插入元素,那么size是 向后移一位,还是不移动,还是两位(在插入元素0后面)?
//1.不移动size,此时0访问不到,此时插入元素0,则没有意义
//2.给加一,7后面没有有效元素
//3.如果向后移动两位,此时零可以访问到,但是七后面没有有效元素
// 此时顺序表一共有八个元素,但是size却是11
//因此:pos是有限制条件的他在[0,size]
//任意位置的删除
//注意:进行删除的时候需要判断pos的位置是否在[0,size)
//将pos之后的所有元素往前整体搬移一个位置,需要删除的元素就会直接被删除
void SeqListErase(SeqList* s, int pos)
{
assert(s);
if (pos < 0 || pos >= s->size)
return;
for (int i = pos + 1; i < s->size; ++i)
s->array[i - 1] = s->array[i];
s->size--;
}
//顺序表里面有效元素个数
int SeqListSize(SeqList* s)
{
assert(s);//对顺序表里面的参数进行检测
return s->size;
}
//顺序表里面的容量
int SeqListCapacity(SeqList* s)
{
assert(s);
return s->capacity;
}
//顺序表是否为NULL
int SeqListEmpty(SeqList* s)
{
assert(s);
return 0 == s->size;//判断size是否为零
}
//将顺序表里面的元素进行清空
void SeqListClear(SeqList* s)
{
assert(s);
s->size = 0;
}
//顺序表里面查找值为data的元素是否再顺序表中,如果在返回下标,否则返回-1
int SeqListFind(SeqList* s, SDataType data)
{
assert(s);
for (int i = 0; i < s->size; ++i)
{
if (s->array[i] == data)
return i;
}
return -1;
}
//将当前的顺序表销毁掉
void SeqListDestroy(SeqList* s)
{
assert(s);
if (s->array)
{
free(s->array);
s->array = NULL;
s->capacity = 0;
s->size = 0;
}
}
//测试方法:
void SeqListPrint(SeqList* s)
{
assert(s);
for (int i = 0; i < s->size; ++i)
printf("%d ", s->array[i]);
printf("\n");
}
void Test1()
{
SeqList s;
SeqListInit(&s, 10);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPushBack(&s, 5);
SeqListPushBack(&s, 6);
printf("&d\n", SeqListSize(&s));
printf("&d\n", SeqListCapacity(&s));
SeqListPrint(&s);
SeqListPopBack(&s);
SeqListPopBack(&s);
SeqListPopBack(&s);
printf("&d\n", SeqListSize(&s));
printf("&d\n", SeqListCapacity(&s));
SeqListPrint(&s);
SeqListPushFront(&s, 0);
SeqListPrint(&s);
SeqListPopFront(&s);
SeqListPrint(&s);
SeqListDestroy(&s);
}
void Test2()
{
//保证顺序表里面含有六个元素
SeqList s;
SeqListInit(&s, 2);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPushBack(&s, 5);
SeqListPushBack(&s, 6);
SeqListPrint(&s);
//任意位置的插入:在三号位置插入零
SeqListInsert(&s, 3, 0);
SeqListPrint(&s);
//删除第一个元素4,你的顺序表中第一个元素四(有可能你的顺序表中含有多个四)
SeqListErase(&s, SeqListFind(&s, 4));
SeqListPrint(&s);
}
void TestSeqList()
{
//Test1();
Test2();
}
//注意:扩容程序最后才补充的,所以有一些语句有点不通顺
// (代码都是一点一点改进成的,没有人的代码直接就能写成,除了大佬,我这样写,也有益于我们学习)
// 这点问题如果给你带来不便,请原谅,(直接把注释删除了,就行了)
Seqlist.h
#pragma once//(防止多次包含)
//顺序表只能存储一种类型,当我们需要改变这个类型的时候,很麻烦,我们只需要将类型的名字改成(SDataType)便可
typedef int SDataType; //这个顺序表可以存放int类型
//typedef double SDataType; //这个顺序表可以存放double类型
//我们定义的这种结构体类型,在c语言中使用时必须要加入struct
//因为麻烦,所以我们将其进行下面的改变
//struct SeqList
//{
// int* array;//存放元素的空间,不够时申请更大的
// int capacity;//空间总大小 capacity:容量
// int size;//空间中有效元素的个数
//};
//【2】我们还有另外一种方法
//typedef 后面的
//结构体类型:
//struct SeqList
//{
// int* array;//存放元素的空间,不够时申请更大的
// int capacity;//空间总大小 capacity:容量
// int size;//空间中有效元素的个数
//};
//我们需要将其命名为SeqList,即下面:
typedef struct SeqList
{
int* array;//存放元素的空间,不够时申请更大的
int capacity;//空间总大小 capacity:容量
int size;//空间中有效元素的个数
}SeqList;
//【1】
//c语言里面你自己定义的结构体,你要用的时候需要假如struct
//而我们每次使用自己的结构体时,都需要用struct,麻烦,所以我们将struct SeqList命名为SeqList
//typedef struct SeqList SeqList;
// 需要改变的顺序表 初始容量大小
void SeqListInit(SeqList* s, int initCapacity);
//尾插 顺序表里面插入一些元素
void SeqListPushBack(SeqList* s, SDataType data);
//尾删
void SeqListPopBack(SeqList* s);
//头插
void SeqListPushFront(SeqList* s, SDataType data);
//头删
void SeqListPopFront(SeqList* s);
//任意位置的插入
void SeqListInsert(SeqList* psl, int pos, SDataType x);
//任意位置的删除
void SeqListErase(SeqList* psl, int pos);
//顺序表里面有效元素个数
int SeqListSize(SeqList* s);
//顺序表里面的容量
int SeqListCapacity(SeqList* s);
//顺序表是否为NULL
int SeqListEmpty(SeqList* s);
//将顺序表里面的元素进行清空
void SeqListClear(SeqList* s);
//顺序表里面查找值为data的元素是否再顺序表中,如果在返回下标,否则返回-1
int SeqListFind(SeqList* s, SDataType data);
//将当前的顺序表销毁掉
void SeqListDestroy(SeqList* s);
//测试方法
void TestSeqList();
//萌新一枚,请多多指教