数据结构学习笔记(二)线性表及其C++实现

这篇博客详细介绍了线性表的基本概念,包括其定义、操作、存储结构,并提供了线性表在C++中的顺序存储和链式存储实现。线性表由相同类型元素的有限序列组成,支持多种操作如添加、删除、获取元素等。存储结构包括数组表示的顺序存储和使用节点及指针的链式存储。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、线性表的定义

     线性表是最基本、最简单的数据结构。它是零个或多个数据元素的有限序列。由相同数据类型的n(n≥0)个元素组成的有限序列,一般表示为L=(a1, a2, ..., ai, ai+1, ..., an)。数据元素的个数n定义为表的长度。当n=0时称为空表。 

      它强调:

  • 表中的元素个数有限;
  • 表中的元素具有逻辑上的顺序性
  • 集合中必存在唯一的一个“第一元素”,称为表头;
  • 集合中必存在唯一的一个 “最后元素” ,称为表尾;
  • 除最后一个元素之外,均有 唯一的后继;
  • 除第一个元素之外,均有 唯一的前驱。


二、线性表的操作

  • 计算线性表的长度;
  • 判断线性表是否为空;
  • 创建一个空表;
  • 往线性表中添加元素;
  • 删除线性表的某一个元素;
  • 获取第i个元素;
  • 清空线性表;
  • 显示整个线性表。
三、线性表的存储结构

  • 顺序存储结构:用一个数组进行描述
  • 链式存储结构:每一个节点包含指针域和数据域,指针域指向下一个节点的地址。

  除了图中的单向链表,还有双向链表,循环链表,静态链表(用数组实现指针功能)等等。


   其实一般数据结构的物理存储方式也都是这两类,以后再学习和使用时要根据具体情况选择使用哪种存储方式。

四、线性表的c++语言实现
//线性表的抽象基类,函数均为纯虚函数
#pragma once
#include<iostream>
using namespace std;

template<class T>
class linerList
{
public:
	virtual ~linerList() {};
	virtual bool empty() const = 0; //判断线性表是否为空,若为空返回true
	virtual int size() const = 0; //返回线性表的元素个数
	virtual T & get(int theIndex) const = 0; //返回索引为theIndex的元素
	virtual int index0f(const T & theElement) const = 0; //返回元素theElement第一次出现的索引
	virtual void erase(int theIndex) = 0; //删除索引为theIndex的元素
	virtual void insert(int theIndex, const T & theElement) = 0; // 把theElement插入到索引为theIndex的位置上
	virtual void output(ostream & out) const = 0; //输出元素
};

线性表的顺序存储结构如下:

//类arrayList的定义和实现
#pragma once
#include<iostream>
#include<sstream>
#include<stdlib.h>
#include"linerList.h"
#include <algorithm> 
#include<string>
#include<iterator>
using namespace std;

//类arrayList的定义
template<class T>
class arrayList:public linerList<T>
{
public:
	//构造函数,复制构造函数和析构函数
	arrayList(int initialCapacity = 10);
	arrayList(const arrayList<T>&);
	~arrayList() { delete[] element; }
	
	//ADT方法
	bool empty() const { return listSize == 0; }
	int size() const { return listSize; }
    T & get(int theIndex) const ;
	int index0f(const T & theElement) const ;
	void erase(int theIndex);
	void insert(int theIndex, const T & theElement);
	void output(ostream & out) const;

	//其他方法
	int capacity() const { return arrayLength; }//返回数组的长度,即线性表的容量
	
protected:
	void checkIndex(int theIndex) const; //检测索引正确性,若索引无效抛出异常

	T* element; // 一维数组存储线性表
	int arrayLength;//一维数组的长度
	int listSize;//线性表的元素个数
};


//构造函数创建一个长度为initialCapacity的数组,缺省值为10
template<class T>
arrayList<T>::arrayList(int initialCapacity)
{
	if (initialCapacity < 1)//如果initialCapacity不正确,抛出异常
	{
		cerr<< "Initial Capacity=" << initialCapacity << "must be>0";
		exit(0);
	}
	arrayLength = initialCapacity;
	element = new T[arrayLength];//创建数组
	listSize = 0;
}

