数据结构笔记一_线性表的顺序存储结构(C++)

本文详细介绍了线性表的逻辑结构、基本操作以及两种物理存储方式,重点讲解了顺序存储结构的实现,包括动态数组、插入、删除、遍历等操作,并通过示例展示了顺序表在C++中的实现。此外,还讨论了顺序表在性能和适用场景上的特点,以及在实际应用中如何处理空间不足的情况。
摘要由CSDN通过智能技术生成

线性表

一、逻辑结构及基本运算

1.线性表的概念

线性表是N个具有相同特征的节点 A 0 , A 1 , ⋯   , A N − 1 A_0,A_1, \cdots ,A_{N-1} A0,A1,,AN1构成的集合。在这个集合中,除了 A 0 A_0 A0 A N − 1 A_{N-1} AN1外,每个元素都有直接唯一的前驱和后继。

2. 表的基本操作:
  • 构造类:构造出表结构
  • 属性类:只观察不修改
  1. 求线性表的长度
  2. 搜索某个元素是否在线性表中出现
  3. 访问线性表的第i个元素
  • 数据操纵类:对数据进行改变
  1. 在第i个位置插入一个元素
  2. 删除第i个位置的元素
  3. 清除一个线性表
  • 遍历类:对每个元素进行访问且只访问一次
3.线性表的抽象类

由上述分析可以得到线性表的类模板定义即父类,如下。

template<class elemType>
class list {
public:
	//对于属性类的函数,因其并不改变原数组or链表的值,在后面加const
	//可以达到保护隐含this指针以及供常对象使用的目的
	virtual int length() const = 0;
	virtual int search(const elemType& x) const = 0;
	//const &结构:在数据类型未知or比较复杂时,可以提高运行效率,同时节省空间
	virtual elemType visit(int i) const = 0;

	//数据操纵类函数
	virtual void insert(int i, const elemType& x) = 0;
	virtual void remove(int i) = 0;
	virtual void clear() = 0;

	//遍历操作
	virtual void traverse()const = 0;

	//析构函数,防止内存泄露
	virtual ~list() {};
};

二、物理结构

物理结构即指数据结构的存储实现,包括以下两个部分:

  • 数据元素的存储
  • 数据元素之间的关系的存储

存储方式有两种:

  • 顺序储存:存放于连续空间,主要用数组实现
  • 链接存储:用指针显式指出元素之间的关系
线性表的顺序存储结构

采用动态数组实现,保存一个动态数组,需要三个变量:

  1. 指向数组的指针
  2. 数组规模(容量)
  3. 数组中实际元素的个数(表长)

存储映像图如下(重要!!):
在这里插入图片描述
顺序表类的定义(子类):

template<class elemType>
class seqList :public list<elemType>
{
private:
	elemType* data;//存储数组元素
	int currentLength;
	int maxSize;
	void doubleSpace();//扩大数组空间
public:
	seqList(int initSize = 10);//构造函数
	~seqList() { delete[]data; }//析构函数
	int length()const;
	int search(const elemType& x)const;
	elemType visit(int i)const;
	void insert(int i, const elemType& x);
	void remove(int i);
	void clear();
	void traverse()const;
};

三、 基本操作实现

  • 构造类
  1. 构造出表结构
//构造函数的实现
//注意在这里参数不能写成initSize=10(只需在声明时说明)
template<class elemType>
seqList<elemType>::seqList(int initSize) {
	data = new elemType[initSize];
	if (!data)throw illegalSize();//异常处理
	maxSize = initSize;
	currentLength = 0;
}
  1. 当空间不足时,扩大容量
    在这里插入图片描述
template<class elemType>
void seqList<elemType>::doubleSpace() {
	elemType* tmp = data;
	maxSize *= 2;
	data = new elemType[maxSize];
	for (int i = 0; i < maxSize; ++i) {
		data[i] = tmp[i];
	}
	delete[]tmp;
}
  • 属性类:只观察不修改
  1. 求线性表的长度
