C++线性表深度解析之动态数组与单链表和栈及队列的实现

目录
  • 一、线性表介绍
    • 线性表性质
  • 二、动态数组
    • 1)分析与设计
    • 2)实现
  • 三、单链表(企业设计方式)
    • 1)分析与设计
    • 2)实现
  • 四、栈(受限线性表)
    • 1)利用数组实现栈
    • 2)利用单链表实现栈
    • 3)栈的应用——就近匹配
      • 1.算法思想
      • 2.实现
  • 五、队列(受限线性表)
    • 1)队列的顺序存储
    • 2)利用单链表实现队列

数据结构大体可以分为两个部分:逻辑结构和物理结构。

物理结构大体也可以分为两个部分,即顺序结构和链式存储结构。

而线性结构就是逻辑结构中的一种。

一、线性表介绍

线性表是零个或多个数据元素组成的有限序列,数据元素之间有顺序,数据元素个数有限且类型必须相同。动态数组、链表、栈和队列都属于线性结构。

线性表性质

1.a[0]为线性表的第一个元素,只有一个后继。

2.a[n - 1]为线性表最后一个元素,只有一个前驱。

3.除a[0]和a[n - 1]之外的其他元素,既有前驱,又有后继。

4.线性表能够逐项访问和顺序存储。

二、动态数组

1)分析与设计

头文件DynamicArray.h

#pragma once
#include<cstring>
class dynamicArray
{
public:
	dynamicArray(int capcity);
	void insert(int pos, void* data);
	void push_back(void* data);
	void for_each(void(*MyPrint)(void*));//参数为函数指针,由用户提供
	void remove(int pos);
	void remove(void* value, bool(*MyCompare)(void*, void*));
	~dynamicArray();
private:
	void** m_pAdder;//维护真实开辟在堆区的指针
	int m_capacity;//容量
	int m_size;//当前大小
};

设计思路:

1.将二级指针、容量、大小等属性设为私有权限,避免用户直接调用。

2.提供按位置插入和尾插两种插入元素的方式。

3.利用函数重载remove实现按位置删除元素和按值删除元素。

4.提供遍历API,用数提供比较函数即可

5.返回容量、大小等API可根据自身需求考虑是否提供

2)实现

源文件DynamicArray.cpp

#include"DynamicArray.h"
dynamicArray::dynamicArray(int capacity)
{
	if (capacity <= 0)return;
	this->m_pAdder = (void**)new (void*[capacity]);
	if (this->m_pAdder == nullptr)return;
	this->m_capacity = capacity;
	this->m_size = 0;
}
void dynamicArray::insert(int pos, void* data)
{
	if (this->m_pAdder == nullptr || data == nullptr)return;
	if (pos<0 || pos>this->m_size)
	{
		pos = this->m_size;//若位置无效则进行尾插
	}
	//动态扩展
	if (this->m_size == this->m_capacity)
	{
		void** newSpace = (void**)new(void*[this->m_capacity * 2]);//每次扩展为原来的两倍
		if (newSpace == nullptr)return;
		memcpy(newSpace, this->m_pAdder, sizeof(void*) * this->m_size);
		delete[]this->m_pAdder;
		this->m_pAdder = newSpace;
		this->m_capacity *= 2;
	}
	//插入元素
	for (int i = this->m_size - 1; i >= pos; --i)//反向遍历
	{
		this->m_pAdder[i + 1] = this->m_pAdder[i];//看似越界实则并没有,m_capacity > m_size
	}
	this->m_pAdder[pos] = data;
	++this->m_size;
}
void dynamicArray::push_back(void* data)
{
	this->insert(this->m_size, data);
}
void dynamicArray::for_each(void(*MyPrint)(void*))
{
	if (this->m_pAdder == nullptr || MyPrint == nullptr)return;
	for (int i = 0; i < this->m_size ; ++i)
	{
		MyPrint(this->m_pAdder[i]);
	}
}
void dynamicArray::remove(int pos)
{
	if (pos<0 || pos>this->m_size - 1)return;
	for (int i = pos; i < this->m_size - 1; ++i)
	{
		this->m_pAdder[i] = this->m_pAdder[i + 1];
	}
	--this->m_size;
}
void dynamicArray::remove(void* value, bool(*MyCompare)(void*, void*))
{
	if (value == nullptr)return;
	for (int i = 0; i < this->m_size; ++i)
	{
		if (MyCompare(this->m_pAdder[i], value))
		{
			this->remove(i);
			--i;
		}
	}
}
dynamicArray:: ~dynamicArray()
{
	if (this->m_pAdder != nullptr)
	{
		delete[]this->m_pAdder;
		this->m_pAdder = nullptr;
	}
}

