文中内容收集整理自《数据结构与算法分析–c++语言描述(第四版) 》,版权归原书所有
我们将处理形如A0, A1, A2, … , AN-1的一般的表.这个表的大小是N. 我们将称大小为0的表为空表(empty list).对于除空表外的任何表, 我们说Ai后继Ai-1(或继Ai-1之后),并称Ai-1前驱Ai(i>1).表中的第一个元素是A0, 而最后一个元素是AN-1. 我们将不定义A0的前驱元,也不定义AN-1的后继元. 元素Ai在表中的位置(position)为i.
表ADT上进行操作的集合一般有:
printList: 打印表;
makeEmpty: 清空表, c++中一般将该功能的函数命名为clear;
find: 在表结构中的函数名一般是indexOf;
insert: 从表的某个位置插入元素;
remove: 从表的某个位置删除元素, 在c++中也会命名为erase;
findKth:返回某个位置上(作为参数而被指定)的元素, 在c++中通常命名为at, 并且会重载下标运算符([]).
一个函数的功能怎样才算恰当,完全要 由程序设计者来确定. 这类似于对特殊情况的处理. 我们还可以添加一些运算, 比如next和previous, 他们会取一个位置作为参数并分别返回其后继元和前驱元的位置.
元素存储在一段连续的内存中
1. 解决了使用数组时需要对表的大小的最大值进行估计的问题
2. 优点是在常数时间内是可索引的, 即索引操作的时间复杂度为O(1)
3. 插入新项或删除已有项是线性开销的(O(n)),除非这些操作发生在vector的末尾
存储元素的内存不连续
为了避免插入和删除的线性开销,需要保证表可以不连续存储.
链表由一系列节点组成,这些节点不必在内存中连续。每个节点均含有表元素和一个链(link),该链指向包含该元素后继元的另一个节点。称之为next链(next link)。最后的一个单元的next链指向nullptr。这种链表称为单链表(单向链表)。
每一个节点同时持有一个指向它在表中的前驱节点的链和指向后继节点的链,这种链表称为双向链表
1.链表的优点是如果知道发生变化的位置,从链表中插入或删除一个元素的时间复杂度是O(1)
2. 缺点是不容易索引
两种方法共同的缺点是查找效率比较低,都是线性时间复杂度O(n)
leetcode-刷题链接-数组和链表
vector的简易实现:
#pragma once
#include
template <typename T>
class Vector
{
public:
typedef T * iterator;
typedef const T * const_iterator;
explicit Vector(int initSize = 0)
: m_size(initSize),
m_capacity(initSize + SPARE_CAPACITY)
{
m_pObjects = new T[m_capacity];
}
Vector(const Vector &rhs)
: m_size(rhs.m_size),
m_capacity(rhs.m_capacity),
m_pObjects(nullptr)
{
m_pObjects = new T[m_capacity];
for (int k = 0; k < m_size; ++k)
{
m_pObjects[k] = rhs.m_pObjects[k];
}
}
Vector & operator=(const Vector &rhs)
{
Vector copy = rhs;
std::swap(*this, copy);
return *this;
}
~Vector()
{
delete[] m_pObjects;
}
Vector(Vector &&rhs)
: m_size(rhs.m_size),
m_capacity(rhs.m_capacity),
m_pObjects(rhs.m_pObjects)
{
rhs.m_pObjects = nullptr;
rhs.m_size = 0;
rhs.m_capacity = 0;
}
const Vector & operator=(const Vector &&rhs)
{
std::swap(m_size, rhs.m_size);
std::swap(m_capacity, rhs.m_capacity);
std::swap(m_pObjects, rhs.m_pObjects);
return *this;
}
void resize(int newSize)
{
if (newSize > m_capacity)
{
reserve(newSize * 2 + 1);
}
m_size = newSize;
}
void reserve(int newCapacity)
{
if (newCapacity < m_size)
{
return;
}
T *newArray = new T[newCapacity];
for (int k = 0; k < m_size; ++k)
{
newArray[k] = std::move(m_pObjects[k]);
}
m_capacity = newCapacity;
std::swap(m_pObjects, newArray);
delete[] newArray;
}
T & operator[](int index)
{
return m_pObjects[index];
}
const T & operator[](int index) const
{
return m_pObjects[index];;
}
bool empty() const
{
return size() == 0;
}
int size() const
{
return m_size;
}
int capacity() const
{
return m_capacity;
}
void push_back(const T & value)
{
if (m_size == m_capacity)
{
reserve(2 * m_capacity + 1);
}
m_pObjects[m_size++] = value;
}
void push_back(const T && value)
{
if (m_size == m_capacity)
{
reserve(2 * m_capacity + 1);
}
m_pObjects[m_size++] = std::move(value);
}
void pop_back()
{
--m_size;
}
const T & back() const
{
return m_pObjects[m_size - 1];
}
iterator begin()
{
return &m_pObjects[0];
}
const_iterator begin() const
{
return &m_pObjects[0];
}
iterator end()
{
return &m_pObjects[size()];
}
const_iterator end() const
{
return &m_pObjects[size()];
}
enum
{
SPARE_CAPACITY = 16
};
private:
int m_size;
int m_capacity;
T *m_pObjects;
};
list的简易实现
#pragma once
#include
class IteratorOutOfBoundsException : public std::exception
{
public:
IteratorOutOfBoundsException() throw()
: std::exception("iterator out of bounds exception.")
{}
};
class IteratorMismatchException : public std::exception
{
public:
IteratorMismatchException() throw()
: std::exception("iterator mismatch exception.")
{}
};
template <typename T>
class List
{
private:
struct Node
{
T data;
Node *prev;
Node *next;
Node(const T & d = T{}, Node *p = nullptr, Node *n = nullptr)
: data{ d }, prev{ p }, next{ n }
{}
Node(T &&d, Node *p = nullptr, Node *n = nullptr)
: data{std::move(d)}, prev{p}, next{n}
{}
};
public:
class const_iterator
{
public:
const_iterator() : pList{ nullptr }, current { nullptr }
{}
const T & operator*() const
{
return retrieve();
}
const_iterator & operator++()
{
current = current->next;
return *this;
}
const_iterator operator++(int)
{
const_iterator old = *this;
++(*this);
return old;
}
const_iterator operator--()
{
current = current->prev;
return *this;
}
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);
}
protected:
const List *pList;
Node *current;
T & retrieve() const
{
return current->data;
}
const_iterator(const List &owner, Node *p)
: pList{ &owner }, current{ p }
{}
void assertIsValid() const
{
if (pList == nullptr || current == nullptr || current == pList->head)
{
throw IteratorOutOfBoundsException{};
}
}
friend class List;
};
class iterator : public const_iterator
{
public:
iterator() {}
T & operator*()
{
return const_iterator::retrieve();
}
const T operator*() const
{
return const_iterator::retrieve();
}
iterator & operator++()
{
current = current->next;
return *this;
}
iterator operator++(int)
{
iterator old = *this;
++(*this);
return old;
}
iterator operator--()
{
current = current->prev;
return *this;
}
iterator operator--(int)
{
const_iterator old = *this;
--(*this);
return old;
}
protected:
iterator(const List &owner, Node *p) : const_iterator{owner, p}
{}
friend class List;
};
public:
List()
{
init();
}
List(const List &rhs)
{
init();
for (auto &x : rhs)
{
push_back(x);
}
}
~List()
{
clear();
delete head;
delete tail;
}
List & operator=(const List &rhs)
{
List copy = rhs;
std::swap(*this, copy);
return *this;
}
List(List &&rhs)
: m_size(rhs.m_size), head(rhs.head), tail(rhs.tail)
{
rhs.m_size = 0;
rhs.head = nullptr;
rhs.tail = nullptr;
}
List &operator=(List &&rhs)
{
std::swap(m_size, rhs.m_size);
std::swap(head, rhs.head);
std::swap(tail, rhs.tail);
return *this;
}
iterator begin()
{
iterator itr{ *this, head };
return ++itr;
}
const_iterator begin() const
{
const_iterator itr{ *this, head };
return ++itr;
}
iterator end()
{
return { *this, tail };
}
const_iterator end() const
{
return { *this, tail };
}
int size() const
{
return m_size;
}
bool empty() const
{
return size() == 0;
}
void clear()
{
while (!empty())
{
pop_front();
}
}
T& front()
{
return *begin();
}
const T& front() const
{
return *begin();
}
T& back()
{
return *--end();
}
const T& back() const
{
return *--end();
}
void push_front(const T &x)
{
insert(begin(), x);
}
void push_front(T &&x)
{
insert(end(), std::move(x));
}
void push_back(const T &x)
{
insert(end(), x);
}
void push_back(T &&x)
{
insert(end(), std::move(x));
}
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
iterator insert(iterator itr, const T &x)
{
itr.assertIsValid();
if (itr.pList != this)
{
throw IteratorMismatchException{};
}
Node *p = itr.current;
m_size++;
return {p->prev = p->prev->next = new Node{x, p->prev, p}}
}
iterator insert(iterator itr, T &&x)
{
itr.assertIsValid();
if (itr.pList != this)
{
throw IteratorMismatchException{};
}
Node *p = itr.current;
m_size++;
return {*this, p->prev = p->prev->next = new Node{ std::move(x), p->prev, p } };
}
iterator erase(iterator itr)
{
itr.assertIsValid();
if (itr.pList != this)
{
throw IteratorMismatchException{};
}
Node *p = itr.current;
iterator retVal{*this, p->next };
p->prev->next = p->next;
p->next->prev = p->prev;
delete p;
m_size--;
return retVal;
}
iterator erase(iterator from, iterator to)
{
for (iterator itr = from; itr != to)
{
itr = erase(itr);
}
}
private:
int m_size;
Node *head;
Node *tail;
void init()
{
m_size = 0;
head = new Node;
tail = new Node;
head->next = tail;
tail->prev = head;
}
};