//数组长度
//注意声明时+const,定义时也要加
template<class elemType>
int seqList<elemType>::length()const {
   return currentLength;
}
  1. 搜索某个元素是否在线性表中出现
//查找某个元素是否在表内并返回编号
template<class elemType>
int seqList<elemType>::search(const elemType& x)const {
   int i;
   for (i = 0; i < currentLength; ++i) {
   	if (data[i] == x) break;
   }
   if (i == currentLength)  return -1;//已经找遍了整个序列仍没有找到
   else return i;
}
  1. 访问线性表的第i个元素
//访问线性表中下标为i的元素
template<class elemType>
elemType seqList<elemType>::visit(int i)const {
   return data[i];
}
  • 数据操纵类:对数据进行改变
  1. 在第i个位置插入一个元素
//插入一个元素,使其下标为i
template<class elemType>
void seqList<elemType>::insert(int i, const elemType& x) {
   if (currentLength == maxSize) doubleSpace();
   for (int j = currentLength ; j > i; --j) {
   	data[j] = data[j - 1];
   }
   data[i] = x;
   ++currentLength;
}
  1. 删除第i个位置的元素
    在这里插入图片描述
//移除下标为i的元素
template<class elemType>
void seqList<elemType>::remove(int i) {
   for (int k = i; k < currentLength - 1; ++k) {
   	data[k] = data[k + 1];
   }
   --currentLength;
}
  1. 清除一个线性表
//清除列表中所有元素
template<class elemType>
void seqList<elemType>::clear() {
   //delete[]data;
   currentLength = 0;
}
  • 遍历类:对每个元素进行访问且只访问一次
//遍历所有元素
template<class elemType>
void seqList<elemType>::traverse() const {
	for (int i = 0; i < currentLength; ++i) {
		cout << data[i] << " ";
	}
}

四、顺序表实现的总结

  • 顺序表在插入删除时需要移动大量的数据,性能并不理想
  • 事先需要估计空间,如果数据很大,则需要反复doubleSpace
  • 逻辑次序和物理次序的一致性使其定位访问性能很好
  • 较为适合静态的、经常做定位访问的线性表

五、seqList代码汇总

注: 有些编译器在处理类模板时,并不支持将函数的声明和定义分别写在头文件和.cpp文件中,会报错LNK2019,如下图:
LNK2019报错
解决方法可以将函数的声明和定义都写在头文件中,用main函数调用。

seqList列表实现代码清单如下:

  • 头文件 seqList.h
//文件名:seqList.h
#include<iostream>
using namespace std;

//父类:指定子类要实现什么功能
template<class elemType>
class list {
public:
	//对于属性类的函数,因其并不改变原数组or链表的值,在后面加const
	//可以达到保护隐含this指针以及供常对象使用的目的
	virtual int length() const = 0;
	virtual int search(const elemType& x) const = 0;
	//const &结构:在数据类型未知or比较复杂时,可以提高运行效率,同时节省空间
	virtual elemType visit(int i) const = 0;

	//数据操纵类函数
	virtual void insert(int i, const elemType& x) = 0;
	virtual void remove(int i) = 0;
	virtual void clear() = 0;

	//遍历操作
	virtual void traverse()const = 0;

	//析构函数,防止内存泄露
	virtual ~list() {};
};

//子类函数
template<class elemType>
class seqList :public list<elemType>
{
private:
	elemType* data;//存储数组元素
	int currentLength;
	int maxSize;
	void doubleSpace();//扩大数组空间
public:
	seqList(int initSize = 10);//构造函数
	~seqList() { delete[]data; }//析构函数
	int length()const;
	int search(const elemType& x)const;
	elemType visit(int i)const;
	void insert(int i, const elemType& x);
	void remove(int i);
	void clear();
	void traverse()const;
};

class illegalSize {};//异常处理



//扩大数组空间的实现
//申请一块更大的空间,将原来的数据复制进去,并释放原数组空间
template<class elemType>
void seqList<elemType>::doubleSpace() {
	elemType* tmp = data;
	maxSize *= 2;
	data = new elemType[maxSize];
	for (int i = 0; i < maxSize; ++i) {
		data[i] = tmp[i];
	}
	delete[]tmp;
}