三、单链表(企业设计方式)

1)分析与设计

该设计方式与常见的一个数据域、一个指针域的设计方式并相同。

应与用户协定:使用该链表时,自定义数据类型预留4个字节的空间交予链表连接使用。

该方法本质上连接的是用户的数据,而传统版是一个个结点连接,插入时创建新结点并将用户数据拷贝进去。

头文件LinkList.h

#pragma once
#include<iostream>
using namespace std;
class LinkNode
{
	friend class LinkList;
private:
	LinkNode* next;
};
class LinkList
{
public:
	LinkList();
	void insert(int pos, void* data);
	void push_back(void* data);
	void for_each(void(*MyPrint)(void*));
	void remove(int pos);
	~LinkList();
private:
	LinkNode pHeader;
	int m_size;
};

2)实现

源文件LinkList.cpp

#include"LinkList.h"
LinkList::LinkList()
{
	this->pHeader.next = nullptr;
	this->m_size = 0;
}
void LinkList::insert(int pos, void* data)
{
	if (data == nullptr)return;
	if (pos<0 || pos>this->m_size)
	{
		pos = this->m_size;//无效位置变为尾插
	}
	LinkNode* NewNode = (LinkNode*)data;
	LinkNode* pCurrent = &(this->pHeader);
	for (int i = 0; i < pos; ++i)
	{
		pCurrent = pCurrent->next;//找到前驱结点
	}
	//变更指针指向
	NewNode->next = pCurrent->next;
	pCurrent->next = NewNode;
	++this->m_size;
}
void LinkList::push_back(void* data)
{
	this->insert(this->m_size, data);
}
void LinkList::for_each(void(*MyPrint)(void*))
{
	LinkNode* node = this->pHeader.next;
	for (int i = 0; i < this->m_size; ++i)
	{
		MyPrint(node);
		node = node->next;
	}
}
void LinkList::remove(int pos)
{
	if (pos<0 || pos>this->m_size - 1)return;
	LinkNode* pCurrent = &(this->pHeader);
	for (int i = 0; i < pos; ++i)
	{
		pCurrent = pCurrent->next;//找到前驱结点
	}
	LinkNode* pDel = pCurrent->next;
	pCurrent->next = pDel->next;
	--this->m_size;
}
LinkList::~LinkList()
{
	this->pHeader.next = nullptr;
	this->m_size = 0;
}

四、栈(受限线性表)

它的特殊之处在于限制了这个线性表的插入和删除的位置,它始终只在栈顶进行。

可分别使用数组和链表实现栈

1)利用数组实现栈

数组首地址做栈底。栈顶(数组尾部)频繁做出入栈和出栈操作。

对于数组尾部做插入和删除操作效率高。(无需移动其他元素)

头文件StackArray.h

#pragma once
#define MAX  1024
#include<iostream>
#include<cstring>
using namespace std;
class Stack
{
public:
	Stack();
	void top_back(void* data);//压栈
	void pop_back();//出栈
	void* top();//返回栈顶
	int size();
	bool isEmpty();
	~Stack();
private:
	void* data[MAX];//指针数组——栈数组
	int m_size;
};

源文件StackArray.cpp

