数据结构(顺序表)(2)

数据结构(顺序表)(2)

  • 本文对顺序表收个尾,还有几个函数.
  • 一个是指定位置插入数据, 一个是指定位置删除数据. 还有一些由这两个函数而牵涉到的函数.
  • 指定位置插入数据, 同样的先分析一波:

请添加图片描述

//在下标为pos的位置插入x
void SLInsert(SL* ps, int pos, SLDataType x)
{
	//防止传入的是空指针
	assert(ps);
	//判断下标pos的值是否在有效数据的下标的范围内
	//当pos=ps->size时候.刚好是特殊情况,尾插数据
	assert(pos >= 0 && pos <= ps->size);
	//判断空间是否足够
	SLCheckCapacity(ps);

	//挪动数据
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	//赋值,size++
	ps->a[pos] = x;
	ps->size++;
}
  • 删除指定位置的数据, 先分析一波:

请添加图片描述

//删除下标为pos的值
void SLErase(SL* ps, int pos)
{
	//同样,先断言判断是否为空指针
	assert(ps);
	//判断pos的范围
	assert(pos >= 0 && pos < ps->size);
	//挪动数据
	//当pos为最后一个元素的下标的时候,这个循环直接就跳过了
	//直接size--,就相当于尾部删除了.
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}

	ps->size--;
}
  • 这时候咱们就可以测试一下:

请添加图片描述

  • 当我们有了这两个函数之后, 前面写的尾插, 头插, 尾删, 头删, 都可以用这两个函数去实现.
//尾部插入数据
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);
   SLInsert(ps, ps->size, x);
}


//尾部删除数据
void SLpopback(SL* ps)
{
	assert(ps);
   SLErase(ps, ps->size-1);
}


//头部插入数据
void SLpushFront(SL* ps, SLDataType x)
{
	assert(ps);
   SLInsert(ps, 0, x);
}


//头部删除数据
void SLpopFront(SL* ps)
{
	assert(ps);
   SLErase(ps, 0);
}
  • 而且以后如果要用c语言写顺序表, 也是这样写, 先把指定位置插入删除, 这两个函数写好之后, 就可以复用这两个函数到别的功能去了.
  • 然后就还有函数, 算是锦上添花吧, 不过也无伤大雅.
  • 一个是修改下标为pos的值为x
//修改下标为pos的值为x
void SLModify(SL* ps, int pos, SLDataType x)
{
	//同样的,先断言判空
	assert(ps);
	//判断pos范围
	assert(pos >= 0 && pos < ps->size);
	//直接赋值就可
	ps->a[pos] = x;
}
  • 有同学觉得这个函数没什么必要, 要这样直接在主函数代码文件写一句sa.a[1] = 2;(这个是我的一个例子,可以看下面的图片,就知道了.) 这样不就可以了吗, 不过写函数有个好处是什么呢, 函数里面有更多的判断条件, 代码就更稳妥些, 如果单单只是一个语句, 可能后面有人用你的代码的时候, 没有理解好你的代码, 把你的代码改了改, 不是如下这样写:

请添加图片描述

  • 而是这样写:

请添加图片描述

  • 对吧, 所以用函数还能检测出来是不是非空指针. 所以有时候是能用函数就用函数.
  • 还有最后一个函数, 这个函数主要是拿来配合指定位置插入数据和指定位置删除数据用的. 作用就是找到值为x的下标, 这样就多了一种玩法, 就可以直接你想删什么数值就删什么数值, 不用去想下标是多少了. 大概就这么一个作用.
//找到值为x的元素的下标,没有找到返回-1
int SLFind(SL* ps, SLDataType x)
{
	//判断是否为空指针
	assert(ps);
	//循环遍历找下标
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->a [i] == x)
		{
			return i;
		}
	}
	//没找到就返回-1,因为别的函数会判断pos的范围的,所以返回个-1就可以了.
	return -1;
}
  • OK, 那顺序表这部分就已经讲完了, 下面是全部的代码:
  • 先是头文件的Seqlist.h
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>

//定义一个静态顺序表
//咱们可以通过这个宏来设定一个N,这样更换数组大小的时候,改N就行.
//#define N 100
//typedef的目的和宏也是一样,如果想要替换顺序表里面的数据类型,改这里就可以.
//typedef int SLDataType;
//
//struct Seqlist
//{
//	那这里就可以看到,这个顺序表是静态的,大小是固定不变的.
//	SLDataType a[N];
//    这里是表示顺序表里面有效数据的个数.
//	int size;
//};
//上文也说了静态顺序表不合适,这里仅做一种演示.


//动态顺序表:
//原因同上
typedef int SLDataType;
//这里也是,由于顺序表名称长,可以typedef更换一下名字
typedef struct Seqlist
{
	SLDataType* a;//指向动态开辟空间的首元素地址
	int size;//顺序表有效的元素个数
	int capacity;//顺序表的容量,空间大小.
}SL;

//初始化顺序表
void SLInit(SL* ps);
//程序结束销毁顺序表
void SLDestroy(SL* ps);

//打印顺序表内容(这个函数主要是为了方便测试)
void SLPrint(SL* ps);
//判断顺序表空间是否足够
void SLCheckCapacity(SL* ps);

//尾部插入数据
void SLPushBack(SL* ps, SLDataType x);
//尾部删除数据
void SLpopback(SL* ps);
//头部插入数据
void SLpushFront(SL* ps, SLDataType x);
//头部删除数据
void SLpopFront(SL* ps);

