数据结构(从概念到C++实现)

目录

第1章 绪论

1.1 算法分析

1.1.1 算法的时间复杂度

1.1.2 算法的空间复杂度

1.2 应用举例

1.2.1 数组的最小值及其下标

1.2.2 数组元素循环左移

第2章 线性表

2.1 引言

2.2 线性表的逻辑结构

2.2.1 线性表的定义

2.2.2 线性表的抽象数据类型定义

2.3 线性表的顺序存储结构及实现

2.3.1 顺序表的存储结构 

2.3.2 顺序表的实现

2.3.3 顺序表的使用

2.3.4 完整源代码

2.4 线性表的链接存储结构及实现

2.4.1 单链表的存储结构

2.4.2 单链表的实现

 2.4.4 完整源代码

2.4.5 双链表

2.4.6 循环链表

2.5 顺序表和链表的比较

2.6 扩展与提高

2.6.1 线性表的静态链表存储

2.6.2 顺序表的动态分配方式

2.7 应用实例

2.7.1 单链表管理学生成绩

2.7.2 约瑟夫环问题

2.7.3 一元多项式求和

2.7.4 合并循环单链表 

第3章 栈和队列

3.1 引言

3.2 栈

3.2.1 栈的逻辑结构

3.2.2 栈的顺序存储结构及实现

3.2.3 完整源代码

3.2.4 栈的链接存储结构及实现

3.2.5 源代码

3.2.6 顺序栈和链栈的比较

3.3 应用实例

3.3.1 执行各个菜单功能(顺序栈)

3.3.2 执行各个菜单的功能(链栈)

3.4 队列

3.4.1 队列的逻辑结构

3.4.2 队列的顺序存储结构及实现

3.4.3 源代码

3.4.4 队列的链接存储结构及实现

3.4.5 源代码

3.5 扩展与提高

3.5.1 两栈共享空间

3.5.2 源代码​

 3.5.3 双端队列

3.6 应用举例

3.6.1 括号匹配问题

3.6.2 表达式求值

第4章 字符串和多维数组

4.1 引言

4.2 字符串

4.2.1 字符串的逻辑结构

4.2.2 字符串的存储结构

 4.2.3 模式匹配

4.3 多维数组

4.3.1 数组的逻辑结构

4.3.2 数组的存储结构与寻址

4.4  矩阵的压缩存储 

4.4.1 特殊矩阵的压缩存储

4.4.2 稀疏矩阵的压缩存储

4.5 扩展与提高   

4.5.1 稀疏矩阵的转置运算

3.5.2 广义表

4.6 应用实例

4.6.1 发纸牌

4.6.2 以列序为主序存储矩阵

第5章 数和二叉树

5.1 引言

5.2 树的逻辑结构

5.2.1 树的定义和基本术语

5.2.2 树的抽象数据类型定义

5.2.3 树的遍历操作

5.3 树的存储结构

5.3.1 双亲表示法

5.3.2 孩子表示法

5.3.3 孩子兄弟表示法

5.4 二叉树的逻辑结构

5.4.1 二叉树的定义

5.4.2 二叉树的基本性质

5.4.3 二叉树的抽象数据类型定义

5.4.4 二叉树的遍历操作

5.5 二叉树的存储结构

5.5.1 顺序存储结构

 5.5.2 二叉链表

5.5.3 三叉链表

5.5.4 二叉链表源代码 

5.6 森林

5.6.1 森林的逻辑结构

5.6.2 森林、树与二叉树的转换

5.7 最优二叉树

5.7.1 哈夫曼算法

5.7.2 哈夫曼编码

5.8 扩展与提高

5.8.1 二叉树遍历的非递归算法

5.8.2 线索链表

5.9 应用实例

5.9.1 堆与优先队列


第1章 绪论

1.1 算法分析

1.1.1 算法的时间复杂度

用大O表示法表示算法

规则:1.只关注最高次项

           2.时间复杂度是指最坏时间复杂度

           3.只有常数项记做1

常见的时间复杂度如下:

O(log2 n)<O(n)<O(nlog2 n)<O(n²)<O(n³)<....<O(2的n次方)<O(n!)

1.1.2 算法的空间复杂度

与算法无关,只与定义的变量与输入输出有关

1.2 应用举例

1.2.1 数组的最小值及其下标

题目:已知求数组最小元素的值及下标的算法如下,编写主函数进行测试,要求输入任意数组,给出运行结果

#include<iostream>
using namespace std;