#include"StackArray.h"
Stack::Stack()
{
	this->m_size = 0;
	memset(this->data, 0, sizeof(void*) * MAX);
}
void Stack::top_back(void* data)
{
	if (data == nullptr)return;
	this->data[this->m_size] = data;
	++this->m_size;
}
void Stack::pop_back()
{
	this->data[this->m_size - 1] = nullptr;
	--this->m_size;
}
void* Stack::top()
{
	if (this->m_size == 0)return nullptr;
	return this->data[this->m_size - 1];
}
int Stack::size()
{
	return this->m_size;
}
bool Stack::isEmpty()
{
	if (this->m_size == 0)return true;
	return false;
}
Stack::~Stack()
{
	this->m_size = 0;
	memset(this->data, 0, sizeof(void*) * MAX);
}

2)利用单链表实现栈

链表头做栈顶利于频繁地插入删除(无需通过遍历找到尾结点)

头文件StackLink.h

#pragma once
class StackNode
{
	friend class StackLink;
private:
	StackNode* next;
};
class StackLink
{
public:
	StackLink();
	void top_back(void* data);
	void pop_back();
	void* top();
	int size();
	bool isEmpty();
	~StackLink();
private:
	StackNode pHeader;
	int m_size;
};

源文件StackLink.cpp

#include"StackLink.h"
StackLink::StackLink()
{
	this->pHeader.next = nullptr;
	this->m_size = 0;
}
void StackLink::top_back(void* data)
{
	if (data == nullptr)return;
	StackNode* myNode = (StackNode*)data;
	myNode->next = this->pHeader.next;
	this->pHeader.next = myNode;
	++this->m_size;
}
void StackLink::pop_back()
{
	StackNode* pDel = this->pHeader.next;
	this->pHeader.next = pDel->next;
	--this->m_size;
}
void* StackLink::top()
{
	if (this->m_size == 0)return nullptr;
	return this->pHeader.next;
}
int StackLink::size()
{
	return this->m_size;
}
bool StackLink::isEmpty()
{
	if (this->m_size == 0)
	{
		return true;
	}
	return false;
}
StackLink::~StackLink()
{
	this->pHeader.next = nullptr;
	this->m_size = 0;
}

3)栈的应用——就近匹配

1.算法思想

从第一个字符开始扫描,当遇见普通字符时忽略。当遇见左括号时压入栈中,遇见右括号则弹出栈顶符号进行匹配。

匹配成功,继续识别下一字符。

匹配失败,立即停止,报错。

成功条件:所有字符扫描完且栈为空

失败条件:匹配失败或扫描完毕但栈非空

2.实现

#include<iostream>
#include<string>
#include"StackArray.h"
using namespace std;
bool isLeft(char ch)
{
	return ch == '(';
}
bool isRight(char ch)
{
	return ch == ')';
}
void printError(char* str, string errMsg, char* pos)
{
	cout << "错误信息:" << errMsg << endl;
	cout << str << endl;
	int num = pos - str;
	for (int i = 0; i < num; ++i)
	{
		cout << " ";
	}
	cout << "~" << endl;
}
int main()
{
	char* str = (char*)"5 + 5 * (6) + 9 / 3 * 1 - ( 1 + 310";
	char* p = str;
	Stack sk;
	while (*p != '\0')
	{
		if (isLeft(*p))
		{
			sk.top_back(p);
		}
		if (isRight(*p))
		{
			if (sk.size() > 0)
			{
				sk.pop_back();
			}
			else
			{
				printError(str, "右括号没有匹配到对应的左括号", p);
			}
		}
		++p;
	}
	while (sk.size() > 0)
	{
		printError(str, "左括号没有匹配到右括号", (char*)sk.top());
		sk.pop_back();
	}
	return 0;
}

五、队列(受限线性表)

只允许在一端进行插入操作,在另一端进行删除操作

可分别使用数组和链表实现队列

1)队列的顺序存储

数组的首地址做队头或队尾效率相同,本文不做详细介绍