//拷贝构造函数
template<class T>
arrayList<T>::arrayList(const arrayList<T>& theList)
{
	arrayLength = theList.arrayLength;
	listSize = theList.listSize;
	element = new T[arrayLength];
	copy(theList.element, theList.element + listSize, element);//拷贝数组元素,利用STL中copy算法
}

//确定索引在0和listSize-1之间,
template<class T>
void arrayList<T>::checkIndex(int theIndex) const  
{
	if (theIndex < 0 || theIndex >= listSize)
	{
		cerr<< "index:"<<theIndex<<" must be > 0 and <= " << listSize;
		exit(0);
	}
}

//返回索引值为theIndex的元素,如果索引不存在抛出异常
template<class T>
T& arrayList<T>::get(int theIndex)const
{
	checkIndex(theIndex);
	return element[theIndex];
}

//返回theElement第一次出现时的索引,如果元素不存在返回-1
template < class T>
int arrayList<T>::index0f(const T& theElement) const
{
	int theIndex = (int)(find(element, element + listSize, theElement) - element);//先查找,利用STl中的find算法

	if (theIndex == listSize)
		return -1;
	else
		return theIndex;
}

// 删除索引theIndex的元素
template<class T>
void arrayList<T>::erase(int theIndex)
{
	checkIndex(theIndex);
	copy(element + theIndex + 1, element + listSize, element + theIndex);//索引有效的情况下。移动大于索引的元素
	element[--listSize].~T();//调用析构函数
}

//插入操作
template<class T>
void arrayList<T>::insert(int theIndex, const T& theElement)
{
	if (theIndex<0 || theIndex>listSize)//无效索引抛出异常
	{
		cerr << "index:" << theIndex << " must be > 0 and <= " << listSize;
		exit(0);
	}
	if (listSize == arrayLength)//数组已满,扩充数组
	{
		addLength1D(element, arrayLength, 2 * arrayLength);
		arrayLength *= 2;
	}
	copy_backward(element + theIndex, element + listSize, element + listSize + 1);//将元素右移一个位置
	element[theIndex] = theElement;
	listSize++;
}

//输出,为什么要这样,有什么优点呢?
template<class T>
void arrayList<T>::output(ostream& out)const //把线性表插入输入输出流
{
	copy(element, element + listSize, ostream_iterator<T>(out, " "));
}
template<class T>
ostream& operator<<(ostream & out, const arrayList<T>& x)//函数重载
{
	x.output(out);
	return out;
}


//当数组长度不够时,通过该函数扩展存储空间
template<class T>
void addLength1D(T*& a, int oldLength, int newLength)
{
	if (newLength < 0)
		cerr<<"new length must be >=0";
	T *temp = new T[newLength];  //新数组
	int number = min(oldLength, newLength); //需要复制的元素个数
	copy(a, a + number, temp); //复制元素
	delete[] a;   //释放旧的元素内存空间
	a = temp;
}

测试代码:


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

void main()
{
	//arrayList<double> listwrong(0);  错误,抛出异常
	arrayList<double> list(10);
	cout << list.capacity();
	cout << endl;
	list.insert(0, 0);
	list.insert(1, 1);
	list.insert(2, 2);
	list.insert(3, 3);
	list.insert(4, 4);
	list.insert(5, 5);
	list.insert(6, 6);
	list.insert(7, 7);
	cout << list;
	cout << endl;
	list.insert(4, 100);
	cout << list;
	cout << endl;
	list.erase(2);
	cout << list;
	cout << endl;
	cout<<list.get(3)<<endl;
	cout<<list.index0f(8)<<endl;
	//list.insert(10, 7);

}


线性表的链式存储结构如下:


//类chain的定义和实现
#pragma once
#include<iostream>
#include<sstream>
#include<stdlib.h>
#include"linerList.h"
#include <algorithm> 
#include<string>
#include<iterator>
using namespace std;

