【数据结构】【2】什么是顺序表?怎么样实现顺序表

1. 顺序表

1.1 什么是线性表

介绍顺序表之前,我们先简单的了解一下什么是线性表线性表就是逻辑上是连续的

1.2 顺序表概念

顺序表,本质就是数组,动态增长,但是要求里面的数据在逻辑上和物理上都是连续的,通俗一点讲就是数据一个接一个的存储,而且存储的位置也是连续的。

1.3 顺序表的缺陷

        我们在设计表的时候,不能说一上来就给数组一个准确的大小,大了或者小了都不合适,所以在设计表的时候,考虑动态增长,少了我就开辟一些空间。但是动态内存开辟空间是有性能消耗的。这里有一篇博客使用代码演示了动态内存开辟时候的消耗。

(46条消息) malloc动态申请内存空间对程序效率的影响_thinkercui的博客-CSDN博客_malloc效率

 那顺序表的缺陷我们就显而易见了

  1. 动态内存增容造成的性能消耗
  2. 动态内存开辟的时候会浪费空间(因为顺序表要求连续存储,假如之前开辟的空间,后面有别的数据,就需要另外找一块空间替换之前的空间)。
  3. 对头部的数据进行操作的时候需要挪动数据

1.4 顺序表的实现

  1. 顺序表不能只存一种数据,所以我们首先先对类型进行一个重定义,这样我们再存别的数据的时候修改一下数据类型即可。
  2. 接下来就是顺序表的结构体,结构体成员有数组,数组里存了多少个元素,数组有多大。
  3. 设计函数,包括实习顺序表的增删改查、打印、初始化,动态内存开辟、销毁。
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

typedef int SeqDataType;

typedef struct SeqList
{
	SeqDataType* a;
	int size;
	int capacity;
}SeqList;

void SeqListInit(SeqList* pSeq);                    //顺序表初始化
void SeqListCheck(SeqList* pSeq);                   //动态内存开辟,如果空间不够了就开辟
void SeqListDestory(SeqList* pSeq);                 //顺序表销毁
void SeqListPrint(SeqList* pSeq);                   //顺序表元素打印
void SeqListPushBack(SeqList* pSeq, SeqDataType x); //尾部插入数据
void SeqListPushFront(SeqList* pSeq, SeqDataType x);//头部插入数据
void SeqListPopBack(SeqList* pSeq);                 //尾部删除数据
void SeqListPopFront(SeqList* pSeq);                //头部删除数据
int SeqListQuery(SeqList* pSeq, SeqDataType x);     //查询数据 
void SeqListInsert(SeqList* pSeq, int pos, SeqDataType x);//任意位置插入
void SeqListErase(SeqList* pSeq, int pos);          //任意位置删除
void SeqListModify(SeqList* pSeq, int pos, SeqDataType x);//修改数据

1.4.1 顺序表的初始化

void SeqListInit(SeqList* pSeq)
{
	assert(pSeq);//防止传入空指针
	pSeq->a = NULL;
	pSeq->capacity = pSeq->size = 0;
}

1.4.2 动态增容

void SeqListCheck(SeqList* pSeq)
{
    assert(pSeq);
	int newcapacity = pSeq->capacity == 0 ? 4 : pSeq->capacity * 2;//如果当前数组的容量为0,那么就开辟4个空间
	if (pSeq->capacity == pSeq->size)
	{
		SeqDataType* newArea = realloc(pSeq->a, newcapacity * sizeof(SeqDataType));
		if (newArea == NULL)
		{
			printf("内存开辟失败\n");
			exit(-1);
		}
		pSeq->a = newArea;
		pSeq->capacity = newcapacity;
	}
}

这里有个点注意一下,realloc是可以传入空指针的,传入空指针的作用等同于malloc

1.4.3 顺序表销毁

void SeqListDestory(SeqList* pSeq)
{
	assert(pSeq);
	free(pSeq->a);//这里面这个数组是动态开辟的,所以要释放的是这个数组
	pSeq->a = NULL;
	pSeq->capacity = pSeq->size = 0;
}

1.4.5 顺序表打印

