学习顺序表相关知识,制作一个简易的“动态顺序表”操作(增删查改)

学习顺序表相关知识,制作一个简易操作(增删查改)

//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.h>    //assert
#include"SeqList.h"
#include<malloc.h>//malloc
#include<stdio.h>//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();

//萌新一枚,请多多指教

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值