void min(double arr[], int m)
{
    double h = arr[0];
    int loc = 0;
    for (int i = 0; i < m; i++)
    {
        if (arr[i] < h)
        {
            h = arr[i];
            loc = i;
        }
    }
    cout << "数组中最小的元素为:" << h <<"\t"<<"最小的元素的下标为:" << loc << endl;
}
int main()
{
    int m;
    double n;
    cout << "请输入数组的位数:";
    cin >> m;
    double* arr = new double[m];
    cout << "请输入数组元素:";
    for (int i = 0; i < m; i++)
    {
        cin >> n;
        arr[i] = n;
    }
    min(arr, m);
}

运行结果: 

 总结:1.构建动态数组

int m;
    double n;
    cout << "请输入数组的位数:";
    cin >> m;
    double* arr = new double[m];
    cout << "请输入数组元素:";
    for (int i = 0; i < m; i++)
    {
        cin >> n;
        arr[i] = n;
    }

            2.直接排序算法 

void min(double arr[], int m)
{
    double h = arr[0];  //数组最小值的初始化
    int loc = 0;
    for (int i = 0; i < m; i++)
    {
        if (arr[i] < h)
        {
            h = arr[i];
            loc = i;
        }
    }
    cout << "数组中最小的元素为:" << h <<"\t"<<"最小的元素的下标为:" << loc << endl;
}

1.2.2 数组元素循环左移

题目:设计一个算法,实现将数组arr[n]中所有元素循环左移k个位置

#include<iostream>
using namespace std;
void Reverse(int arr[],int from,int to)
{
  for(int i=0;i<(to-from+1)/2;i++)
  {
    int temp=arr[from+i];
    arr[from+i]=arr[to-i];
    arr[to-i]=temp;
  }
}
void Converse(int arr[],int n,int k)
{
  Reverse(arr,0,k-1);
  Reverse(arr,k,n-1);
  Reverse(arr,0,n-1);
}
int main()
{
  int n,m;
  cout<<"请输入数组的位数:";
  cin>>n;
  int *arr=new int[n];
  cout<<"请输入"<<n<<"位数组的元素值:";
  for(int i=0;i<n;i++)
  {
    cin>>m;
    arr[i]=m;
  }
  int k;
  cout<<"请输入左移位数:";
  cin>>k;
  cout<<"左移后的数组变为:";
  Converse(arr,n,k);
  for(int i=0;i<n;i++)
  {
    cout<<arr[i]<<"\t";
  }
  cout<<endl;
  return 0;
}

 运行结果:

知识点:

1.首先相对应位置交换位置,再分块逆置 

第2章 线性表

2.1 引言

线性表是描述单一的前驱和后继的关系

2.2 线性表的逻辑结构

2.2.1 线性表的定义

线性表是一个有限的序列,每个元素有且只有一个前驱和一个 后继,表头和表尾元素分别无前驱和后继。

线性表数据元素的个数称为线性表的长度,长度为0的线性表称为空表

线性表的常用表达方式为:L=(a1,a2,a3,a4....an)

2.2.2 线性表的抽象数据类型定义

DataModel
  数据元素具有相同类型,相邻元素具有前驱和后继的关系
InitList
  输入:无
  功能:线性表的初始化
  输出:空的线性表
CreatList
  输入:n个数据元素
  功能:建立一个线性表
  输出:具有n个元素的线性表
DestroyList
  输入:无
  功能:销毁线性表
  输出:释放线性表的存储空间
PrintList
  输入:无
  功能:遍历操作,按序号依次输出线性表中的元素
  输出:线性表的各个数据元素
Length
  输入:无
  功能:求线性表的长度
  输出:线性表中数据元素的个数
Locate
  输入:数据元素x
  功能:按值查找,在线性表中查找值等于x的元素
  输出:如果查找成功,返回元素x在线性表中的序号,否则返回0
Get
  输入:元素的符号i
  功能

2.3 线性表的顺序存储结构及实现

2.3.1 顺序表的存储结构 

线性表的顺序存储结构称为顺序表

确定了起始的首地址,就可以确定整个线性表的地址

顺序表的存储结构指的是一块连续的地址存储单元格的信息,且存储的信息和下标具有一一对应的关系

用一维数组相邻的信息存储在相邻的位置

需要提前设定好数组的长度,一般来说,数组长度要大于当前线性表的长度

2.3.2 顺序表的实现

由于顺序表的类型不确定,因此在实现时要调用C++模板机制

