1. 线性表
-
线性表是n个具有相同特性的数组元素的有序序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表有:顺序表、链表、栈、队列、字符串。
-
线性表在逻辑上是线性结构,但物理结构上并不一定是连续的,线性表在物理上储存时,通常以数组和链式结构的形式储存。
2. 顺序表的介绍
2.1概念与结构
- 概念:顺序表是用一段物理地址连续的存储单元一次存储数据元素的线性结构,顺序表的底层逻辑其实也就是数组,所以顺序表的逻辑上是线性的,物理结构上也是线性的。
- 顺序表和数组的区别:顺序表其实就是数组的升级版,在顺序表里面可以对数组进行插入数据、头删(从数组头部删除)和尾删(从数组尾部删除)、在指定位置插入数据、删除指定位置的数据等等,顺序表==数组plus。
3. 顺序表的实现
1. 创建文件以及实现初始化和销毁
- 首先我们要创建头文件Seqlist.h(之所以叫做Seqlist,原因是顺序表的英文就是Sequence list)作用是进行函数声明,然后创建Seqlist.cpp()进行函数实现和test.cpp用来测试代码。
- 然后创建一个类名为SeqList,把成员变量*_arr, _capacity(数组容量),以及_size(数组大小)放在私有。
- 再通过构造函数(缺省构造函数)进行顺序表的初始化,然后使用析构函数对顺序表进行销毁;代码如下:
#pragma once #include<iostream> #include<assert.h> using namespace std; typedef int SLDateType; class SeqList { public: //初始化顺序表 SeqList(int n = 4) { _arr = (SLDateType*)malloc(sizeof(SLDateType) * n); if (nullptr == _arr) { perror("malloc fail!"); exit(1); } _capacity = n; _size = 0; } //打印顺序表 void SLPrint(); //检查空间是否足够 void SLCheckCapacity(); //头插入数据和尾插入 void SLPushFront(SLDateType x); void SLPushBack(SLDateType x); //头删除和尾删除 void SLPopBack(); void SLPopFront(); //在指定位置之前插入数据 void SLInsert(SLDateType x, int pos); //删除指定位置的数据 void SLErase(int pos); //销毁动态创建的顺序表 ~SeqList() { if (_arr) { free(_arr); _arr = nullptr; } _capacity = 0; _size = 0; } private: SLDateType* _arr; int _capacity; int _size; };
一般初始化和销毁都在声明类内实现(个人习惯);代码的解释:首先是malloc动态申请一块大小为4的内存给_arr;判断_arr是否为空,因为会出现申请空间失败的情况,但一般不会,加上去只是提升代码的健壮性;然后让数组的容量为n(也就是4,因为前面申请了4个SLDateType类型的空间),然后让_size=0 因为数组里面还没有元素;后面就是析构函数销毁顺序表。
2. 实现顺序表的打印
- 其实也就是从下标为0开始打印数组元素;代码如下:
#include"Seqlist.h" void SeqList::SLPrint() { for (int i = 0; i < _size; i++) { cout << _arr[i] << " "; } cout << endl; }
3.实现顺序表的空间检查
-
增加数据的时候我们要判断顺序表内的空间是否充足;因为一开始我们初始化的时候就给了4个SLDateType类型的空间,如果说顺序表内已经有4个了,则需要动态开辟空间给顺序表实现增容;
-
之所以要实现动态的顺序表是因为减少空间浪费,比如说你只要用4个SLDateType类型的空间,但开辟了1000个,那就造成了空间浪费;而动态开辟空间进行增容(补充说明:增容也分两种情况,一种是内存的连续空间足够,直接扩容,另一种则是连续空间不够,首先重新找一块地址,分配足够的内存,然后拷贝数据到新的地址处,最后再销毁地址,这是增容时底层会实现的操作,不需要我们操作,只是补充说明一下)的大小一般规定是按原有空间的两倍;代码如下:
void SeqList::SLCheckCapacity() { //看他的_size是否等于_capacity; if (_size == _capacity) { int newcapacity = 2 * _capacity; SLDateType* temp = (SLDateType*)realloc(_arr, sizeof(SLDateType) * newcapacity); if (nullptr==temp) { perror("realloc fail!"); exit(1); } _arr = temp; _capacity = newcapacity; } }
判断_size(数组大小)和_capacity(数组容量)是否相等,相等则代表顺序表空间满了,需要进行增容操作;创建一个newcapacity = 2*_capacity,实现扩容,然后创建一个新的指针接收增容后的地址,注意这里要创建新的指针来接收原因是:使用_arr来接收,如果realloc增容失败的话,整个_arr为空,丢失数组,最后再让_arr指向扩容后的空间temp,让原有记录顺序表容量的_capacity等于扩容后的顺序表的容量;
4. 实现顺序表的尾插和头插
- 实现尾插思路:首先要判断_arr是否为空,再检查_arr的空间是否充足,然后让_arr[_size] = x,因为_size是数组的大小然后再让_size++,如下图和代码:
void SeqList::SLPushBack(SLDateType x) { assert(_arr); SLCheckCapacity(); _arr[_size] = x; _size++; }
5. 实现顺序表的尾删和头删
- 尾删思路:尾删其实很简单,只要让_size--就行;代码如下:
void SeqList::SLPopBack() { assert(_arr); //_size不能为0; assert(_size); _size--; }
6.实现指定位置插入数据
- 思路:其实和上面的头插很相似,只需要把指定位置的数据全部往后移一位,然后指定位置就空出来用来存储插入的数据即可。
//在指定位置之前插入数据 void SeqList::SLInsert(SLDateType x, int pos) { assert(_arr); assert(pos >= 0 && pos <= _size); SLCheckCapacity(); for (int i = _size; i > pos; i--) { _arr[i] = _arr[i - 1]; } _arr[pos] = x; _size++; }
7.实现指定位置删除数据
- 思路:也是同上面的头删很相似,不同于头删的是,需要把指定位置的数据往前移一位达到把指定位置的数据覆盖的效果;
- 注意:这里不仅要判断_arr是否为空、pos指定位置数据不能超过数组的大小、还有当数组大小为0的时候意味着没有数据所以也不能删除;代码如下:
//删除指定位置的数据 void SeqList::SLErase(int pos) { assert(_arr); assert(pos >= 0 && pos <= _size); assert(_size); for (int i = pos; i < _size; i++) { _arr[i] = _arr[i + 1]; } _size--; }
8. 顺序表实现的全部代码如下:
Seqlist.h:
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
typedef int SLDateType;
class SeqList
{
public:
//初始化顺序表
SeqList(int n = 4)
{
_arr = (SLDateType*)malloc(sizeof(SLDateType) * n);
if (nullptr == _arr)
{
perror("malloc fail!");
exit(1);
}
_capacity = n;
_size = 0;
}
//打印顺序表
void SLPrint();
//检查空间是否足够
void SLCheckCapacity();
//头插入数据和尾插入
void SLPushFront(SLDateType x);
void SLPushBack(SLDateType x);
//头删除和尾删除
void SLPopBack();
void SLPopFront();
//在指定位置之前插入数据
void SLInsert(SLDateType x, int pos);
//删除指定位置的数据
void SLErase(int pos);
//销毁动态创建的顺序表
~SeqList()
{
if (_arr)
{
free(_arr);
_arr = nullptr;
}
_capacity = 0;
_size = 0;
}
private:
SLDateType* _arr;
int _capacity;
int _size;
};
Seqlist.cpp:
#include"Seqlist.h"
void SeqList::SLPrint()
{
for (int i = 0; i < _size; i++)
{
cout << _arr[i] << " ";
}
cout << endl;
}
void SeqList::SLCheckCapacity()
{
//看他的_size是否等于_capacity;
if (_size == _capacity)
{
int newcapacity = 2 * _capacity;
SLDateType* temp = (SLDateType*)realloc(_arr, sizeof(SLDateType) * newcapacity);
if (nullptr==temp)
{
perror("realloc fail!");
exit(1);
}
_arr = temp;
_capacity = newcapacity;
}
}
void SeqList::SLPushBack(SLDateType x)
{
assert(_arr);
SLCheckCapacity();
_arr[_size] = x;
_size++;
}
void SeqList::SLPushFront( SLDateType x)
{
assert(_arr);
SLCheckCapacity();
for (int i = _size; i > 0; i--)
{
_arr[i] = _arr[i - 1];
}
_arr[0] = x;
_size++;
}
void SeqList::SLPopBack()
{
assert(_arr);
//_size不能为0;
assert(_size);
_size--;
}
void SeqList::SLPopFront()
{
assert(_arr);
assert(_size);
//直接往把第一位顶掉
for (int i = 0; i < _size; i++)
{
_arr[i] = _arr[i + 1];
}
_size--;
}
//在指定位置之前插入数据
void SeqList::SLInsert(SLDateType x, int pos)
{
assert(_arr);
assert(pos >= 0 && pos <= _size);
SLCheckCapacity();
for (int i = _size; i > pos; i--)
{
_arr[i] = _arr[i - 1];
}
_arr[pos] = x;
_size++;
}
//删除指定位置的数据
void SeqList::SLErase(int pos)
{
assert(_arr);
assert(pos >= 0 && pos <= _size);
assert(_size);
for (int i = pos; i < _size; i++)
{
_arr[i] = _arr[i + 1];
}
_size--;
}
test.cpp:
#include"Seqlist.h"
void test()
{
SeqList sq;
sq.SLPushBack(1);
sq.SLPushBack(2);
sq.SLPushBack(3);
sq.SLPushBack(4);
sq.SLPushBack(5);
sq.SLPushFront(11);
//sq.SLPopFront();
//sq.SLPopBack();
//sq.SLErase(1);
//sq.SLInsert(6, 6);
sq.SLPrint();
}
int main()
{
test();
return 0;
}
END!