//在下标为pos的位置插入x
void SLInsert(SL* ps, int pos, SLDataType x);
//删除下标为pos的值
void SLErase(SL* ps, int pos);

//修改下标为pos的值为x
void SLModify(SL* ps, int pos, SLDataType x);
//找到值为x的元素的下标,没有找到返回-1
int SLFind(SL* ps, SLDataType x);
  • 下面是实现函数的.c文件. Seqlist.c
#include"Seqlist.h"

//初始化顺序表
void SLInit(SL* ps)
{
	//咱们可以先动态开辟一段空间,这段空间
	//想开多少就开多少,咱们这边就先给4个整型空间
	//因为这时候SLDataType是int的别名
	ps->a = (SLDataType*)malloc(sizeof(SLDataType) * 4);
	//为了代码的健壮性,咱们可以判断一下指针
	//空的话,直接整个程序结束
	if (ps->a == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	//这里就是初始有效数据是0
	ps->size = 0;
	//这里就按照我们刚刚开辟的4个空间给
	ps->capacity = 4;
}


//程序结束销毁顺序表
void SLDestroy(SL* ps)
{
	//先释放空间
	free(ps->a);
	//指针置空
	ps->a = NULL;
	//有效数据个数和容量置为0
	ps->capacity = ps->size = 0;
}


//打印顺序表内容(这个函数主要是为了方便测试)
void SLPrint(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

//判断顺序表空间是否足够
void SLCheckCapacity(SL* ps)
{
	//满了就要扩容
	if (ps->size == ps->capacity)
	{
		//为了代码健壮性,我们先用临时变量tmp接受
		SLDataType* tmp = (SLDataType*)realloc(ps->a, ps->capacity * 2 * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("relloc failed");
			exit(-1);
		}
		//判断是否为空之后,再赋值给ps->a
		ps->a = tmp;
		//一般情况来说,我们扩容都是两倍两倍的扩容
		ps->capacity *= 2;
	}
}


//尾部插入数据
void SLPushBack(SL* ps, SLDataType x)
{
	//先判断空间是否足够
	SLCheckCapacity(ps);
	//插入数据
	ps->a[ps->size] = x;
	//插入数据之后记得,有效数据个数size要加一.
	ps->size++;

}


//尾部删除数据
void SLpopback(SL* ps)
{
	//温柔版检查  
	//if (ps->size == 0)
	//	return;
	
	//暴力检查,这就是如果出错了就直接报错,退出程序,
	assert(ps->size > 0);

	ps->size--;
}


//头部插入数据
void SLpushFront(SL* ps, SLDataType x)
{
	//先判断空间是否足够
	SLCheckCapacity(&ps);
	//挪动数据
	int end = ps->size - 1;
	for(int i = end; i >= 0;i--)
	{
		ps->a[i + 1] = ps->a[i];
	}
	//赋值
	ps->a[0] = x;
	//记得有效数据要加一
	ps->size++;
}


//头部删除数据
void SLpopFront(SL* ps)
{
	//断言一下,防止为空
	assert(ps);
	//判断有效数据个数
	assert(ps->size > 0);
	//挪数据
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

//找到值为x的元素的下标,没有找到返回-1
int SLFind(SL* ps, SLDataType x)
{
	//判断是否为空指针
	assert(ps);
	//循环遍历找下标
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->a [i] == x)
		{
			return i;
		}
	}
	//没找到就返回-1,因为别的函数会判断pos的范围的,所以返回个-1就可以了.
	return -1;
}

//在下标为pos的位置插入x
void SLInsert(SL* ps, int pos, SLDataType x)
{
	//防止传入的是空指针
	assert(ps);
	//判断下标pos的值是否在有效数据的下标的范围内
	//当pos=ps->size时候.刚好是特殊情况,尾插数据
	assert(pos >= 0 && pos <= ps->size);
	//判断空间是否足够
	SLCheckCapacity(ps);

	//挪动数据
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	//赋值,size++
	ps->a[pos] = x;
	ps->size++;
}


//删除下标为pos的值
void SLErase(SL* ps, int pos)
{
	//同样,先断言判断是否为空指针
	assert(ps);
	//判断pos的范围
	assert(pos >= 0 && pos < ps->size);
	//挪动数据
	//当pos为最后一个元素的下标的时候,这个循环直接就跳过了
	//直接size--,就相当于尾部删除了.
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}

	ps->size--;
}


//修改下标为pos的值为x
void SLModify(SL* ps, int pos, SLDataType x)
{
	//同样的,先断言判空
	assert(ps);
	//判断pos范围
	assert(pos >= 0 && pos < ps->size);
	//直接赋值就可
	ps->a[pos] = x;
}
  • 下面是测试代码, 可看可不看, 无伤大雅: test.c
#include"Seqlist.h"

int main()
{
	SL sa;
	SLInit(&sa);
	SLPushBack(&sa, 1);
	SLPushBack(&sa, 2);
	SLPushBack(&sa, 3);
	SLPushBack(&sa, 4);
	SLPushBack(&sa, 5);

	/*SLPrint(&sa);


	SLInsert(&sa, 2, 100);

	SLPrint(&sa);

	SLErase(&sa, 2);

	SLPrint(&sa);*/


	SLPrint(&sa);

	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值