template<class DataType>  //定义类模板(由于线性表的类型不确定)
class SeqList
{
public:
	SeqList();  //无参构造函数,建立空的顺序表
	SeqList(DataType a[], int n);  //有参构造函数,建立长度为n的顺序表
	~SeqList();  //析构函数
	int Length();  //求线性表的长度
	int Empty();  //判空
	DataType Get(int i);  //按位查找,查找第i个元素的值
	int Locate(DataType x);  //按值查找,返回值为x的元素序号
	void Insert(int i, DataType x);
	DataType Delete(int i);
	void PrintList();
private:
	DataType data[MaxSize];  //存放数据元素的数组
	int length;  //线性表的长度
};

1.无参构造函数——初始化顺序表

将length初始化为0

template<class DataType>
SeqList<DataType>::SeqList()
{
	length = 0;
}

2.有参构造函数——建立顺序表

传入个数和元素

template<class DataType>
SeqList<DataType>::SeqList(DataType a[], int n)
{
	if (n > MaxSize) throw"参数非法";
	for (int i = 0; i < n; i++)
		data[i] = a[i];
	length = n;
}

3.析构函数——销毁顺序表

由于顺序表是静态内存分配,在退出顺序表时会自动销毁所占的内存单元,因此析构函数为空

template<class DataType>
SeqList<DataType>::~SeqList()
{

}

4.判空操作

length是否为0

template<class DataType>
int SeqList<DataType>::Empty()
{
	if (length == 0)
		return 1;
	else
		return 0;
}

5.求顺序表的长度

返回length

template<class DataType>
int SeqList<DataType>::Length()
{
	return length;
}

6.遍历操作

template<class DataType>
void SeqList<DataType>::PrintList()
{
	for (int i = 0; i < length; i++)
		cout << data[i];
}

7.按位查找

template<class DataType>
DataType SeqList<DataType>::Get(int i)
{
	if (i<1 || i>length)
		throw"查找位置非法";
	else
		return data[i - 1];
}

8.按值查找

返回的是序号

template<class DataType>
int SeqList<DataType>::Locate(DataType x)
{
	for (int i = 0; i < length; i++)
		if (data[i] == x) return i + 1;
	return 0;
}

9.插入操作

template<class DataType>
void SeqList<DataType>::Insert(int i, DataType x)
{
	if (length >= MaxSize)
		throw"上溢";
	if (i<1 || i>length + 1)
		throw"位置非法";
	for (int j = length; j >= i; j--)
		data[j] = data[j - 1];
	data[i - 1] = x;
	length++;
}

10.删除操作

template<class DataType>
DataType SeqList<DataType>::Delete(int i)
{
	if (length == 0)
		throw"下溢";
	if (i<1 || i>length)
		throw"位置非法";
	int x = data[i - 1];
	for (int j = i; j < length; j++)
		data[j - 1] = data[j];
	length--;
	return x;
}

2.3.3 顺序表的使用

int main()
{
	int r[5] = { 1,2,3,4,5 }, i, x;
	SeqList<int> L{ r,5 };  //建立具有5个元素的顺序表
	cout << "当前线性表的数据为:";
	L.PrintList();
	try
	{
		L.Insert(2, 8);
		cout << endl << "执行插入操作后数据为:";
		L.PrintList();
		cout << endl;
	}
	catch (char* str) {
		cout << str << "插入位置错误!" << endl;
	}

	cout << "当前线性表的长度为:" << L.Length();
	cout << endl;

	cout << "请输入要查找的元素值:";
	cin >> x;
	i = L.Locate(x);
	if (0 == i) cout << "查找失败" << endl;
	else cout << "元素" << x << "的位置为:" << i << endl;

	try
	{
		cout << "请输入查找第几个元素值:";
		cin >> i;
		cout << "第" << i << "个元素值是" << L.Get(i) << endl;
	}
	catch (char* str) {
		cout << "线性表中没有该元素" << endl;
	}

	try
	{
		cout << "请输入要删除第几个元素:";
		cin >> i;
		x = L.Delete(i);
		cout << "删除的元素是" << x << ",删除后的数据为:";
		L.PrintList();
	}
	catch (char* str)
	{
		cout << "删除错误!" << endl;
	}
	return 0;
}

2.3.4 完整源代码

#include<iostream>
using namespace std;

const int MaxSize = 100;  //为顺序表预留足够的内存空间

