双向类模板链表
一.双向链表
- 首先我们要复习下双向链表的大概思路:
(当然实际储存并不这样摆放的,只是便于理解)
这是一个节点的基本结构:
prev:是一个指向前面一个节点的指针。
next: 是一个指向后面一个节点的指针。
data: 任意T类型的数据。
图例:
(三个节点:head, 中间,tail)
可以看到
中间节点的prev存放着head这个节点的地址
中间节点的next存放着tali这个节点的地址
二.双向链表类模板的思路
分为三部分:
- 节点的构造(struct Node)
代码:
template<typename T>
struct Node
{
T data;
Node<T>* pre;//结构体自引用
Node<T>* next;
Node(const T& item, Node* p = NULL, Node* n = NULL) :data(item), pre(p), next(n) {}
};
- 迭代器(class Iterator)
代码:
template<typename T>
class Iterator
{
public:
Node<T>* current;// 因为class List用这个成员也(可以用继承)
};
解释:迭代器的操作对象是一个节点的地址 而不是一个节点放的数据的地址,所以迭代器也能访问其中的数据
- 链表(class List)
代码:
template<typename T>
class List
{
Node<T>* head;
Node<T>* tail;
int size;
}
解释:因为链表操作对象是一个个节点Node的对象, 所以需要创造head与tail节点连成链,再把其余的节点插入进来
三.双向链表类模板的思路
3.1 节点
template<typename T>
struct Node
{
T data;
Node<T>* pre;
Node<T>* next;
//之后要用new 创造Node
Node(const T& item = T(), Node* p = NULL, Node* n = NULL) :data(item), pre(p), next(n) {}
};
3.2迭代器
template<typename T>
class Iterator
{
public:
Node<T>* current;
//构造函数
Iterator() :current(NULL) {}
Iterator(Node<T>* c) { current = c; }
Iterator(const Iterator& itr) :current(itr.current) {}
//重载函数
Iterator& operator ++() { current = current->next; return *this; }//前缀++
Iterator& operator ++(int n)
{
Iterator old = *this;
++(*this);
return old;
};//后缀++
Iterator& operator --() { current = current->pre; return *this; }//前缀--
Iterator& operator --(int n) { Iterator old = *this; --(*this); return old; }//后缀--
T& operator *() { return current->data; }
const T& operator *() const { return current->data; }
//判断
bool operator ==(const Iterator& itr) { return (current == itr.current);
bool operator !=(const Iterator& itr) { return (current != itr.current); }
};
解释说明
1.不管是++还是–,我们都希望是从一个节点移动到下一个节点。比如++,从 current 移到 current->pre(这里就装着下一个节点的地址)
2.而我们用*,我们希望取出来的是data,所以我们想要这个*current=current->data
3.3链表(重点)
这是全部的代码 看着很头晕。 别着急 可以先不看 后面我会放图片详细解释
template<typename T>
class List
{
Node<T>* head;
Node<T>* tail;
int size;
public:
typedef Iterator<T>iterator;
typedef const Iterator<T> const_iterator;
List()
{
size = 0;
head = new Node<T>();
tail = new Node<T>();
tail->pre = head;
head->next = tail;//连起来 变成链表
};
~List()
{
Clear();
delete head;
delete tail;
};
List(const List& l)
{
size = l.size;
head = new Node<T>();
tail = new Node<T>();
tail->pre = head;
head->next = tail;
for (iterator temp = l.Begin(); temp != l.End(); temp++)
push_back(*temp);//一个一个节点复制
}
void Clear()
{
iterator temp = Begin();
while (temp != End())
temp = Erase(temp);
}
iterator Begin() { return iterator(head->next); }
const_iterator Begin() const { return iterator(head->next); }
iterator End() { return iterator(tail); }
const_iterator End()const { return iterator(tail); }
T& Front() { return *Begin(); }
const T& Front()const { return *Begin(); }
T& Back() { return *(--End()); }
const T& Back()const { return *(--End()); }
List& operator =(const List& l)
{
if (this != &l)
{
Clear();
size = l.size;
for (iterator temp = l.Begin(); temp != l.End(); temp++)
push_back(*temp);
return *this;
}
}
void push_front(const T& item) { Insert(Begin(), item); }
void push_back(T& item) { Insert(End(), item); }
void pop_front() { Erase(Begin()); }
void pop_back() { Erase(--End()); }
int Size()const { return size; }
bool Empty() { return head->next == tail; }
iterator Insert(const iterator itr, const T& item)
{
Node<T>* n = itr.current;
Node<T>* newnode = new Node<T>(item, n->pre, n);
n->pre->next = newnode;
n->pre = newnode;
size++;
return iterator(newnode);
}
iterator Erase(iterator itr)
{
Node<T>* temp = itr.current;
temp->pre->next = temp->next;
temp->next->pre = temp->pre;
size--;
Node<T>* p = temp->next;
delete temp;
return iterator(p);
}
};
我们先讲最难的,因为前面基本的像构造函数需要用到这几个难的函数,所以我们要先了解
1.插入。 itr.current是当前指向的节点的地址 ,我们想把某一个节点插入到itr.current 指向的节点的前面位置 ,item是要插入到这个某个节点data里的数据
iterator Insert(const iterator itr, const T& item)
{
Node<T>* n = itr.current;//n是节点
Node<T>* newnode = new Node<T>(item, n->pre, n);//插入newnode节点到n节点的前面
n->pre->next = newnode;//修改 n前面节点(没插入之前 )的后继
n->pre = newnode;//修改 n节点的前驱
size++;
return iterator(newnode);//返回类型要与函数类型一致
}
如图:
我们要把 newnode 插入n前面去
2.消除。我们要清除itr的current指向的节点,并重新连接前面,返回值是下一个节点就是(itr变了 向后移了一位)划重点:itr变了 向后移了一位,因为它原来指向的被我们删了
iterator Erase(iterator itr)
{
Node<T>* temp = itr.current;
temp->pre->next = temp->next;
temp->next->pre = temp->pre;
size--;
Node<T>* p = temp->next;
delete temp;
return iterator(p);
}
和插入的思路其实一样 。不懂评论告诉我,我帮你瞅瞅
3.比较简单的函数名字
Begin() ; // 第一个装有数据的节点的地址(head->next)
End(); //最后一个装有数据节点的下一个节点(tail)
Front();//链表中第一个数据
Back();//链表中最后的一个数据
push_front();//首插
push_back();//尾插
pop_front();//首删
pop_back();//尾删
4.前面的构造函数(都很简单 注释我写在前面好了)
~List()
{
Clear();
delete head;
delete tail;
};
void Clear()
{
iterator temp = Begin();
while (temp != End())
temp = Erase(temp);//这里不用temp++,这里temp自己会往后移动。这是我们老师写的,老师nb;
}
四 测试函数
#include <iostream>
#include"List.h" //用户头文件
using namespace std;
template<class Iterator> //在指定范围内输出元素
void display(Iterator first, Iterator last)
{
for (; first != last; ++first)
cout << *first << ' ';
cout << endl;
}
int main()
{
List<int> L;
for (int i = 0; i < 10; i++)
{
L.Insert(L.Begin(), i + 100);
}
display(L.Begin(), L.End());
L.Erase(L.Begin());
display(L.Begin(), L.End());
List<int> L2;
L2 = L;
display(L2.Begin(), L2.End());
for (int i = 0; i < 20; i++)
{
L2.Insert(L2.Begin(), i * 2);
}
display(L2.Begin(), L2.End());
L.Erase(L2.Begin());
display(L2.Begin(), L2.End());
const List<int> L3(L2);
display(L2.Begin(), L2.End());
return 0;
}
结果:
哪里不懂 或者错误 都可以评论 、私信提出来。
感谢林老师O(∩_∩)~