目录
第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
{