template<class DataType>  //定义类模板(由于线性表的类型不确定)
class SeqList
{
public:
	SeqList();  //无参构造函数,建立空的顺序表
	SeqList(DataType a[], int n);  //有参构造函数,建立长度为n的顺序表
	~SeqList();  //析构函数
	int Length();  //求线性表的长度
	int Empty();  //判空
	DataType Get(int i);  //按位查找,查找第i个元素的值
	int Locate(DataType x);  //按值查找,返回值为x的元素序号
	void Insert(int i, DataType x);
	DataType Delete(int i);
	void PrintList();
private:
	DataType data[MaxSize];  //存放数据元素的数组
	int length;  //线性表的长度
};
template<class DataType>
SeqList<DataType>::~SeqList()
{

}
template<class DataType>
SeqList<DataType>::SeqList()
{
	length = 0;
}
template<class DataType>
int SeqList<DataType>::Empty()
{
	if (length == 0)
		return 1;
	else
		return 0;
}
template<class DataType>
int SeqList<DataType>::Length()
{
	return length;
}
template<class DataType>
SeqList<DataType>::SeqList(DataType a[], int n)
{
	if (n > MaxSize) throw"参数非法";
	for (int i = 0; i < n; i++)
		data[i] = a[i];
	length = n;
}
template<class DataType>
void SeqList<DataType>::PrintList()
{
	for (int i = 0; i < length; i++)
		cout << data[i];
}
template<class DataType>
int SeqList<DataType>::Locate(DataType x)
{
	for (int i = 0; i < length; i++)
		if (data[i] == x) return i + 1;
	return 0;
}
template<class DataType>
DataType SeqList<DataType>::Get(int i)
{
	if (i<1 || i>length)
		throw"查找位置非法";
	else
		return data[i - 1];
}
template<class DataType>
DataType SeqList<DataType>::Delete(int i)
{
	if (length == 0)
		throw"下溢";
	if (i<1 || i>length)
		throw"位置非法";
	int x = data[i - 1];
	for (int j = i; j < length; j++)
		data[j - 1] = data[j];
	length--;
	return x;
}
template<class DataType>
void SeqList<DataType>::Insert(int i, DataType x)
{
	if (length >= MaxSize)
		throw"上溢";
	if (i<1 || i>length + 1)
		throw"位置非法";
	for (int j = length; j >= i; j--)
		data[j] = data[j - 1];
	data[i - 1] = x;
	length++;
}
int main()
{
	int r[5] = { 1,2,3,4,5 }, i, x;
	SeqList<int> L{ r,5 };  //建立具有5个元素的顺序表
	cout << "当前线性表的数据为:";
	L.PrintList();
	try
	{
		L.Insert(2, 8);
		cout << endl << "执行插入操作后数据为:";
		L.PrintList();
		cout << endl;
	}
	catch (char* str) {
		cout << str << "插入位置错误!" << endl;
	}

	cout << "当前线性表的长度为:" << L.Length();
	cout << endl;

	cout << "请输入要查找的元素值:";
	cin >> x;
	i = L.Locate(x);
	if (0 == i) cout << "查找失败" << endl;
	else cout << "元素" << x << "的位置为:" << i << endl;

	try
	{
		cout << "请输入查找第几个元素值:";
		cin >> i;
		cout << "第" << i << "个元素值是" << L.Get(i) << endl;
	}
	catch (char* str) {
		cout << "线性表中没有该元素" << endl;
	}

	try
	{
		cout << "请输入要删除第几个元素:";
		cin >> i;
		x = L.Delete(i);
		cout << "删除的元素是" << x << ",删除后的数据为:";
		L.PrintList();
	}
	catch (char* str)
	{
		cout << "删除错误!" << endl;
	}
	return 0;
}

2.4 线性表的链接存储结构及实现

顺序表的不足:1.容量难以确定2.地址不连续就不能使用3.需要移动大量元素


根本原因是静态内存存储,可通过链表动态存储解决问题 

2.4.1 单链表的存储结构

template<typename DataType>
struct Node
{
	DataType data;
	Node<DataType>* next;
};

链表存储指的是一组任意的存储地址存储数据元素,地址可以是连续的,也可以是不连续的。一个单元当中,除了存放自身的数据之外,还要存放下一个元素的地址,即为指针,由此的数据域和指针域构成一个链表的结点

由于只有一个指针域,因此叫做单链表

每一个节点的地址都存储在前一个节点的指针域当中。由于第一个节点无前驱节点,因此设立头指针指向链表的第一个节点,链表的操作必须要从头指针开始,因此,头指针具有标识一个链表的作用。

由于最后一个节点无后继节点,因此设置一个尾标志来表示。

为了统一非空表和空表,因此设立链表的第一个结点为头结点,头结点是与链表类型一致的节点,通常不存储任何的信息。

2.4.2 单链表的实现

template<typename DataType>
class LinkList
{
public:
	LinkList();
	LinkList(DataType a[], int n);
	~LinkList();
	int Length();
	int Empty();
	DataType Get(int i);
	int Locate(DataType x);
	void Insert(int i, DataType x);
	DataType Delete(int i);
	void PrintList();
private:
	Node<DataType>* first;  //单链表的头指针
};

