文章目录
数据描述
数据可以用不同的形式进行描述或存储在计算机存储器中,最常见的数据描述方法有:公式化描述、链接描述、间接寻址和模拟指针。
数据结构的概念:
- 抽象数据类型。
- 公式化描述、链接描述、间接寻址和模拟指针。
- 单向链表、循环链表和双向链表。
数据结构包括数据对象和实例以及构成实例的每个元素之间所存在的各种关系。这些关系可由相关的函数来实现。
线性表
线性表有必要执行的操作:
- 创建一个线性表
- 确定线性表是否为空
- 确定线性表的长度
- 查找第k个元素
- 查找指定的元素
- 删除第k个元素
- 在第k个元素之后插入一个新元素
ADT 线性表的抽象数据类型描述
抽象数据类型LinearList{
实例
0或多个元素的有序集合
操作
Create(); 创建一个个空线性表
Destroy(); 删除表
IsEmpty(); 如果表为空则返回true,否则返回false
Length(); 返回表的大小(即表中元素个数)
Find(k,x); 寻找表中第k个元素,并把它保存到x中;如果不存在,则返回false
Search(x); 返回元素x在表中的位置;如果x在表中,则返回false
Delete(k,x); 删除表中第k个元素,并把它保存到x中;函数返回修改后的线性表
Insert(k,x); 在第k个元素之后插入x,函数返回修改后的线性表
Output(out);把线性表放入输出流out之中
}
公式化描述
基本概念
公式化描述采用数组来表示表示一个对象的实例,数组中的每个位置被称之为单元或结点,每个数组单元应该足够大,以便能够容纳数据对象实例中任意一个元素。
基于公式的类LinearList
//基于公式的类LinearList
#pragma once
#include<iostream>
using namespace std;
template<class T>
class LinearList
{
public:
LinearList(int MaxListSize = 10);//构造函数
~LinearList() { delete[]element; }//析构函数
bool IsEmpty()const { return length == 0; }
int Length()const { return length; }
bool Find(int k, T& x)const;//返回第k个元素至x中
int Search(const T& x)const;//返回x所在位置
LinearList<T>& Delete(int k, T& x);//删除第k个元素并将它返回至x中
LinearList<T>& Insert(int k, const T& x);//在第k个元素之后插入x
void Output(ostream& out)const;
private:
int length;
int MaxSize;
T* element;//一维动态数组
};
异常类NoMem
//异常类NoMem
#pragma once
#include<iostream>
using namespace std;
//内存不足
class NoMem {
public:
NoMem(){}
};
//使new引发NoMem异常而不是xalloc异常
void my_new_handler() {
throw NoMem();
}
new_handler Old_Handler_ = set_new_handler(my_new_handler);
函数my_new_handler简单地引发一个类型为NoMem的异常。
最后一行调用了C++函数set_new_handler,每当分配内存失败时,该函数就让操作符new调用函数my_new_handler.所以,new引发的是异常NoMem而不是xalloc。每当分配内存失败时,set_new_handler将返回一个指针,指向由new此前所调用的那个函数,该指针保存在变量Old_Handle_中。
为了恢复new的原始行为,可以作如下调用:
set_new_handler(Old_Handler_);
以上程序中用一个函数名作为参数,另外,不管new以前引发的是哪一种类型的异常,以上程序都将把new引发的异常变成NoMem异常。
操作
基本的表操作
//基本的表操作
template<class T>
LinearList<T>::LinearList(int MaxListSize)
{//基于公式的线性表的构造函数
MaxSize = MaxListSize;
element = new T[MaxSize];
length = 0;
};
//把第k个元素取至x中
template<class T>
bool LinearList<T>::Find(int k,T&x)const
{//如果不存在第k个元素则返回false,否则返回true
if (k<1 || k>length) {
return false; //不存在第k个元素
}
x = element[k - 1];
return true;
};
//查找x,如果找到,则返回x所在的位置
template<class T>
int LinearList<T>::Search(const T&x)const
{//如果x不在表中,则返回0
for (int i = 0; i < length; i++) {
if (element[i] == x) {
return ++i;
}
}
return 0;
};
IsEmpty,Length和Find的复杂性为θ(1),Search的复杂性为O(length).
下面的语句创建一个整数线性表y,其最大长度为100:
LinearListy(100);
从线性表中删除一个元素
//从线性表中删除一个元素
//把第k个元素放入x中,然后删除第k个元素
template<class T>
LinearList<T>& LinearList<T>::Delete(int k, T& x) {
//如果不存在第k个元素,则引发异常OutOfBounds
if (Find(k, x)) {
//把元素k+1,...向前移动一个位置
for (int i = k; i < length; i++) {
element[i - 1] = element[i];
length--;
return *this;
}
}
else {
throw OutOfBounds();
}
}
如果不存在第k个元素,将引发一个异常,Delete所需要的的事件为θ(1);如果存在第k个元素,则移动length-k个元素,需要耗时θ((length-k)s),其中s是每个元素的大小.此外,被删除的元素被移动至x,因此,总的时间复杂性为O((length-k)s).
向线性表中插入一个元素
//向线性表中插入一个元素
template<class T>
LinearList<T>& LinearList<T>::Insert(int k, const T& x) {
//在第k个元素之后插入x
//如果不存在第K个元素,则引发异常OutOfBounds
//如果表已经满,则引发异常NoMem
if (k<0 || k>length)throw OutOfBounds();
if (length == MaxSize)throw NoMem();
//向后移动一个位置
for (int i = length-1; i >= k; i--) {
element[i + 1] = element[i];
}
element[k] = x;
length++;
return *this;
}
Insert的时间复杂性为O((length-k)s).
把线性表输送至输出流
//把线性表输送至输出流
template<class T>
void LinearList<T>::Output(ostream& out)const {
//把表输送至输出流
for (int i = 0; i < length; i++) {
out << element[i] << " ";
}
}
//重载<<
template<class T>
ostream& operator<<(ostream& out, const LinearList<T>& x) {
x.Output(out);
return out;
}
时间复杂性为θ(length.)
全部代码
异常类NoMem.h
//异常类NoMem
#pragma once
#include<iostream>
using namespace std;
//内存不足
class NoMem {
public:
NoMem(){
cout << "引发异常NoMem类" << endl;
exit(1);
}
};
//使new引发NoMem异常而不是xalloc异常
void my_new_handler() {
throw NoMem();
}
new_handler Old_Handler_ = set_new_handler(my_new_handler);
class OutOfBounds {
public:
OutOfBounds() {
cout << "不存在第k个元素,引发异常OutOfBounds发生!" << endl;
exit(1);
}
};
LinearList.h
//基于公式的类LinearList
#pragma once
#include<iostream>
#include"NoMem.h"
using namespace std;
template<class T>
class LinearList
{
public:
LinearList(int MaxListSize = 10);//构造函数
~LinearList() { delete[]element; }//析构函数
bool IsEmpty()const { return length == 0; }
int Length()const { return length; }
bool Find(int k, T& x)const;//返回第k个元素至x中
int Search(const T& x)const;//返回x所在位置
LinearList<T>& Delete(int k, T& x);//删除第k个元素并将它返回至x中
LinearList<T>& Insert(int k, const T& x);//在第k个元素之后插入x
void Output(ostream& out)const;
private:
int length;
int MaxSize;
T* element;//一维动态数组
};
//基本的表操作
template<class T>
LinearList<T>::LinearList(int MaxListSize)
{//基于公式的线性表的构造函数
MaxSize = MaxListSize;
element = new T[MaxSize];
length = 0;
};
//把第k个元素取至x中
template<class T>
bool LinearList<T>::Find(int k,T&x)const
{//如果不存在第k个元素则返回false,否则返回true
if (k<1 || k>length) {
return false; //不存在第k个元素
}
x = element[k - 1];
return true;
};
//查找x,如果找到,则返回x所在的位置
template<class T>
int LinearList<T>::Search(const T&x)const
{//如果x不在表中,则返回0
for (int i = 0; i < length; i++) {
if (element[i] == x) {
return ++i;
}
}
return 0;
};
//从线性表中删除一个元素
//把第k个元素放入x中,然后删除第k个元素
template<class T>
LinearList<T>& LinearList<T>::Delete(int k, T& x) {
//如果不存在第k个元素,则引发异常OutOfBounds
if (Find(k, x)) {
//把元素k+1,...向前移动一个位置
for (int i = k; i < length; i++) {
element[i - 1] = element[i];
length--;
return *this;
}
}
else {
throw OutOfBounds();
}
}
//向线性表中插入一个元素
template<class T>
LinearList<T>& LinearList<T>::Insert(int k, const T& x) {
//在第k个元素之后插入x
//如果不存在第K个元素,则引发异常OutOfBounds
//如果表已经满,则引发异常NoMem
if (k<0 || k>length)throw OutOfBounds();
if (length == MaxSize)throw NoMem();
//向后移动一个位置
for (int i = length-1; i >= k; i--) {
element[i + 1] = element[i];
}
element[k] = x;
length++;
return *this;
}
//把线性表输送至输出流
template<class T>
void LinearList<T>::Output(ostream& out)const {
//把表输送至输出流
for (int i = 0; i < length; i++) {
out << element[i] << " ";
}
}
//重载<<
template<class T>
ostream& operator<<(ostream& out, const LinearList<T>& x) {
x.Output(out);
return out;
}
main.cpp
//主函数
#include<iostream>
#include"LinearList.h"
#include"NoMem.h"
using namespace std;
void main(void) {
try {
LinearList<int> L(5);
cout << "Length = " << L.Length() << endl;
cout << "IsEmpty = " << L.IsEmpty() << endl;
L.Insert(0, 2).Insert(1, 6);
cout << "List is " << L << endl;
cout << "IsEmpty = " << L.IsEmpty() << endl;
int z;
L.Find(1, z);
cout << "First element is " << z << endl;
cout << "Length = " << L.Length() << endl;
L.Delete(1,z);
cout << "Deleted element is " << z << endl;
cout << "List is " << L << endl;
}
catch (...) {
cerr << "An exception has occurred" << endl;
}
}
链表描述
类ChainNode和Chain
在链表描述中,数据对象实例的每个元素都放在单元或节点中进行描述.不过,节点不必是一个数组元素,因此没有什么公式可用来定位某个元素.取而代之的是,每个节点中都包含了与该节点相关的其它节点的位置信息.这种关于其它节点的位置信息被称为 链或指针.
//链表的类定义
template<class T>
class ChainNode {
friend Chain<T>;
private:
T data;
ChainNode<T>* link;
};
template<class T>
class Chain {
private:
ChainNode<T>* first; //指向第一个结点的指针
public:
Chain() { first = 0; }
~Chain();
bool IsEmpty()const { return first == 0; }
int Length()const;
bool Find(int k, T& x)const;
int Search(const T& x)const;
Chain<T>& Delete(int k, T& x);
Chain<T>& Insert(int k,const T& x);
void OutPut(ostream& out)const;
};
Chain是ChainNode的一个友类,所以Chain可以访问ChainNode的所有成员(尤其是私有成员).
操作
创建一个空的整数型线性表:Chain L;
线性表的链表描述不需要指定表的最大长度.
删除链表中的所有节点
确定链表的长度
在链表中查找第k个元素
//删除链表中的所有节点
template<class T>
Chain<T>::~Chain() {
//链表的析构函数,用于删除链表中的所有节点
ChainNode<T>* next; //下一个节点
while (first) {
next = first->link;
delete first;
first = next;
}
}
/* 析构函数的复杂性为θ(n),其中n为链表的长度. */
//确定链表的长度
template<class T>
int Chain<T>::Length()const {
//返回链表中的元素总数
ChainNode<T>* current = first;
int len = 0;
while (current) {
len++;
current = current->link;
}
return len;
}
/* Length的复杂性为θ(n) */
//在链表中查找第k个元素
template<class T>
bool Chain<T>::Find(int k, T& x)const {
//寻找链表中的第k个元素,并将其传送至x
//如果不存在第k个元素,则返回false,否则返回true
if (k < 1)return false;
ChainNode<T>* current = first;
int index = 1; //current的索引
while (index < k && current) {
current = current->link;
index++:
}
if (current) { x = current->data; return true; }
return false; //不存在第k个元素
}
在链表中搜索
输出链表
//在链表中搜索
template<class T>
int Chain<T>::Search(const T& x)const {
//寻找x,如果发现x,则返回x的地址
//如果x不在链表中,则返回0
ChainNode<T>* current = first;
int index = 1; //current的索引
while (current && current->data != x) {
current = current->link;
index++;
}
if (current) {
return index;
}
return 0;
}
//输出链表
template<class T>
void Chain<T>::OutPut(ostream& out)const {
//将链表元素输送至输出流
ChainNode<T>* current;
for (current = first; current; current = current->link) {
cout << current->data << " ";
}
}
//重载
template<class T>
ostream& operator<<(ostream& out, const Chain<T>& x) {
x.OutPut(out);
return out;
}
从链表中删除一个元素
三种情形:
- k小于1或链表为空;
- 第一个元素将被删除且链表不为空;
- 从一个非空的链表中删除首元素之外的其他元素.
//从链表中删除一个元素
template<class T>
Chain<T>&Chain<T>::Delete(int k,T& x) {
//把第k个元素取至x,然后从链表中删除第k个元素
//如果不存在第k个元素,则引发异常OutOfBounds
if (k < 1 || !first) {
throw OutOfBounds(); //不存在第k个元素
}
//p最终将指向第k个节点
ChainNode<T>* p = first;
//将p移动至第k个元素,并从链表中删除该元素
if (k == 1) {
//p已经指向第k个元素
first = first->link; //删除
}
else {
//用p指向第k-1个元素
ChainNode<T>* q = first;
for (int index = 1; index < k - 1 && q; index++) {
q = q->link;
}
if (!q || !q->link) {
throw OutOfBounds(); //不存在第k个元素
}
p = q->link; //存在第k个元素
q->link = p->link; //从链表中删除该元素
}
//保存第k个元素,并释放节点p
x = p->data;
delete p;
return *this;
}