2)利用单链表实现队列

头文件QueueLink.h

#pragma once
class QueueNode
{
	friend class QueueLink;
private:
	QueueNode* next;
};
class QueueLink
{
public:
	QueueLink();
	void push_QueueLink(void* data);
	void pop_QueueLink();
	int size();
	void* head();
	void* tail();
	bool isEmpty();
	~QueueLink();
private:
	QueueNode pHeader;
	QueueNode* pTail;//用于记录尾结点,不必通过遍历找到尾结点
	int m_size;
};

源文件QueueLink.cpp

#include"QueueLink.h"
QueueLink::QueueLink()
{
	this->m_size = 0;
	this->pHeader.next = nullptr;
	this->pTail = &this->pHeader;
}
void QueueLink::push_QueueLink(void* data)
{
	if (data == nullptr)return;
	QueueNode* myNode = (QueueNode*)data;
	this->pTail->next = myNode;
	myNode->next = nullptr;
	this->pTail = myNode;
	++this->m_size;
}
void QueueLink::pop_QueueLink()
{
	if (this->m_size == 0)return;
	if (this->m_size == 1)
	{
		this->pHeader.next = nullptr;
		this->pTail = &this->pHeader;
	}
	else
	{
		QueueNode* pDel = this->pHeader.next;
		this->pHeader.next = pDel->next;
	}
	--this->m_size;
}
int QueueLink::size()
{
	return this->m_size;
}
void* QueueLink::head()
{
	return this->pHeader.next;
}
void* QueueLink::tail()
{
	return this->pTail;
}
bool QueueLink::isEmpty()
{
	if (this->m_size == 0)return true;
	return false;
}
QueueLink::~QueueLink()
{
	this->m_size = 0;
	this->pHeader.next = nullptr;
	this->pTail = &this->pHeader;
}