1.无参构造函数——单链表的初始化

生成只有头结点的空单链表 

template<typename DataType>
LinkList<DataType>::LinkList()
{
	first = new Node<DataType>;
	first->next = NULL;
}

2.判空条件

template<typename DataType>
int LinkList<DataType>::Empty()
{
	if (first->next == NULL)
		return 1;
	else return 0;
}

3.遍历操作

template<typename DataType>
void LinkList<DataType>::PrintList()
{
	Node<DataType>* p = first->next;
	while (p != NULL)
	{
		cout << p->data << "\t";
		p = p->next;
	}
}

4.求单链表的长度

template<typename DataType>
int LinkList<DataType>::Length()
{
	Node<DataType>* p = first->next;
	int count = 0;
	while (p != NULL)
	{
		p = p->next;
		count++;
	}
	return count;
}

5.按位查找

template<typename DataType>
DataType LinkList<DataType>::Get(int i)
{
	Node<DataType>* p = first->next;
	int count = 1;
	while (p != NULL && count < i)
	{
		p = p->next;
		count++;
	}
	if (p == NULL) throw"位置非法";
	else return p->data;
}

6.按值查找

template<typename DataType>
int LinkList<DataType>::Locate(DataType x)
{
	Node<DataType>* p = first->next;
	int count = 1;
	while (p != NULL)
	{
		if (p->data == x) return count;
		p = p->next;
		count++;
	}
	return 0;   //退出循环表明查找失败
}

7.插入操作

template<typename DataType>
void LinkList<DataType>::Insert(int i, DataType x)
{
	Node<DataType>* p = first, * s = NULL;
	int count = 0;
	while (p != NULL && count < i - 1)
	{
		p = p->next;
		count++;
	}
	if (p == NULL) throw"位置异常";
	else {
		s = new Node<DataType>;
		s->data = x; //s结点的数据域为x
		s->next = p->next;
		p->next = s;
	}
}

8.构造函数——建立单链表

头插法:

template<typename DataType>
//LinkList<DataType>::LinkList(DataType a[], int n)
//{
//	first = new Node<DataType>;
//	first->next = NULL;
//	for (int i = 0; i < n; i++)
//	{
//		Node<DataType>* s;
//		s = new Node<DataType>;
//		s->data = a[i];
//		s->next = first->next;
//		first->next = s;  //将结点s插在头结点之后
//	}
//}

尾插法:

template<typename DataType>
LinkList<DataType>::LinkList(DataType a[], int n)
{
	first = new Node<DataType>;
	Node<DataType>* r = first, * s = NULL;  //头指针和尾指针的初始化
	for (int i = 0; i < n; i++)
	{
		s = new Node<DataType>;
		s->data = a[i];
		r->next = s;
		r = s;    //将结点s插入到终端结点之后
	}
	r->next = NULL;  //单链表建立完毕,将终端结点的指针域置空
}

9.删除操作

template<typename DataType>
DataType LinkList<DataType>::Delete(int i)
{
	DataType x;
	Node<DataType>* p = first, * q = NULL;
	int count = 0;
	while (p != NULL && count < i - 1)
	{
		p = p->next;
		count++;
	}
	if (p == NULL || p->next == NULL)
		throw"位置不存在";
	else
	{
		q = p->next;  //暂存被删结点
		x = q->data;
		p->next = q->next;
		delete q;
		return x;
	}
}

 2.4.3 单链表的使用

int main()
{
	int r[5] = { 1,2,3,4,5 }, i, x;
	LinkList<int> L{ r,5 };
	cout << "当前线性表的数据为:";
	L.PrintList();
	cout << endl;
	try
	{
		L.Insert(2, 8);
		cout << "执行插入操作之后的数据为:";
		L.PrintList();
		cout << endl;
	}
	catch (char* str)
	{
		cout << str << endl;
	}

	cout << "当前链表的长度为:" << L.Length() << endl;
	cout << "请输入要查找的元素值:";
	cin >> x;
	i = L.Locate(x);
	if (i >= 1)
		cout << "元素" << x << "的元素位置为:" << i << endl;
	else
		cout << "单链表中没有元素" <<x<< endl;
	try
	{
		cout << "请输入要删除第几个元素:";
		cin >> i;
		x = L.Delete(i);
		cout << "删除的元素值是" << x << "执行删除操作后数据为:";
		L.PrintList();
	}
	catch (char* str)
	{
		cout << str << endl;
	}
	return 0;
}

 2.4.4 完整源代码

