这一章主要内容:
* 抽象数据类型(ADT)的概念
* 实现表/栈/队列
* 了解这三个数据结构的应用场景
1. ADT
ADT: abstract data type, 是抽象的数学模型,在该模型上定义了一系列的操作。使用它的人,不需要了解它的存储方式,只关心它的逻辑特征。可以使用三元组的方法来表示(D,S,P),D是数据对象,S是数据之间的关系,P是对数据的基本操作,具体介绍,可以参考帖子:点击打开链接
2. 表ADT
表的数据自然是单个元素,而元素之间的关系是前驱与后继,操作包括查找/插入/删除等操作。
2.1 表的简单数组实现
数组是静态分配的,查找是花费常数时间(O(1))的,而插入和删除是线性时间(O(N)),如果操作发生在表的末尾,就不需要移动任何时间,花费常数时间(O(1)).
在C++中,包含有公共数据结构的实现--STL(standard template library)-标准模板库。
Vector基于数组实现,可以复制并且其占用的内存可以自动回收(通过析构函数),可以调整Vector的大小,以及容量(容量的改变是通过为基本数组分配一个新的内存块,然后复制旧的内存块到新块中,再释放旧块的内存)。在进行插入和删除操作时,需要位置标记,这里使用通用的迭代器(其实就是指针)。有三个问题:如何得到迭代器;迭代器执行什么操作;哪些表ADT方法需要迭代器作为形参。源码如下:
/*
* 用数组的方式实现线性表
*/
#include<iostream>
using namespace std;
template <typename Object>
class Vector
{
private:
int theSize;
int theCapacity;
Object * objects;
public:
//构造函数
explicit Vector(int initSize = 0) : theSize(initSize), theCapacity( initSize + SPACE_CAPACITY) {
objects = new Object[theCapacity];
}
//复制构造函数
Vector( const Vector &rhs) : objects(NULL) {
operator=( rhs);
}
//析构函数
~Vector() {
delete[] objects;
}
//重载复制操作符
const Vector & operator= (const Vector &rhs) {
if( this != &rhs) {
delete [] objects;
theSize = rhs.size();
theCapacity = rhs.capacity();
objects = new Object[ capacity() ];
for(int k=0; k<size(); k++) {
objects[k] = rhs.objects[k];
}
}
return this;
}
//调整Vector的大小
void resize(int newSize) {
if(newSize > theCapacity)
reserve( newSize*2 + 1);
theSize = newSize;
}
//调整容量
void reserve( int newCapacity) {
if( newCapacity < theSize)
return;
Object *oldArray = objects;
objects = new Object[newCapacity];
for( int k=0; k < newCapacity; k++)
objects[k] = oldArray[k];
theCapacity = newCapacity;
delete[] oldArray;
}
//重载取元素字符[]
Object & operator[] (int index) {
return objects[index];
}
//重载取元素字符[],并且返回的元素为左值,不能修改
const Object & operator[] (int index) const {
return objects[index];
}
//判断Vector是否为空
bool empty() const {
return size()==0;
}
//获得Vector的大小
int size() const {
return theSize;
}
//获得Vector的容量
int capacity() const {
return theCapacity;
}
//在尾部插入元素
void push_back(const Object &x) {
if(theSize == theCapacity) {
reserve( 2*theCapacity +1);
}
objects[theSize] = x;
theSize++;
}
//弹出尾部元素
void pop_back() {
theSize--;
}
//返回尾部元素
const Object & back() const {
return objects[theSize-1];
}
//使用指针来定义迭代器
typedef Object * iterator;
typedef const Object * const_iterator;
//获取Vector开始的迭代器
iterator begin() {
return &objects[0];
}
const_iterator begin() const {
return &objects[0];
}
//获取Vector结束的迭代器
iterator end() {
return &objects[ size() ];
}
const_iterator end() const {
return &objects[size()];
}
enum { SPACE_CAPACITY = 16 };
};
int main()
{
Vector<int> test;
test.push_back(2);
test.push_back(3);
//通过迭代器来打印Vector的内容
//TODO:怎么调用前面定义的迭代器呢
for(Vector<int>::iterator itr = test.begin(); itr < test.end(); itr++) {
//for(int i=0; i < test.size(); i++) {
cout << *itr << " ";
}
cout << endl;
bool vectorEmpty = test.empty();
if(vectorEmpty)
cout << "the vector is empty" << endl;
return 0;
}Vector是静态分配的数组,而List的空间则不是连续的,通过前向和后向指针来表明元素之间的关系。源码如下:
#include<iostream>
using namespace std;
template <typename Object>
class List
{
private:
//链表节点的定义,包括数据、指向前驱节点的指针、指向后驱节点的指针
struct Node {
Object data;
Node *prev;
Node *next;
Node(const Object & d = Object(), Node *p = NULL, Node *n = NULL) : data(d), prev(p), next(n) {}
};
private:
int theSize;
Node *head;
Node *tail;
void init()
{
theSize = 0;
head = new Node;
tail = new Node;
head->next = tail;
tail->prev = head;
}
public:
//const_iterator define
class const_iterator
{
protected:
Node *current;
Object & retrieve() const
{
return current->data;
}
const_iterator(Node *p) : current(p) { }
friend class List<Object>;
public :
const_iterator():current(NULL) {}
const Object & operator*() const
{
return retrieve();
}
//前缀,++itr
const_iterator & operator++ ()
{
current = current->next;
return *this;
}
//后缀,itr++
const_iterator & operator++(int)
{
const_iterator old = *this;
++(*this);
return old;
}
bool operator== (const const_iterator &rhs) const
{
return current == rhs.current;
}
bool operator!= (const const_iterator &rhs) const
{
return !( *this == rhs);
}
};
//iterator define
class iterator : public const_iterator
{
protected:
iterator(Node *p) : const_iterator(p) {}
friend class List<Object>;
public:
iterator() {}
Object & operator* ()
{
return this->retrieve();
}
const Object & operator* () const
{
return const_iterator::operator*();
}
iterator & operator++ ()
{
this->current = this->current->next;
return *this;
}
iterator operator++(int)
{
iterator old = *this;
++(*this);
return old;
}
};
public:
List()
{
init();
}
List(const List & rhs)
{
init();
*this = rhs;
}
~List()
{
clear();
delete head;
delete tail;
}
const List & operator= (const List & rhs)
{
if( this == &rhs)
return *this;
clear();
for( const_iterator itr = rhs.begin(); itr != rhs.end(); itr++) {
push_back( *itr);
}
}
iterator begin()
{
return iterator( head-> next );
}
const_iterator begin() const
{
return const_iterator( head->next);
}
iterator end()
{
return iterator(tail);
}
const_iterator end() const
{
return const_iterator(tail);
}
int size() const
{
return theSize;
}
bool empty()
{
return size() == 0;
}
void clear()
{
while( !empty() )
pop_front();
}
Object & front()
{
return *begin();
}
const Object & front() const
{
return *begin();
}
Object & back()
{
return *--end();
}
const Object & back() const
{
return *--end();
}
void push_front( const Object &x)
{
insert( begin(), x);
}
void push_back( const Object &x)
{
insert( end(), x);
}
void pop_front()
{
erase( begin() );
}
void pop_back()
{
erase(--end());
}
//插入节点,先创建这个节点,给它的prev和next赋值,然后再让p->prev->next和p->prev都指向这个新创建的节点
iterator insert(iterator itr, const Object &x)
{
Node *p = itr.current;
theSize++;
//return iterator(p->prev = p->prev->next = new Node(x, p->prev, p));
Node *newNode = new Node(x, p->prev, p);
p->prev->next = newNode;
p->prev = newNode;
return iterator(newNode);
}
//删除节点,让p->prev->next指向p->next即可
iterator erase(iterator itr)
{
Node *p = itr.current;
p->prev->next = p->next;
p->next->prev = p->prev;
iterator result(p->next);
delete p;
theSize--;
return result;
}
iterator erase(iterator start, iterator end)
{
for( iterator itr = start; itr != end;)
itr = erase(itr);
return end;
}
};
int main()
{
List<int> test;
test.push_back(1);
test.push_back(2);
test.push_back(3);
for(List<int>::iterator itr = test.begin(); itr != test.end(); itr++) {
cout << *itr << " " ;
}
cout << endl;
return 0;
}issues:在编写代码中,出现了一次段错误,使用gdb调试是在执行插入操作时,p->prev->next这一句会出现段错误,查看p->prev指向了NULL,发现在初始化函数init中,将tail->prev=head,写成了tail->next=head了。这样逻辑上肯定就不对了。
3. 栈ADT
栈其实就是一个表,只是操作有所不同而已。在栈中是后入先出(LIFO)。push是元素入栈--输入,pop是元素出栈,top是取出元素。这里通过继承的方式,使用Vector来实现简单的栈。
#include "vector.cpp"
template<typename Object>
class Stack: public Vector<Object>
{
public:
void push(const Object & data)
{
this->push_back(data);
}
void pop()
{
this->pop_back();
}
const Object &top()
{
return this->back();
}
};
int main()
{
Stack<int> test;
test.push(1);
test.push(2);
test.pop();
int a = test.top();
cout << "the top element is " << a << endl;
return 0;
}#include<iostream>
using namespace std;
//循环输出多个元素
void print(int start, int end)
{
if(start == end)
return;
cout << start << endl;
start++;
print(start, end);
}
int main()
{
print(1,20000000);
return 0;
}在我的PC上,运行到261812次就会出错。使用:
ulimit -s可以查看PC的默认栈空间,我的为8192,我将空间*2=16384之后,运行次数也会加倍,变为:523989。当然现在编译器都可以做相应的优化(gcc -O2),将尾递归转化为循环,常量栈,具体帖子可以参见清水河畔的交流贴:点击打开链接 点击打开链接
3. 队列ADT
同样,队列ADT也是表,是一个先进先出的表(FIFO). 可以考虑用循环数组实现。代码后面会补上。。明天回家。。
应用:排队论等
原文地址:http://blog.csdn.net/zy416548283/article/details/43411943