附源码-数据结构线性表——动态顺序表详解-大学生必备!!!

本文详细介绍了动态顺序表的实现,包括Sqlist.h和Sqlist.cpp中的关键函数如初始化、扩容、插入、删除和查找操作,以及如何在测试程序中使用这些功能。
摘要由CSDN通过智能技术生成

你的点赞是对我最大的鼓励!!!

目录

1.顺序表

1.1线性表

2.顺序表的分类

2.1顺序表和数组的区别

2.2分类

3.动态顺序表的实现

3.1 Sqlist.h头文件的讲解

Sqlist.h源码:

3.1.1包含必要的头文件,以及对顺序表中元素类型进行重命名。

3.1.2 对结构体的讲解

3.2 Sqlist.cpp头文件的讲解

Sqlist.cpp源码:

3.2.1 init()函数的讲解,初始化顺序表

3.2.2 expand()函数的讲解,对顺序表扩容函数

3.2.3 pushfront()函数讲解(头插)

3.2.4 pushback()尾插

3.2.5 popback()尾删

3.2.6 popfront() 头删

3.2.7  SLinsert() 在指定位置插入元素

3.2.8 SLErase() 删除指定位置元素

 3.2.9 find() 查找元素

3.2.10 print() 打印顺序表

3.3 test.cpp 的讲解

测试:

总结:        怎么样?看了我的讲解是不是茅塞顿开了?


1.顺序表

1.1线性表

        线性表是n个具有相同特征的数据元素的有限序列。常见的线性表:顺序表,链表,栈,队列,字符串等等……

        线性表在逻辑上是线性结构,也就是说是一条连续的直线。但是在物理结构上并不一定是连续的,线性表在物理上存储时,通常以数组和链式结构的形式存储。

2.顺序表的分类

2.1顺序表和数组的区别

        顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等功能。

2.2分类

        静态顺序表:使用定长数组存储元素。存在很大缺陷,不够灵活,空间给少了不够用,给多了造成空间浪费。

        动态顺序表:可自动增容,使用realloc等函数对内存进行动态管理,按需分配空间。

        realloc函数是对已有空间扩容,顺序表用

        malloc函数是申请新的空间,链表用

3.动态顺序表的实现

        采用头文件.h与.cpp文件分离的方法

如图:

      

3.1 Sqlist.h头文件的讲解

                                                                Sqlist.h源码
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<iostream>
using namespace std;

typedef int SLdatatype;

struct Seqlist
{
	SLdatatype* arr;
	int size;
	int capacity;
};

typedef struct Seqlist SL;

void init(SL* ps);//动态顺序表的初始化
void destory(SL* ps);//动态顺序表的销毁
void pushfront(SL* ps, SLdatatype x);//动态顺序表的头插
void pushback(SL* ps, SLdatatype x);//动态顺序表的尾插
void popback(SL* ps);//动态顺序表的尾删
void popfront(SL* ps);//动态顺序表的头删
void expand(SL* ps);//动态顺序表的扩容
void SLinsert(SL* ps, int pos, SLdatatype x);//在顺序表指定位置插入元素
void SLErase(SL* ps, int pos);//在顺序表指定位置删除元素
int find(SL* ps, SLdatatype x);//在顺序表中查找元素

void print(SL* ps);//打印顺序表

3.1.1包含必要的头文件,以及对顺序表中元素类型进行重命名。

3.1.2 对结构体的讲解
struct Seqlist
{
	SLdatatype* arr;
	int size;
	int capacity;
};


typedef struct Seqlist SL;

        首先,我们要定义一个arr指针,用来存储空间,刚定义好后arr没有空间,我们用realloc函数就是对arr来分配空间。

int size;

表示当前顺序表中有几个元素。

int capacity;

表示当前顺序表的容量。

当size与capacity相等时,相当于顺序表满了,此时需要用realloc来扩容

3.2 Sqlist.cpp头文件的讲解

                                                                Sqlist.cpp源码
#include"Sqlist.h"

void expand(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcap = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLdatatype* tmp = (SLdatatype*)realloc(ps->arr,2 * newcap * sizeof(SLdatatype));
		if (tmp == NULL)
		{
			perror("realloc");
			return;
		}

		ps->capacity = newcap;
		ps->arr = tmp;
	}
}