#include<iostream>
using namespace std;
template<typename DataType>
struct Node
{
	DataType data;
	Node<DataType>* next;
};

template<typename DataType>
class LinkList
{
public:
	LinkList();
	LinkList(DataType a[], int n);
	~LinkList();
	int Length();
	int Empty();
	DataType Get(int i);
	int Locate(DataType x);
	void Insert(int i, DataType x);
	DataType Delete(int i);
	void PrintList();
private:
	Node<DataType>* first;
};

template<typename DataType>
LinkList<DataType>::LinkList()
{
	first = new Node<DataType>;
	first->next = NULL;
}

template<class DataType>
LinkList<DataType>::~LinkList()
{
	Node<DataType>* q = NULL;
	while (first != NULL)
	{
		q = first;
		first = first->next;
		delete q;
	}
}

template<typename DataType>
int LinkList<DataType>::Empty()
{
	if (first->next == NULL)
		return 1;
	else return 0;
}

template<typename DataType>
void LinkList<DataType>::PrintList()
{
	Node<DataType>* p = first->next;
	while (p != NULL)
	{
		cout << p->data << "\t";
		p = p->next;
	}
}

template<typename DataType>
int LinkList<DataType>::Length()
{
	Node<DataType>* p = first->next;
	int count = 0;
	while (p != NULL)
	{
		p = p->next;
		count++;
	}
	return count;
}

template<typename DataType>
DataType LinkList<DataType>::Get(int i)
{
	Node<DataType>* p = first->next;
	int count = 1;
	while (p != NULL && count < i)
	{
		p = p->next;
		count++;
	}
	if (p == NULL) throw"位置非法";
	else return p->data;
}

template<typename DataType>
int LinkList<DataType>::Locate(DataType x)
{
	Node<DataType>* p = first->next;
	int count = 1;
	while (p != NULL)
	{
		if (p->data == x) return count;
		p = p->next;
		count++;
	}
	return 0;   //退出循环表明查找失败
}

template<typename DataType>
void LinkList<DataType>::Insert(int i, DataType x)
{
	Node<DataType>* p = first, * s = NULL;
	int count = 0;
	while (p != NULL && count < i - 1)
	{
		p = p->next;
		count++;
	}
	if (p == NULL) throw"位置异常";
	else {
		s = new Node<DataType>;
		s->data = x; //s结点的数据域为x
		s->next = p->next;
		p->next = s;
	}
}

//头插法构造
//template<typename DataType>
//LinkList<DataType>::LinkList(DataType a[], int n)
//{
//	first = new Node<DataType>;
//	first->next = NULL;
//	for (int i = 0; i < n; i++)
//	{
//		Node<DataType>* s;
//		s = new Node<DataType>;
//		s->data = a[i];
//		s->next = first->next;
//		first->next = s;  //将结点s插在头结点之后
//	}
//}

template<typename DataType>
LinkList<DataType>::LinkList(DataType a[], int n)
{
	first = new Node<DataType>;
	Node<DataType>* r = first, * s = NULL;  //头指针和尾指针的初始化
	for (int i = 0; i < n; i++)
	{
		s = new Node<DataType>;
		s->data = a[i];
		r->next = s;
		r = s;    //将结点s插入到终端结点之后
	}
	r->next = NULL;  //单链表建立完毕,将终端结点的指针域置空
}

template<typename DataType>
DataType LinkList<DataType>::Delete(int i)
{
	DataType x;
	Node<DataType>* p = first, * q = NULL;
	int count = 0;
	while (p != NULL && count < i - 1)
	{
		p = p->next;
		count++;
	}
	if (p == NULL || p->next == NULL)
		throw"位置不存在";
	else
	{
		q = p->next;  //暂存被删结点
		x = q->data;
		p->next = q->next;
		delete q;
		return x;
	}
}

int main()
{
	int r[5] = { 1,2,3,4,5 }, i, x;
	LinkList<int> L{ r,5 };
	cout << "当前线性表的数据为:";
	L.PrintList();
	cout << endl;
	try
	{
		L.Insert(2, 8);
		cout << "执行插入操作之后的数据为:";
		L.PrintList();
		cout << endl;
	}
	catch (char* str)
	{
		cout << str << endl;
	}

	cout << "当前链表的长度为:" << L.Length() << endl;
	cout << "请输入要查找的元素值:";
	cin >> x;
	i = L.Locate(x);
	if (i >= 1)
		cout << "元素" << x << "的元素位置为:" << i << endl;
	else
		cout << "单链表中没有元素" <<x<< endl;
	try
	{
		cout << "请输入要删除第几个元素:";
		cin >> i;
		x = L.Delete(i);
		cout << "删除的元素值是" << x << "执行删除操作后数据为:";
		L.PrintList();
	}
	catch (char* str)
	{
		cout << str << endl;
	}
	return 0;
}