//构造函数的实现
//注意在这里参数不能写成initSize=10(只需在声明时说明)
template<class elemType>
seqList<elemType>::seqList(int initSize) {
	data = new elemType[initSize];
	if (!data)throw illegalSize();//异常处理
	maxSize = initSize;
	currentLength = 0;
}

//数组长度
//注意声明时+const,定义时也要加
template<class elemType>
int seqList<elemType>::length()const {
	return currentLength;
}

//查找某个元素是否在表内并返回编号
template<class elemType>
int seqList<elemType>::search(const elemType& x)const {
	int i;
	for (i = 0; i < currentLength; ++i) {
		if (data[i] == x) break;
	}
	if (i == currentLength)  return -1;//已经找遍了整个序列仍没有找到
	else return i;
}

//访问线性表中下标为i的元素
template<class elemType>
elemType seqList<elemType>::visit(int i)const {
	return data[i];
}

//插入一个元素,使其下标为i
template<class elemType>
void seqList<elemType>::insert(int i, const elemType& x) {
	if (currentLength == maxSize) doubleSpace();
	for (int j = currentLength ; j > i; --j) {
		data[j] = data[j - 1];
	}
	data[i] = x;
	++currentLength;
}

//移除下标为i的元素
template<class elemType>
void seqList<elemType>::remove(int i) {
	for (int k = i; k < currentLength - 1; ++k) {
		data[k] = data[k + 1];
	}
	--currentLength;
}

//清除列表中所有元素
template<class elemType>
void seqList<elemType>::clear() {
	//delete[]data;
	currentLength = 0;
}

//遍历所有元素
template<class elemType>
void seqList<elemType>::traverse() const {
	for (int i = 0; i < currentLength; ++i) {
		cout << data[i] << " ";
	}
}
  • 调用文件(测试集) main函数
#include<iostream>
#include"seqList.h"
using namespace std;

int main() {
	//构造列表
	seqList<int> array(15);
	//插入数据,列表为0-14的顺序排列
	for (int i = 0; i < 15; ++i)
		array.insert(i, i);
	//遍历数据
	cout << "此时线性表中元素为:" << endl;
	array.traverse();
	cout << endl;
	cout << "列表长度为:" << array.length() << endl;
	//访问数据
	cout << "下标为1的数据为" << array.visit(1) << endl;
	//查找数据
	cout << "2在列表中的下标为" << array.search(2) << endl;
	cout << "15在列表中的下标为" << array.search(15) << endl;
	//移除数据
	array.remove(3);
	cout << "移除data[3]后线性表中元素为:" << endl;
	array.traverse();
	cout << endl;
	cout << "此时列表长度为:" << array.length() << endl;
	array.clear();
}

运行结果如下:
(实现了所有功能)
在这里插入图片描述

六、 顺序表的应用

例:求集合的交集

#include<iostream>
#include"seqlist.h"
using namespace std;

int main() {
	seqList<int> list1(10), list2(10), list3(10);
    int i = 0, j, x;
    int len1;

    //输入第一个集合中的元素
    cout << "请输入第一个集合中元素:(以0作为结束标志)";
    cin >> x;
    while (x != 0)
    {
        list1.insert(i, x);
        i++;
        cin >> x;
    }
    
    //输入第二个集合的元素
    i = 0;
    cout << "请输入第二个集合中元素:(以0作为结束标志)";
    cin >> x;
    while (x != 0)
    {
        list2.insert(i, x);
        i++;
        cin >> x;
    }

    //查找list1中的元素是否在list2中出现
    //如果出现则将其存入list3中
    len1 = list1.length();
    int num = 0;
    for (j = 0; j < len1; ++j) {
        int y = list1.visit(j);
        if (list2.search(y) != -1) {
            list3.insert(num, y);
            num++;
        }
    }

    //输出list3
    list3.traverse();
}

运行结果:
在这里插入图片描述

下期预告:线性表的链式存储结构

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值