void SeqListPrint(SeqList* pSeq)
{
    assert(pSeq);
	for (int i = 0; i < pSeq->size; i++)
	{
		printf("%d ", pSeq->a[i]);
	}
	printf("\n");
}

1.4.6 顺序表尾部插入

void SeqListPushBack(SeqList* pSeq, SeqDataType x)
{
	assert(pSeq);
	SeqListCheck(pSeq);//是不是需要增容
	pSeq->a[pSeq->size] = x;
	pSeq->size++;
}

1.4.7 顺序表头部插入

void SeqListPushFront(SeqList* pSeq, SeqDataType x)
{
	assert(pSeq);
	SeqListCheck(pSeq);
	int end = pSeq->size - 1;//下标,一共有size个元素需要往后移
	while (end >= 0)
	{
		pSeq->a[end + 1] = pSeq->a[end];
		end--;
	}
	pSeq->a[0] = x;
	pSeq->size++;
}

        我们在头部插入数据的时候,需要将整个数组的元素向后移一个,把第一个元素的位置让出来,那么数据在数组中应该是从后往前移,先移8然后再依次拿前面的元素向后移,这样不会干扰元素,如果我们拿前面的元素向后移,我们会发现,后面的元素还没移已经发生变化了,这样不可行。我们只能从后往前移,但是我们得考虑数组能不能往后移,所以在移之前我们判断一下空间是否够不够的话我们对空间进行开辟,然后把数据依次向后移,然后将我们插入的数据赋给第一个元素,这就是头插的过程。

void SeqListPushFront(SeqList* pSeq, SeqDataType x)
{
	assert(pSeq);
	SeqListCheck(pSeq);
	int end = pSeq->size - 1;//下标,一共有size个元素需要往后移
	while (end >= 0)
	{
		pSeq->a[end + 1] = pSeq->a[end];
		end--;
	}
	pSeq->a[0] = x;
	pSeq->size++;
}

 1.4.8 尾部删除数据

void SeqListPopBack(SeqList* pSeq)
{
	pSeq->size--;
}

        尾部删除数据我们直接将数组的大小减1,在寻找的时候就不会找到后面这个元素,删除数据就不进行容量减少,本身增容对性能的影响比较大。

1.4.9 任意位置插入数据

        比方说从pos位置开始插入数据,那么后面的数据就会向后移,数据向后移的话,必然是从后往前一个一个的向后移,然后会出现数据覆盖的情况。,那一直移到什么程度呢?一直把end移动到pos的位置。最后将pos的位置赋值即可。

void SeqListInsert(SeqList* pSeq, int pos, SeqDataType x)
{
	assert(pSeq);
	assert((pos >= 0) && pos <= pSeq->size);
	SeqListCheck(pSeq);
	int end = pSeq->size - 1;
	while (end >= pos)
	{
		pSeq->a[end+1] = pSeq->a[end];
		end--;
	}
	pSeq->a[pos] = x;
	pSeq->size++;
}

 1.4.10 任意位置删除数据

        我们想要删除位置2上的元素,必然会将后面的元素向前移动,思路还是一样,从后往前移动会有数据覆盖的问题,所以这里从前往后向前移动,从pos开始后面的数据向前移动,一直移动到最后一个元素向前移动完。

void SeqListErase(SeqList* pSeq, int pos)
{
	assert(pSeq);
	assert(pos >= 0 && pos <= pSeq->size);
	int begin = pos;
	while (begin < pSeq->size - 1)
	{
		pSeq->a[begin] = pSeq->a[begin + 1];
		begin++;
	}
	pSeq->size--;
}

1.4.11 修改数据

void SeqListModify(SeqList* pSeq, int pos, SeqDataType x)
{
	assert(pSeq);
	assert(pos >= 0 && pos <= pSeq->size);
	pSeq->a[pos] = x;
}

1.4.12 查找元素

int SeqListQuery(SeqList* pSeq, SeqDataType x)
{
	assert(pSeq);
	for (int i = 0; i < pSeq->size; i++)
	{
		if (x == pSeq->a[i])
		{
			return i;
		}
	}
	return -1;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值