void init(SL* ps)
{
	ps->arr = NULL;
	ps->capacity = ps->size = 0;
}
void destory(SL* ps)
{
	free(ps);
	ps->arr = NULL;
	ps->capacity = 0;
	ps->size = 0;
}
void pushfront(SL* ps, SLdatatype x)
{
	expand(ps);
	for (int i = ps->size; i-1 >= 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[0] = x;
	ps->size++;
}
void pushback(SL* ps, SLdatatype x)
{
	expand(ps);

	ps->arr[ps->size++] = x;
}
void popback(SL* ps)
{
	assert(ps);
	assert(ps->size);

	ps->size--;
}
void popfront(SL* ps)
{
	assert(ps);
	assert(ps->size);

	for (int i = 0; i+1 < ps->size; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

void SLinsert(SL* ps, int pos, SLdatatype x)
{

	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	expand(ps);
	for (int i = ps->size; i-1>= pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(ps->size);
	if (pos >= ps->size || pos < 0)
	{
		printf("pos is error\n");
	}
	for (int i = pos; i < ps->size; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

int find(SL* ps, SLdatatype x)
{
	assert(ps);
	assert(ps->size);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
		{
			return i;
		}
	}

	return -1;
}


void print(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		cout << ps->arr[i] << ' ';
	}
	cout << endl;
	
}

3.2.1 init()函数的讲解,初始化顺序表

声明:

void init(SL* ps);

定义:

void init(SL* ps)
{
	ps->arr = NULL;
	ps->capacity = ps->size = 0;
}

初始化函数中要将arr初始化为空指针NULL,并且把容量和元素个数都赋值为0。

3.2.2 expand()函数的讲解,对顺序表扩容函数

        我们对顺序表扩容,一般以二倍来扩容,以capacity的二倍来扩容。经过数学可推导出二倍扩容是最高效的,空间利用最合理。

声明:

void expand(SL* ps);

 定义:

void expand(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcap = ps->capacity == 0 ? 4 : 2 * ps->capacity;
		SLdatatype* tmp = (SLdatatype*)realloc(ps->arr,2 * newcap * sizeof(SLdatatype));
		if (tmp == NULL)
		{
			perror("realloc");
			return;
		}

		ps->capacity = newcap;
		ps->arr = tmp;
	}
}

(1).首先要判断顺序表是否已经满了,判断size与capacity是否一样即可。

(2).当最开始时,size与capicty都是0,所以当我们要插入元素时,我们要扩容,因为要以capacity的二倍来扩容,而capacity此时为0,0乘任何数都是0,所以我们此时要引入一个新变量newcap,用三目表达式,当capacity为0时,newcap为2,当capacity不为0是,newcap=2*capacity。

(3).因为realloc是对空间的扩容,realloc 会在原有内存的后面继续扩容,当扩容失败时,就会导致我们之前存储的数据全部丢失,所以我们又要引入一个指针变量 SLdatatype* tmp 先存储下扩容后的空间,然后在判断realloc 是否为NULL,当为NULL是,意味着扩容失败,报错然后返回空。当扩容成功,我们再把tmp赋值给arr,这样即使扩容失败也不会丢失arr内的数据。

3.2.3 pushfront()函数讲解(头插)

声明:

void pushfront(SL* ps, SLdatatype x);

定义:

void pushfront(SL* ps, SLdatatype x)
{
	expand(ps);
	for (int i = ps->size; i-1 >= 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[0] = x;
	ps->size++;
}

(1).首先要对顺序表进行判断,是否需要扩容,我们直接调用expand()函数,expand()函数内部会判断是否需要增容,如果size==capacity就扩容,否则就什么也不做。

(2).头插,就是在顺序表最前面插入一个数字,想象一下如果数组中你会怎么做? 所以我们可以把顺序表看作一个数组了,不就是要把全部元素向后整体挪动一位吗?

        序号为执行的先后顺序,先从顺序表最后一个开始挪动,一直到第一个,就会使的第一个空出来。 

(3).把x赋值给顺序表第一个位置,然后再将size++

3.2.4 pushback()尾插

声明:

void pushback(SL* ps, SLdatatype x);

定义:

void pushback(SL* ps, SLdatatype x)
{
	expand(ps);

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

 我们会发现,尾插更加简单,直接按数组来。

(1). 由于size此时指向最后一个元素之后的空位置,所以直接把 x 赋值给arr[size],然后再将size++,等价于 arr[size++]=x;

3.2.5 popback()尾删

声明:

void popback(SL* ps);

定义:

void popback(SL* ps)
{
	assert(ps);
	assert(ps->size);

	ps->size--;
}

(1).首先断言,要想删除元素,顺序表中必须要有元素,且顺序表不能为空指针。assert(ps)如果ps为NULL,则报错;assert(ps->size)如果size为0,则报错。

(2).通过两个断言后,顺序表一定有元素且不为空指针,则只需将size--即可,因为当我们插入元素时,新的元素会覆盖旧的元素,无需再将尾删的这个元素赋值为0或者其他值。

3.2.6 popfront() 头删

声明:

void popfront(SL* ps);

定义:

void popfront(SL* ps)
{
	assert(ps);
	assert(ps->size);

	for (int i = 0; i+1 < ps->size; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

(1).首先,老规矩,我们要进行断言,要想删除元素,顺序表中必须要有元素,且顺序表不能为空指针,与尾删道理一样。

(2).想要删除顺序表中第一个元素,再次联想一下数组中,在数组中你会怎么做?不就是将整体向前挪动一位吗,最前面的一位就会被挤出去,就达到了删除第一个元素的目的了。

(3).遍历完顺序表后,一定要记得将size--。

3.2.7  SLinsert() 在指定位置插入元素

声明:

void SLinsert(SL* ps, int pos, SLdatatype x);

定义:

void SLinsert(SL* ps, int pos, SLdatatype x)
{

	assert(ps);
	assert(pos >= 0 && pos <= ps->size);
	expand(ps);
	for (int i = ps->size; i-1>= pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];
	}
	ps->arr[pos] = x;
	ps->size++;
}

(1).先判断顺序表是否为空指针,在判断pos(要插入的位置下标)是否大于等于0 并且 小于等于size。

(2).再判断是否需要扩容,我们直接调用expand()函数,expand()函数内部会判断是否需要增容,如果size==capacity就扩容,否则就什么也不做。

(3).我们想要在pos位置插入元素,则只需将pos及pos之后的元素,整体向后挪动一位,将pos位置空出来即可。

(4).然后将pos位置赋值为x,记得要将size++。

3.2.8 SLErase() 删除指定位置元素

声明:

void SLErase(SL* ps, int pos);

定义:

void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(ps->size);
	if (pos >= ps->size||pos<0)
	{
		printf("pos is error\n");
	}
	for (int i = pos; i < ps->size; i++)
	{
		ps->arr[i] = ps->arr[i + 1];
	}
	ps->size--;
}

(1).只要涉及到删除元素,就要判断顺序表是否为空,并且还要判断顺序表是否为空指针。

(2).在判断pos是否合法,pos必须大于等于0,并且小于size。

(3).要移除pos,只需将pos之后的整体向前移动一位。

(4).最后将size--。

 3.2.9 find() 查找元素

声明:

int find(SL* ps, SLdatatype x);

定义:

int find(SL* ps, SLdatatype x)
{
	assert(ps);
	assert(ps->size);
	for (int i = 0; i < ps->size; i++)
	{
		if (ps->arr[i] == x)
		{
			return i;
		}
	}

	return -1;
}

(1).先判断顺序表是否为空,或者为空指针。

(2).从0到size遍历顺序表即可,等于x就返回下标,如果循环结束还没有返回,则返回-1。

3.2.10 print() 打印顺序表

声明:

void print(SL* ps);

定义:

void print(SL* ps)
{
	for (int i = 0; i < ps->size; i++)
	{
		cout << ps->arr[i] << ' ';
	}
	cout << endl;
	
}

(1).只需从头到尾遍历顺序表即可。

3.3 test.cpp 的讲解

包括了三个函数

(1).主函数

(2).测试函数

(3).菜单函数

                                                                test.cpp源码
#include"Sqlist.h"
SL ps;

void menu()
{
	printf("****************************\n");
	printf("***1.pushfront 2.pushback***\n");
	printf("***3.popback   4.popfront***\n");
	printf("***5.SLinsert   6.SLErase***\n");
	printf("***7.find          0.exit***\n");
	printf("***8.print               ***\n");
	printf("****************************\n");
}

void test()
{
	

	int op;
	
	int n;
	SLdatatype x;
	int pos;
	do 
	{
        menu();
	    printf("input option\n");
		cin >> op;
		switch (op)
		{
		case 1:
			printf("input pushfront number\n");
			cin >> n;
			printf("input member\n");
			for (int i = 0; i < n; i++)
			{
				cin >> x;
				pushfront(&ps, x);
			}
			break;
		case 2:
			printf("input pushback number\n");
			cin >> n;
			printf("input member\n");
			for (int i = 0; i < n; i++)
			{
				cin >> x;
				pushback(&ps, x);
			}
			break;
		case 3:
			popback(&ps);
			break;
		case 4:
			popfront(&ps);
			break;
		case 5:
			printf("input pos\n"); cin >> pos;
			printf("input x\n"); cin >> x;
			SLinsert(&ps, pos, x);
			break;
		case 6:
			printf("input pos\n"); cin >> pos;
			SLErase(&ps, pos);
			break;
		case 7:
			printf("input x\n"); cin >> x;
			find(&ps, x);
			break;
		case 8:
			print(&ps);
			break;
		case 0:
			break;
		default:
			printf("error option\n");
			break;
		}
	} while (op);

}

int main()
{
	init(&ps);
	
	test();
	return 0;
}

测试:

总结:
        怎么样?看了我的讲解是不是茅塞顿开了?

        顺序表有以下注意点:

                (1).每当删除元素时,一定要记得将size--。

                (2).每当增加元素时,一定要记得将size++。

                (3).删除元素时要先判断顺序表是否已经为空。

如果你喜欢我的博客,就点点小赞👍,谢谢各位大佬!!!

  • 24
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值