2.4.5 双链表

为每个单链表的结点在原先存储自身数据和后继结点的地址之外,还要存储前驱结点的地址

prior表示前驱指针域

template<typename DataType>
struct Node
{
DataType data;
DulNode<DataType> *prior,*next;
}

1.插入操作

在结点p后面插入新结点s

    1.s->prior=p;

    2.s->next=p->next;

    3.p->next->prior=s;

    4.p->next=s;

顺序不可修改

2.删除操作

设指针p指向待删除结点

    1.(p->prior)->next=p->next;

    2.(p->next)->prior=p->prior;

这两个语句顺序可以调换,执行删除操作完了以后要将p所占内存释放

2.4.6 循环链表

将单链表的尾部空指针由空改为指向头结点 ,形成一个环

为了更加方便,在循环单链表的尾部添加一个指向尾部的指针rear,则尾结点可表示为rear,而头结点表示为(rear->next)->next

  

将尾部结点的指针指向头结点,将头结点的指针指向尾结点的循环链表名为循环双链表,则开始结点(不是头结点)表示为first->next,终端结点表示为first->prior

2.5 顺序表和链表的比较

1.时间性能比较

按位查找操作时,顺序表实现为O(1),链表还需要从左到右遍历

顺序表按位查找:

template<class DataType>
DataType SeqList<DataType>::Get(int i)
{
	if (i<1 || i>length)
		throw"查找位置非法";
	else
		return data[i - 1];
}

链表按位查找: 

template<typename DataType>
DataType LinkList<DataType>::Get(int i)
{
	Node<DataType>* p = first->next;
	int count = 1;
	while (p != NULL && count < i)
	{
		p = p->next;
		count++;
	}
	if (p == NULL) throw"位置非法";
	else return p->data;
}

插入和删除操作时,顺序表需移动大量元素位置,而链表不用 

顺序表插入操作:

template<class DataType>
void SeqList<DataType>::Insert(int i, DataType x)
{
	if (length >= MaxSize)
		throw"上溢";
	if (i<1 || i>length + 1)
		throw"位置非法";
	for (int j = length; j >= i; j--)
		data[j] = data[j - 1];
	data[i - 1] = x;
	length++;
}

链表插入操作: 

template<typename DataType>
LinkList<DataType>::LinkList(DataType a[], int n)
{
	first = new Node<DataType>;
	Node<DataType>* r = first, * s = NULL;  //头指针和尾指针的初始化
	for (int i = 0; i < n; i++)
	{
		s = new Node<DataType>;
		s->data = a[i];
		r->next = s;
		r = s;    //将结点s插入到终端结点之后
	}
	r->next = NULL;  //单链表建立完毕,将终端结点的指针域置空
}

2.空间性能比较

空间性能指的是占用内存空间的大小

线性表只存储当前的数据,而链表除了存储自身的数据之外,还要存储前后结点的地址,则线性表每个结点占据的空间要小

但线性表需要提前预留足够的长度,否则会造成溢出等问题

因此,当数组大小确定时,采用线性表最佳。否则采用链表

2.6 扩展与提高

2.6.1 线性表的静态链表存储

1.静态链表的存储结构

用数组来模拟链表存储,用数组的下标来模拟表示元素的地址指针

由于是用数组存储,因此属于静态内存分配

每一个结点由两个部分组成:数据域以及指针域,指针域存储后继元素的下标

template<typename DataType>
struct SNode
{
	DataType data;
	int next;  //指针域,注意不是指针类型
};

2.静态链表的实现 

用first表示非空闲链表的头指针,用avail表示空闲链表的头指针

const int MaxSize = 100;
template<typename DataType>
class StaList
{
public:
	StaList();
	StaList(DataType a[], int n);
	~StaList();
private:
	SNode SList[MaxSize];  //静态链表数组
	int first, avail; //游标,链表头指针以及空闲指针
};

(1)插入操作

将空闲链的最前端结点摘下,插入到链表尾部

s = avail;  //不用申请新结点,利用空闲链表的第一个结点
avail = SList[avail].next;  //空闲链的头指针后移
SList[s].data = x;  //将x填入下标为s的结点
SList[s].data = SList[p].next;
SList[p].next = s;

(2)删除操作

q = SList[p].next;  //暂存被删结点的下标
SList[p].next = SList[q].next;  //摘链
SList[q].next = avail;  //将结点q插在空闲链avail的最前端
avail = q;  //空闲链头指针avail指向结点q