到此这篇关于C++线性表深度解析之动态数组与单链表和栈及队列的实现的文章就介绍到这了,更多相关C++动态数组内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++超细致讲解队列queue的使用

    目录 queue介绍 queue常用函数 1.常用函数 2.函数运用示例 queue介绍 只能访问 queue<T> 容器适配器的第一个和最后一个元素.只能在容器的末尾添加新元素,只能从头部移除元素. 许多程序都使用了 queue 容器.queue 容器可以用来表示超市的结账队列或服务器上等待执行的数据库事务队列.对于任何需要用 FIFO 准则处理的序列来说,使用 queue 容器适配器都是好的选择. 调用#include< queue>即可使用队列类 queue<Type,

  • C/C++ 动态数组的创建的实例详解

    C/C++ 动态数组的创建的实例详解 在C++语言中,二维动态数组主要使用指针的方法建立,以建立一个整数二维数组为例: #include<iostream> #include<string> #include<malloc.h> using namespace std; int main(int argc,char **argv) { ///*int a[2][3]={{1,2,3},{4,5,6}}; //cout<<sizeof(a+1)<<

  • 动态数组C++实现方法(分享)

    回顾大二的数据结构知识.从数组开始.实现了一个可自动扩充容量的泛型数组. 头文件:Array.h #ifndef Array_hpp #define Array_hpp template <class T> class Array{ private: T *base; //数组首地址 int length; //数组中元素 int size; //数组大小,以数组中元素的大小为单位 public: //初始化数组,分配内存 bool init(); //检查内存是否够用,不够用就增加 bool

  • C++中队列queue的用法实例详解

    目录 一.定义 一.queue初始化 二.queue常用函数 补充:queue 的基本操作举例如下 总结 一.定义 queue是一种容器转换器模板,调用#include< queue>即可使用队列类. 一.queue初始化 queue<Type, Container> (<数据类型,容器类型>) 初始化时必须要有数据类型,容器可省略,省略时则默认为deque 类型 初始化示例 1: queue<int>q1; queue<double>q2; q

  • C++实现栈与分析栈的知识点

    目录 一.栈的概念 二.栈的基本组成和操作 三.栈元素的存储方式 四.C++实现静态栈 (1)栈类的设计 (1)isEmpty()判断是否为空 (2)isFull()判断是否已满 (3)push()压栈 (4)pop()出栈 (5)getTop()获取栈顶元素 (6)clear()清空栈 一.栈的概念 栈的英文为stack,译为一叠或者一摞.栈是一种采用先进后出FILO(first in last out)或称为后进先出LIFO(last in first out)策略进行元素访问的数据结构.栈

  • C++超详细讲解单链表的实现

    目录 单链表的实现(从入门到熟练) 概念和结构 链表的实现 增删查改接口 节点结构体创建 节点开辟 数据打印 链表尾插数据 头删 链表数据查找 链表pos位置前插数据 链表pos位置后插数据 链表pos位置数据删除 链表节点释放 链表与顺序表区别 链表的优缺点 顺序表的优缺点 单链表的实现(从入门到熟练) 概念和结构 概念:链表是一种物理存储结构上非连续.非顺序的存储结构 数据元素的逻辑顺序是通过链表中的指针链 接次序实现的 图示: 注意: 链表结构在逻辑上为连续的,但是物理上(内存中)不一定连

  • C++实现动态数组功能

    数组 数组是一种线性表数据结构.它用一组连续内存空间,来存储一组具有相同数据类型数据. 1.线性表:数据存储像一条线一样的结构,每个线性表上的数据最多只有前和后的两个方向,如数组.链表.队列.栈等都是这种结构,所以实现的数组的动态操作,其他结构也可轻易的类似实现.更重要的是,在这之后看源码就可大大降低难度.(博主自己看的是STL源码剖析) 2.非线性表:如二叉树.堆.图等. 3连续内存空间和相同数据类型:当数组作插入.删除操作时,为了保证数据的连续性,往往需要做大量的数据搬移工作,效率很低. 动

  • C++线性表深度解析之动态数组与单链表和栈及队列的实现

    目录 一.线性表介绍 线性表性质 二.动态数组 1)分析与设计 2)实现 三.单链表(企业设计方式) 1)分析与设计 2)实现 四.栈(受限线性表) 1)利用数组实现栈 2)利用单链表实现栈 3)栈的应用——就近匹配 1.算法思想 2.实现 五.队列(受限线性表) 1)队列的顺序存储 2)利用单链表实现队列 数据结构大体可以分为两个部分:逻辑结构和物理结构. 物理结构大体也可以分为两个部分,即顺序结构和链式存储结构. 而线性结构就是逻辑结构中的一种. 一.线性表介绍 线性表是零个或多个数据元素组

  • Python实现栈的方法详解【基于数组和单链表两种方法】

    本文实例讲述了Python实现栈的方法.分享给大家供大家参考,具体如下: 前言 使用Python 实现栈. 两种实现方式: 基于数组 - 数组同时基于链表实现 基于单链表 - 单链表的节点时一个实例化的node 对象 完整代码可见GitHub: https://github.com/GYT0313/Python-DataStructure/tree/master/5-stack 目录结构: 注:一个完整的代码并不是使用一个py文件,而使用了多个文件通过继承方式实现. 1. 超类接口代码 arra

  • C语言线性表的链式表示及实现详解

    目录 前言 代码实现 1. 单链表的结点构造 2. 构造一个空的头结点 3. 对线性表进行赋值 4.对线性表进行销毁 5.对线性表进行重置 6.判断线性表是否为空 7.获取线性表的长度 8.获取线性表某一位置对应的元素 9.在线性表某一位置插入元素 10.删除线性表某一位置的元素 11.求线性表某一元素的前驱 12.求线性表某一元素的后继 13.打印线性表 运行结果演示 源码 前言 线性表的顺序表示指的是用一组地址连续的存储单元依次存储线性表的数据元素,而线性表的链式存储特点则是用一组任意的存储

  • Python线性表种的单链表详解

    目录 1. 线性表简介 2. 数组 3. 单向链表 设计链表的实现 链表与顺序表的对比 1. 线性表简介 线性表是一种线性结构,它是由零个或多个数据元素构成的有限序列.线性表的特征是在一个序列中,除了头尾元素,每个元素都有且只有一个直接前驱,有且只有一个直接后继,而序列头元素没有直接前驱,序列尾元素没有直接后继. 数据结构中常见的线性结构有数组.单链表.双链表.循环链表等.线性表中的元素为某种相同的抽象数据类型.可以是C语言的内置类型或结构体,也可以是C++自定义类型. 2. 数组 数组在实际的

  • 算法系列15天速成 第七天 线性表【上】

    哈哈,我们的数据也一样,存在这三种基本关系,用术语来说就是: <1>  线性关系.<2>  树形关系.<3>  网状关系. 一: 线性表 1 概念:                 线性表也就是关系户中最简单的一种关系,一对一.                  如:学生学号的集合就是一个线性表. 2 特征:                 ① 有且只有一个"首元素".                 ② 有且只有一个"末元素".

  • java实现线性表及其算法

    线性表 线性表是最简单和最常用的一种数据结构,它是有n个体数据元素(节点)组成的有限序列.其中,数据元素的个数n为表的长度,当n为零时成为空表,非空的线性表通常记为: (a1,a2,- ,ai-1,ai, ai+1,-,an) 一. 线性表的顺序存储及算法 线性表的顺序存储指的是将线性表的数据元素按其逻辑次序依次存入一组地址连续的存储单元里,用这种方法存储的线性表称为顺序表. 1.顺序表的结构定义 public class SeqList { /* 初始空间为10 */ private stat

  • java数据结构基础:线性表

    目录 前言 需求分析 编码 add方法 getIndex方法 pop方法 insert方法 getAll 全部代码 总结 前言 其实线性表在生活中和栈的结构差不多.昨天总结了一篇单链表,也是线性表的一种. 今天用另一种写法来控制指针的移动实现数据的顺序存储结构. 需求分析 首先要明确,这种顺序存储结构的线性表底层用什么.根据之前查看过的源码来看,list一般都是以数组为底层.我们也不例外. 其次,我们还得去定义好线性表的长度,以及每个元素的指针. private Object[] arr; //

  • Java数据结构专题解析之栈和队列的实现

    目录 1. 栈 1.1 概念 1.2 助解图题 1.3 栈的数组实现 1.4 问题 1.5 栈的单链表实现 2. 队列 2.1 概念 2.2 问题 2.3 队列的单链表实现 2.4 数组实现队列 2.5 循环队列 2.6 双端队列 3. 栈和队列练习题 3.1 有效的括号 3.2 用队列实现栈 3.3 用栈实现队列 3.4 实现一个最小栈 3.5 设计循环队列 1. 栈 1.1 概念 栈:是一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作. 特点:栈中的数据元素遵循先进后出的原则,但

  • C++实现动态线性表

    之前在学习c语言的时候用c语言实现了动态线性表.现在再使用c++实现一下动态线性表. 相关数据结构方面就不多说了.在之前的博客里也有.下面就直接来实现吧. 这里使用指针来遍历数组,这样在算size,capacity的时候,直接用指针相减的方式就可以得到元素个数,以及容量. Vector.h #include <iostream> #include<assert.h> #include<stdio.h> #include<string.h> //用typede

  • C++ 数据结构线性表-数组实现

    C++ 数据结构线性表-数组实现 线性表的数组实现,实现几个核心的功能,语言是C++,如果有更好的想法和意见,欢迎留言~~~ /* Author : Moyiii * 线性表的数组实现,仅作学习之用,当然如果 * 你想拿去用,随你好啦. */ #include<iostream> using namespace std; //顺序表 class SeqList { public: //构造函数,接受一个默认的列表大小 SeqList(int size = MAX_LIST_SIZE); //析

随机推荐