文章目录
线性表抽象基类迭代器类的层次设计使用方法
变长数组描述关于构造函数、析构函数、赋值、复制关于扩容、缩容性能分析完整代码及测试
链表描述链表的种类性能分析完整代码及测试
数组描述与链表描述的对比
线性表
抽象基类
这个抽象基类实际上约定了一个线性表应该具有那些功能。
#ifndef DATASTRUCTURE_LINEARLIST_H
#define DATASTRUCTURE_LINEARLIST_H
template<class T>
class LinearList{
protected:
int length
;
public:
LinearList() {
length
= 0;
}
virtual ~LinearList() {}
virtual T
& get(int index
) const = 0;
virtual void insert(int index
,const T
& t
) = 0;
virtual void push_back(const T
& t
) = 0;
virtual void push_front(const T
& t
) = 0;
virtual void pop_back() = 0;
virtual void pop_front() = 0;
virtual void erase(int index
) = 0;
virtual void clear() = 0;
bool empty(){
return length
==0;
}
int size(){
return length
;
}
};
#endif
迭代器
迭代器提供了一种通用的方式去访问和操控容器对象。
类的层次设计
作为私有内部嵌套类定义在类的内部(私有是为了不让外部的调用者显式操纵迭代器类);迭代器的方法(如++,--,*等等作为公有方法暴露给外面);
private:
class arrayList_iterator{
protected:
T
* pos
;
public:
arrayList_iterator(T
*pos
= nullptr) : pos(pos
) {}
};
对外部调用者而言使用别名 typedef arrayList_iterator iterator;
另外其他一些迭代器的接口在外部类的公有方法中给出(它们并不是迭代器对象自己调用的,故而不是在内部类定义),例如(begin();end()等等)。
iterator
begin(){
return iterator(arr
);
}
iterator
end(){
return iterator(arr
+this->length
);
}
使用方法
声明迭代器 下面这句话相当于定义了属于线性表类的一个成员变量,不过这个变量的类型是我们自己写的类类型,实际上创建了一个新的对象。 待它的生命周期结束会自动释放空间。
ArrayList
<T
>::iterator it
= arrayList
.begin();
读写操作 就像操控指针一样操控迭代器对象即可,因为我们已经重载了那些操作指针的运算符。 迭代器实际上是对指针的再封装。
T t
= *it
;
T tt
;
*it
= tt
;
it
++;
变长数组描述
关于构造函数、析构函数、赋值、复制
这四个函数最关键的就是动态分配数组。
关于扩容、缩容
扩容机制 在数组容量不够的时候,将数组容量扩大一倍。
缩容机制 为了内存空间的高效使用,不能只扩容,不缩小容量。 所以,在线性表的元素的数量小于等于容量的1/4时,减小一半的空间。
性能分析
随机查询的函数效率高:
g
e
t
(
)
、
s
e
t
(
)
get()、set()
get()、set()的函数性能为
O
(
1
)
O(1)
O(1) 插入和删除尾部的元素效率高,插入和删除头部的元素效率低,性能为
O
(
n
)
O(n)
O(n) 并且,在变化容量的时候,比较耗时。
完整代码及测试
#ifndef DATASTRUCTURE_ARRAYLIST_H
#define DATASTRUCTURE_ARRAYLIST_H
#include "LinearList.h"
#include <iostream>
using std
::cout
;
using std
::cerr
;
using std
::endl
;
using std
::bad_alloc
;
using std
::min
;
template<class T>
class ArrayList : public LinearList
<T
>{
private:
class arrayList_iterator{
protected:
T
* pos
;
public:
arrayList_iterator(T
*pos
= nullptr) : pos(pos
) {}
bool operator==(const arrayList_iterator
&rhs
) const {
return pos
== rhs
.pos
;
}
bool operator!=(const arrayList_iterator
&rhs
) const {
return pos
!= rhs
.pos
;
}
T
& operator*(){
return *pos
;
}
T
* operator->(){
return pos
;
}
arrayList_iterator
& operator++(){
++pos
;
return *this;
}
arrayList_iterator
operator++(int){
iterator old
= *this;
++pos
;
return old
;
}
};
public:
typedef arrayList_iterator iterator
;
ArrayList(int capacity
= DEFAULT_SIZE
):capacity(capacity
){
if(capacity
<=0){
throw "Illegal parameter.";
}
arr
= new T
[capacity
];
}
virtual ~ArrayList() {
delete [] arr
;
}
ArrayList(const ArrayList
<T
> & b
);
ArrayList
<T
>& operator=(const ArrayList
<T
> & b
);
T
& get(int index
) const override
;
T
& operator[] (int index
){
check(index
);
return arr
[index
];
}
const T
& operator[] (int index
) const{
check(index
);
return arr
[index
];
}
void insert(int index
, const T
&t
) override
;
void push_back(const T
&t
) override
{
insert(this->length
,t
);
}
void push_front(const T
&t
) override
{
insert(0,t
);
}
void erase(int index
) override
;
void pop_back() override
{
erase(this->length
-1);
}
void pop_front() override
{
erase(0);
}
void clear() override
;
void resize(int new_capacity
);
void printAll() const {
for(int i
=0;i
<this->length
;i
++){
cout
<<arr
[i
]<<" ";
}
cout
<<endl
;
}
iterator
begin(){
return iterator(arr
);
}
iterator
end(){
return iterator(arr
+this->length
);
}
private:
static const int DEFAULT_SIZE
= 10;
int capacity
;
T
*arr
= nullptr;
void check(int index
) const;
void expand_capacity(){
if(this->length
==capacity
){
resize(2*capacity
);
}
}
void reduce_capacity(){
if(this->length
<capacity
/4 && capacity
/2>=10){
resize(capacity
/2);
}
}
};
template<class T>
ArrayList
<T
>::ArrayList(const ArrayList
<T
> & b
){
if(this==&b
) return;
if(arr
!= nullptr) delete [] arr
;
arr
= new T
[b
.capacity
];
capacity
= b
.capacity
;
this->length
= b
.length
;
for(int i
=0;i
<this->length
;i
++){
arr
[i
] = b
.arr
[i
];
}
}
template<class T>
ArrayList
<T
>& ArrayList
<T
>::operator=(const ArrayList
<T
> & b
){
if(this==&b
) return *this;
if(arr
!= nullptr) delete [] arr
;
arr
= new T
[b
.capacity
];
capacity
= b
.capacity
;
this->length
= b
.length
;
for(int i
=0;i
<this->length
;i
++){
arr
[i
] = b
.arr
[i
];
}
return *this;
}
template<class T>
T
& ArrayList
<T
>::get(int index
) const {
check(index
);
return arr
[index
];
}
template<class T>
void ArrayList
<T
>::check(int index
) const {
if(index
<0 || index
>=this->length
){
throw "Index out of bounds";
}
}
template<class T>
void ArrayList
<T
>::insert(int index
, const T
&t
) {
if(index
<0 || index
>this->length
){
throw "Illegal parameter.";
}
expand_capacity();
if(index
==this->length
){
arr
[this->length
] = t
;
}else{
for(int pos
= this->length
;pos
>index
;pos
--){
arr
[pos
] = arr
[pos
-1];
}
arr
[index
] = t
;
}
this->length
++;
}
template<class T>
void ArrayList
<T
>::erase(int index
) {
check(index
);
if(index
<this->length
-1){
for(int pos
=index
+1;pos
<=this->length
-1;pos
++){
arr
[pos
-1] = arr
[pos
];
}
}
arr
[--this->length
].~T();
reduce_capacity();
}
template<class T>
void ArrayList
<T
>::clear() {
delete [] arr
;
arr
= new T
[DEFAULT_SIZE
];
capacity
= DEFAULT_SIZE
;
this->length
= 0;
}
template<class T>
void ArrayList
<T
>::resize(int new_capacity
){
if(new_capacity
<0){
throw "Illegal parameter.";
}
try{
T
* a
= new T
[new_capacity
];
for(int i
=0;i
<std
::min(new_capacity
,this->length
);i
++){
a
[i
] = arr
[i
];
}
delete [] arr
;
arr
= a
;
capacity
= new_capacity
;
this->length
= min(new_capacity
,this->length
);
} catch (bad_alloc e
) {
cerr
<<"Out of memoty."<<endl
;
exit(1);
}
}
#endif
测试代码
#include "ArrayList.h"
using namespace std
;
struct Person
{
string name
;
int age
;
Person() {}
Person(const string
&name
, int age
) : name(name
), age(age
) {}
friend ostream
& operator<<(ostream
&os
,const Person
& person
){
os
<<person
.name
<<" "<<person
.age
<<" ";
return os
;
}
};
int main(){
ArrayList
<Person
> arrayList
;
arrayList
.push_back(Person("Green",10));
arrayList
.push_back(Person("Brown",12));
arrayList
.push_back(Person("White",15));
arrayList
.push_back(Person("Red",16));
for(ArrayList
<Person
>::iterator it
= arrayList
.begin();it
!=arrayList
.end();it
++){
cout
<<*it
<<endl
;
}
return 0;
}
链表描述
链表的种类
单向链表 每个节点的存储 元素和指向下一个节点的指针。双向链表 每个节点的存储 元素,指向下一个节点的指针 以及 指向前一个节点的指针。循环链表 头结点的pre指针指向尾结点,尾结点的next指针指向头结点
为了避免对头结点何尾结点特殊分析,我们增加两个特殊的哨兵节点,head和tail。
性能分析
随机访问,效率很低:
O
(
n
)
O(n)
O(n) 但是不论是插入和删除,效率都很高:
O
(
1
)
O(1)
O(1) 另外,由于没有扩容机制——所有节点都是按需分配的,所以性能没有波动。 空间上,一方面,没有开辟一块连续的内存空间,高效使用了计算机内存。 另一方面,每个节点为了能联系下一个节点,有必须存储下一个节点的地址。
完整代码及测试
#ifndef DATASTRUCTURE_LINKEDLIST_H
#define DATASTRUCTURE_LINKEDLIST_H
#include "LinearList.h"
template<class T>
class LinkedList : public LinearList
<T
>{
private:
class Node{
public:
T val
;
Node
* next
= nullptr, *previous
= nullptr;
Node() {}
Node(T val
) : val(val
) {}
};
class linkedList_iterator{
private:
Node
* pos
;
public:
linkedList_iterator(Node
*pos
= nullptr) : pos(pos
) {}
bool operator==(const linkedList_iterator
&rhs
) const {
return pos
== rhs
.pos
;
}
bool operator!=(const linkedList_iterator
&rhs
) const {
return pos
!= rhs
.pos
;
}
T
& operator*(){
return pos
->val
;
}
T
* operator->(){
return &(pos
->val
);
}
linkedList_iterator
& operator++(){
pos
= pos
->next
;
return *this;
}
linkedList_iterator
operator++(int){
Node
* temp
= pos
;
pos
= pos
->next
;
return iterator(temp
);
}
};
Node
*head
,*tail
;
void check(int index
) const;
public:
typedef linkedList_iterator iterator
;
LinkedList() ;
virtual ~LinkedList() ;
LinkedList(const LinkedList
<T
>&) = delete ;
LinkedList
<T
>& operator=(const LinkedList
<T
>&) = delete ;
virtual T
&get(int index
) const;
virtual void insert(int index
, const T
&t
) ;
virtual void push_back(const T
&t
) ;
virtual void push_front(const T
&t
) ;
virtual void pop_back() ;
virtual void pop_front() ;
virtual void erase(int index
) ;
virtual void clear() ;
void printAll() const {
Node
* cur
= head
->next
;
while(cur
!=tail
){
cout
<<cur
->val
<<" ";
cur
= cur
->next
;
}
cout
<<endl
;
}
iterator
begin(){
return iterator(head
->next
);
}
iterator
end(){
return iterator(tail
);
}
};
template<class T>
LinkedList
<T
>::LinkedList() {
head
= new Node
;
tail
= new Node
;
head
->next
= tail
;
tail
->previous
= head
;
}
template<class T>
LinkedList
<T
>::~LinkedList() {
Node
*cur
= head
,*temp
;
while(cur
!= nullptr){
temp
= cur
->next
;
delete cur
;
cur
= temp
;
}
}
template<class T>
T
& LinkedList
<T
>::get(int index
) const {
check(index
);
Node
*cur
= head
->next
;
for(int i
=0;i
<index
;i
++){
cur
= cur
->next
;
}
return cur
->val
;
}
template<class T>
void LinkedList
<T
>::insert(int index
, const T
&t
) {
if(index
==this->length
){
push_back(t
);
return;
}
check(index
);
Node
* cur
= head
;
for(int i
=0;i
<index
;i
++){
cur
= cur
->next
;
}
Node
* newNode
= new Node(t
);
newNode
->next
= cur
->next
;
cur
->next
->previous
= newNode
;
newNode
->previous
= cur
;
cur
->next
= newNode
;
this->length
++;
}
template<class T>
void LinkedList
<T
>::push_back(const T
&t
) {
Node
* newNode
= new Node(t
);
newNode
->previous
= tail
->previous
;
tail
->previous
->next
= newNode
;
newNode
->next
= tail
;
tail
->previous
= newNode
;
this->length
++;
}
template<class T>
void LinkedList
<T
>::push_front(const T
&t
) {
Node
* newNode
= new Node(t
);
newNode
->next
= head
->next
;
head
->next
->previous
= newNode
;
newNode
->previous
= head
;
head
->next
= newNode
;
this->length
++;
}
template<class T>
void LinkedList
<T
>::erase(int index
) {
check(index
);
Node
* temp
= head
;
for(int i
=0;i
<index
;i
++){
temp
= temp
->next
;
}
Node
* cur
= temp
->next
;
temp
->next
= cur
->next
;
cur
->next
->previous
= temp
;
delete cur
;
this->length
--;
}
template<class T>
void LinkedList
<T
>::pop_back() {
Node
* cur
= tail
->previous
;
tail
->previous
= cur
->previous
;
cur
->previous
->next
= tail
;
delete cur
;
this->length
--;
}
template<class T>
void LinkedList
<T
>::pop_front() {
Node
* cur
= head
->next
;
head
->next
= cur
->next
;
cur
->next
->previous
= head
;
delete cur
;
this->length
--;
}
template<class T>
void LinkedList
<T
>::clear() {
Node
*cur
= head
->next
,*temp
;
while(cur
!=tail
){
temp
= cur
->next
;
delete cur
;
cur
= temp
;
}
this->length
= 0;
}
template<class T>
void LinkedList
<T
>::check(int index
) const {
if(index
<0 || index
>=this->length
){
throw "Index out of bounds";
}
}
#endif
数组描述与链表描述的对比
在对随机访问有着高性能要求的场景,使用数组描述。在随机访问稀疏,插入删除频繁的场景,使用链表。在存储元素是一个很大的对象时,比较适合使用链表。