2.6.2 顺序表的动态分配方式

当内存不够时,可以灵活分配内存

2.7 应用实例

2.7.1 单链表管理学生成绩

编制C/C++程序,利用链式存储方式实现下列功能:从键盘输入学生成绩建立一个单链表(整数),然后根据屏幕提示,进行求学生成绩个数、查找学生成绩、删除学生成绩等操作。

#include<iostream>
using namespace std;
struct Node
{
	double data;
	Node* next;
};
Node* CreatStudent()
{
	double score;
	Node* h, * s, * p;
	h = new Node;
	h->next = NULL;
	p = h;
	cin >> score;
	while (score != -1)
	{
		s = new Node;
		s->data = score;
		p->next = s;
		p = s;
		cin >> score;
	}
	p->next = NULL;
	return h;
}
int ListLength(Node* h)
{
	Node* p;
	p = h;
	int j = 0;
	while (p->next != NULL)
	{
		p = p->next;
		j++;
	}
	return j;
}
void PrintList(Node* h)
{
	Node* p = h->next;
	while (p != NULL)
	{
		cout << p->data << "\t";
		p = p->next;
	}
	cout << endl;
}
int Get(Node* h, int i)
{
	int m;
	m = ListLength(h);
	Node* p;
	p = h;
	int j = 0;
	if (i<1 || i>m)
	{
		cout << "您查找的学生不存在" << endl;
		return 0;
	}
	while (j < i)
	{
		p = p->next;
		j++;
	}
	return p->data;
}
int DelList(Node* h, int i)
{
	Node* pre, * r;
	pre = h;
	int j = 0;
	int m = ListLength(h);
	if (i<1 || i>m)
	{
		cout << "删除位置不合法" << endl;
		return 0;
	}
	while (j < i - 1)
	{
		pre = pre->next;
		j = j + 1;
	}
	r = pre->next;
	pre->next = r->next;
	return r->data;
	free(r);
}
void main()
{
	Node* head = new Node;  //创建头指针
	head->next = NULL;
	int i;
	cout << "请输入学生的成绩,以-1结束:" << endl;
	head = CreatStudent();
	cout << "您输入的学生成绩个数为:" << ListLength(head);
	cout << endl << "您要查找第几个同学的成绩:";
	cin >> i;
	cout << "他的成绩是:" << Get(head, i);
	cout << endl << "您要删除第几个同学的成绩:";
	cin >> i;
	cout << "您删除的第" << i << "个学生的成绩是:" << DelList(head, i) << endl;
}

运行结果:

总结:

1. 

出现成绩乱码的原因: 

 改为:

2.7.2 约瑟夫环问题

我的结果: 

1.分析:构建环并设定相关操作即可

2.我的不足:(1)不知道怎样创建环,最终只创建出来了一个线性表

                     (2)不知道怎样一次循环到底一直到最后一位数

3.我的代码:

#include<iostream>
using namespace std;
template<typename DataType>
struct Node
{
内含资源如下: 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 ......................... 并查集_基于树思想实现
数据结构是计算机科学中的一门重要课程,它研究的是如何组织和存储数据以便有效地访问和操作。概念上,数据结构包括了数组、链表、栈、队列、树、图等等。 C语言是一种通用的、高级的编程语言,广泛应用于系统开发、嵌入式设备、游戏开发等领域。在实现数据结构时,C语言提供了丰富的数据类型和语法特性,使得实现数据结构变得相对简单。 《数据结构概念到C语言实现第三版》是一本对数据结构进行深入讲解,并通过使用C语言进行实现的书籍。它通过分析各种数据结构的特性和应用场景,讲解了它们的基本概念和操作方法,并给出了具体的C语言实现代码。 在这本书中,作者首先简要介绍了数据结构和算法的基本概念和原则,为读者打下理论基础。然后,逐一介绍了数组、链表、栈、队列、树等常见数据结构实现方式,并对它们的性能进行了分析和比较,帮助读者选择最适合自己需求的数据结构。 此外,该书还对常用的算法进行了详细介绍,如排序算法、查找算法等,这些算法在实际开发中非常重要。通过学习这些算法,读者可以了解它们的原理和实现方式,并能够灵活运用在自己的项目中。 总之,《数据结构概念到C语言实现第三版》是一本全面介绍数据结构和算法的书籍,通过C语言实现代码的方式,帮助读者理解和掌握数据结构的基本概念和操作方法,提高编程能力。无论是计算机科学的学生,还是从事软件开发的工程师,都可以从中获得实际的收益。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值