//链表节点的结构表示
template<class T>
struct chainNode
{
	T element;//数据域
	chainNode<T> *next;//指针域

	chainNode() {}//默认构造函数
	chainNode(const T& element)	{  this->element = element;	}//构造函数
	chainNode(const T& element, chainNode<T>* next) 
	{ 
		this->element = element;
		this->next = next;
	}
};

template<class T>
class chain :public linerList<T>
{
public:
	//构造函数,复制构造函数和析构函数
	chain(int initialCapacity=10);
	chain(const chain<T>&);
	~chain();

	//抽象数据类型的ADT方法
	bool empty() const { return listSize == 0; }; //判断线性表是否为空,若为空返回true
	int size() const { return listSize; }; //返回线性表的元素个数
	T & get(int theIndex) const ; //返回索引为theIndex的元素
	int index0f(const T& theElement) const; //返回元素theElement第一次出现的索引
	void erase(int theIndex); //删除索引为theIndex的元素
	void insert(int theIndex, const T & theElement); // 把theElement插入到索引为theIndex的位置上
	void output(ostream& out) const ; //输出元素

	//其他方法
	void clear();//清空表

protected:
	void checkIndex(int theIndex) const;//如果索引无效,抛出异常
	chainNode<T>* firstNode;//指向链表的第一个节点的指针
	int listSize;//线性表中元素个数
};

//构造函数,创建一个空的链表,这里的预定义容量不是必须的,只是为了与arrayList兼容
template<class T>
chain<T>::chain(int initialCapacity)
{
	if (initialCapacity < 1)
	{
		cerr << "Initial Capacity=" << initialCapacity << "must be>0";
		exit(0);
	}
	firstNode = NULL;
	listSize = 0;
}

//复制构造函数
template<class T>
chain<T>::chain(const chain<T>& theList)
{
	listSize = theList.listSize;
	//链表为空时
	if (listSize==0)
	{
		firstNode = NULL;
		return;
	}
	//链表非空时
	chainNode<T>* sourceNode = theList.firstNode;
	firstNode = new chainNode<T>(sourceNode->element);//复制链表theList的首元素
	sourceNode = sourceNode->next;
	chainNode<T>* targetNode = firstNode;
	while (sourceNode!=NULL)
	{ 
		targetNode->next = new chainNode<T>(sourceNode->element);
		targetNode = targetNode->next;
		sourceNode = sourceNode->next;
	}
	targetNode->next = NULL;
}

//析构函数,删除所有节点,最后firstNode为NULL
template<class T>
chain<T>::~chain()
{
	while (firstNode != NULL)
	{
		chainNode<T> * nextNode = firstNode->next;
		delete firstNode;
		firstNode = nextNode;
	}
}

//找到索引为theIndex的元素
template<class T>
T& chain<T>::get(int theIndex) const
{
	checkIndex(theIndex);
	chainNode<T>* currentNode = firstNode;//使用currentNode作为指示器寻找元素
	for (int i = 0; i < theIndex; i++)
		currentNode = currentNode->next;
	return currentNode->element;
}

//返回元素theElement首次出现的索引,没找到返回-1
template<class T>
int chain<T>::index0f(const T& theElement) const
{
	chainNode<T>* currentNode = firstNode;//使用currentNode作为指示器
	int index = 0;
	while (currentNode != NULL && currentNode->element != theElement)
	{
		currentNode = currentNode->next;
		index++;
	}
	if (currentNode == NULL)//确定是否找到并返回
		return -1;
	else
		return index;
}

//删除索引为theIndex的元素
template<class T>
void chain<T>::erase(int theIndex)
{
	checkIndex(theIndex);//索引无效的情况
	//索引有效
	chainNode<T> * deleteNode;
	if (theIndex == 0)//如果在首位置,删除首节点
	{
		deleteNode = firstNode;
		firstNode = firstNode->next;
	}
	else//找到位置后删除节点,p指向删除节点的前驱
	{
		chainNode<T>* p = firstNode;
		for (int i = 0; i < theIndex; i++)
			p = p->next;
		deleteNode = p->next;
		p->next = p->next->next;//删除指针
	}
	listSize--;
	delete deleteNode;
}

