链表
定义: 链表在物理内存上是一种非连续的存储结构,数据元素的逻辑顺序是由链表的指针链接来实现的。
链表的优点 1.插入和删除的效率高,只需要改变指针的指向就可以进行插入和删除。2.内存利用率高,不会浪费内存,可以使用内存中细小的不连续的空间,只有在需要的时候才去创建空间。大小不固定,拓展很灵活。
链表的缺点 :查找的效率低,因为链表是从第一个节点向后遍历查找。
链表的基本操作:
- 初始化
- 创建
- 是否为空
- 链表的个数
- 清空链表
- 返回给定位置的相应元素
- 查找
- 删除
- 插入
链表的创建及初始化
创建链表之前,我们首先得知道链表结点的构成,它由一个指针域跟一个数据域构成,指针域的作用就是指明下一个地址的信息;
template<class T>
class linkNode{
public:
T data_;
linkNode *next_;
public:
linkNode() : next_(nullptr) {}
linkNode( T d ) : data_(d), next_(nullptr) {}
linkNode( T d, linkNode *ne ) : data_(d), next_(ne) {}
~linkNode(){ next_ = nullptr; }
};
我们现在有了结点类之后,就继续写链表类,当我们声明一个链表的时候,我们就初始化一个头节点,这个头节点可以作为链表的第一个元素,也可以只做头结点。
template<class T>
class List{
private:
linkNode<T> *head_;
int length_;
public:
List() : length_(0){
head_ = new linkNode<T>();
}
~List();
public:
};
template<class T>
List<T>::~List(){
linkNode<T> *t;
while( head_ != nullptr ){
t = head_;
head_ = head_->next_;
delete t;
}
}
链表的个数
我们在链表的成员变量里设置了 length,我们直接返回这个值就行
template <class T>
int List<T>::length(){
return length_;
}
判断是否为空
只需要判断 length 是否为 0
template <class T>
bool List<T>::empty(){
return length_();
}
清空链表
我们调用析构函数就能做到,不过我们需要再把头节点给申请出来
template <class T>
void List<T>::clear(){
this->~List();
head_ = new linkNode<T>();
length_ = 0;
}
返回给定位置的相应元素
判断位置的范围是否在链表的长度里面,然后我们再遍历链表拿到相应位置的元素
template <class T>
T List<T>::get( int idx ){
if( idx > length_ || idx <= 0){
return nullptr;
}
int cnt = 0;
linkNode<T> *p = head_;
while( p != nullptr ){
cnt ++;
if( cnt == idx ){
return p->data_;
}
p = p->next_;
}
return nullptr;
}
查找
循环遍历整个链表,有就返回相应位置;没有返回 0 or -1
template <class T>
int List<T>::find( T x ){
int cnt = 0;
linkNode<T> *p = head_;
while( p != nullptr ){
cnt ++;
if( p->data_ == x ){
return cnt;
}
p = p->next_;
}
return -1;
}
删除
判断删除的范围是否在合理范围内,然后判断是否为头结点,最后修改被删除结点的指针指向;
template <class T>
bool List<T>::erase( int idx ){
if( idx <= 0 || idx > length_ ){
idx = length_;
}
int cnt = 0;
linkNode<T> *p = head_, *t = nullptr;
while( p != nullptr ){
if( ++ cnt == idx ){
break;
}
t = p;
p = p->next_;
}
if( t != nullptr ){
t->next_ = p->next_;
delete p;
}else{
t = p->next_;
head_ = t;
delete p;
}
length_ --;
return true;
}
插入
插入的时候看是否在头结点,我们要更新头结点,其他情况就是先把下一个结点的指针赋值给插入的结点,再把下一个指向插入的就行;
template <class T>
bool List<T>::insert( T x, int idx ){
if( idx <= 0 || idx > length_ ){
idx = length_;
}
int cnt = 0;
linkNode<T> *p = head_, *t;
if( length_ == 0 ){
head_->data_ = x;
length_ ++;
return true;
}
while( p != nullptr ){
if( ++ cnt == idx )
break;
p = p->next_;
}
t = new linkNode<T>( x, p->next_ );
p->next_ = t;
length_ ++;
return true;
}