//插入元素theElement并使其索引为theIndex
template<class T>
void chain<T>::insert(int theIndex, const T& theElement)
{
	if (theIndex<0 || theIndex>listSize)
	{
		cerr << "index:" << theIndex << " must be > 0 and <= " << listSize;
		exit(0);
	}
	if (theIndex == 0)//在表头插入
	{
		firstNode = new chainNode<T>(theElement, firstNode);
	}
	else//先找到位置,p为指示器在p后面插入
	{
		chainNode<T>* p = firstNode;
		for (int i = 0; i < theIndex - 1; i++)
			p = p->next;
		p->next = new chainNode<T>(theElement, p->next);//在p后插入
	}
	listSize++;
}

//输出链表
template<class T>
void chain<T>::output(ostream &out)const
{
	for (chainNode<T>* currentNode = firstNode; currentNode != NULL; currentNode = currentNode->next)
		out << currentNode->element << " ";
}
template<class T>
ostream & operator<<(ostream & out, const chain<T>& x)
{
	x.output(out);
	return out;
}

//确定索引在0和listSize-1之间,
template<class T>
void chain<T>::checkIndex(int theIndex) const
{
	if (theIndex < 0 || theIndex >= listSize)
	{
		cerr << "index:" << theIndex << " must be > 0 and <= " << listSize;
		exit(0);
	}
}

//清表
template<class T>
void chain<T>::clear()
{
	while (firstNode != NULL)
	{
		chainNode<T>* nextNode = firstNode->next;
		delete firstNode;
		firstNode = nextNode;	
	}
	listSize = 0;
}

测试代码:


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

void main()
{
	chain<int> list(10);
	for (int i = 0; i < 9; i++)
		list.insert(i,i);
	cout << list << endl;
	list.insert(4, 100);
	cout << list;
	cout << endl;
	list.erase(2);
	cout << list << endl;
	cout<<list.get(3)<<endl;
	cout<<list.index0f(8)<<endl;
	cout << list.empty() << endl;
	list.clear();
	cout << list << "        " << list.empty() << endl;
}




内含资源如下: 1.基本数据结构 1.1.Array ........... 动态数组 1.2.LinkedList ... 链表 1.3.BST .............. 分搜索树 1.4.MapBST ..... 分搜索树(用于实现映射) 1.5.AVLTree ...... AVL树 2.接口 2.1.Queue ........... 队列接口 2.2.Stack .............. 栈接口 2.3.Set .................. 集合接口 2.4.Map ............... 映射接口 2.5.Merger .......... 自定义函数接口 2.6.UnionFind ..... 并查集接口 3.高级数据结构 3.1.ArrayQueue .......................... 队列_基于动态数组实现 3.2.LinkedListQueue .................. 队列__基于链表实现 3.3.LoopQueue ........................... 循环队列_基于动态数组实现 3.4.PriorityQueue ....................... 优先队列_基于最大叉堆实现 3.5.ArrayPriorityQueue ............. 优先队列_基于动态数组实现 3.6.LinkedListPriorityQueue ..... 优先队列_基于链表实现 3.7.ArrayStack ............................. 栈_基于动态数组实现 3.8.LinkedListStack ..................... 栈_基于链表实现 3.9.BSTSet ..................................... 集合_基于分搜索树实现 3.10.LinkedListSet ....................... 集合_基于链表实现 3.11.BSTMap ................................ 映射_基于分搜索树实现 3.12.AVLTreeMap ....................... 映射_ 基于AVL树实现 3.13.LinkedListMap .................... 映射_基于链表实现 3.14.MaxHeap ............................. 最大叉堆 3.15.SegmentTree ...................... 线段树 3.16.Trie ......................................... 字典树 3.17.QuickFind ............................ 并查集_基于数组实现 3.18.QuickUnion ......................... 并查集_基于树思想实现
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值