首先创建一个带有尾指针的类,写两个构造方法,一个为私有供内部自己使用,另一个用来初始化,id作为模拟的数据,在对象初始化时,对象名将作为头指针,头指针不带有数据,同时将尾(末尾)指针指向自己
class stu{
public:
stu() {
this->id = 0;
this->end = this;
this->next = nullptr;
this->length = 0;
}
void addItem(int i,int pos);
void delItem(int pos);
void pushBack(int i);
int size() {
return this->length;
};
void show() {
//由于把this指针作为了头节点,头节点不存放数据
//同时得益于尾指针的存在,在头插和尾插可以得到更高的效率
if (this->length == 0) {
cout << "没有元素" << endl;
return;
}
stu* tmp = this->next;
while (tmp != nullptr) {
cout << tmp->id;
if (tmp->next == nullptr) {
cout << endl;
}
else {
cout << ",";
}
tmp = tmp->next;
}
}
private:
stu* end;
int length;
int id;
stu* next;
stu(int i) {
this->length = 1;
this->id = i;
this->next = nullptr;
this->end = this;
}
};
由于我们有一个尾指针,我们先写一个尾插法,调用私有的构造函数,创建一个新的结点由end->next(this->end->next)来接收,随后将end指针指向新插入的末尾结点,然后长度自增1.
void stu::pushBack(int i){
end->next = new stu(i);
end = end->next;
this->length++;
}
接着写上普通的插入功能,需要提供数据(i)以及位置(pos),首先判断位置是否合适,不合适直接retrun跳出.
发现是往最尾插入,直接调用尾插法.
发现是往头部插入,首先申请一个新的tmp结点调用私有构造方法填入数据,之后将tmp结点的next指针指向头结点的下一位(next),之后再将头结点的下一位指向tmp,随后长度自增1后跳出.
如果既非头插也非尾插,则申请一个current结点进行一次遍历,遍历时找到指定位置的上一位,如我要插入第三位,那我先将当前(current)的指针指向第二位,随后申请一个新的tmp结点填入数据,再将tmp的下一位(tmp->next)指向current的下一位(current->next,也就是原来的第三位,current目前是第二位),随后再将current的下一位指向tmp,此时的tmp 为插入后的第三位,随后长度自增1.
void stu::addItem(int i,int pos) {
if (pos < 1 || pos >this->length + 1) {
cout << "位置应在1-" << this->length + 1 << "之间" << endl;
return;
}
if (pos == this->length + 1) {
//直接调用尾插法
this->pushBack(i);
return;
}
if (pos == 1) {
//头插法
stu* tmp = new stu(i);
tmp->next = this->next;
this-> next = tmp;
this->length++;
return;
}
stu* current = this;
for (int j = 1; j < pos; j++) {
current = current->next;
}
stu* tmp = new stu(i);
tmp->next = current->next;
current->next = tmp;
this->length++;
}
接着是删除某一位置结点,先做简单的判断,不符合规则则直接跳出.
对于这种结构的链表,除了删除头指针以外,其他都需要进行一次遍历,因为end指针要指向最末尾,直接删除他,end依旧需要遍历找到新的最末尾.
删除最后一位时,遍历找到倒数第二个结点,随后将end指针指向倒数第二位,再删除原最后的一位.
删除第一个结点,由于不可先删除,先删除链表结构会断连,因此需要申请一个新的结点去指向第一个结点,随后头指针的下一位指向第二个结点,保持链表连续结构后,再将第一个结点删除.
删除中间的某一位结点时,则是申请一个新的结点(current)去遍历到指定位置的前一位,随后申请一个临时的结点,去指向删除的指定位置,先将指定位置前一位(current)的next指针指向指定位置的后一位(current->next = tmp->next),保持链表结构不断,再将指定位置结点删除.
void std::delItem(int pos) {
if (this->length == 0) {
return;
}
if (pos <1 || pos >this->length) {
cout << "位置应在1-" << this->length << "之间" << endl;
return;
}
if (pos == this->length) {
stu* tmp = this;
while (tmp->next->next != nullptr) {
tmp = tmp->next;
}
this->end = tmp;
delete tmp->next;
tmp->next = nullptr;
this->length--;
return;
}
if (pos == 1) {
stu* tmp = this->next;
this->next = tmp->next;
delete tmp;
tmp = nullptr;
this->length--;
return;
}
stu* current = this;
for (int j = 1; j < pos; j++) {
current = current->next;
}
stu* tmp = current->next;//这个位置是要删除的结点
current->next = tmp->next;
delete tmp;
tmp = nullptr;
this->length--;
}
值得注意的是,在删除链表时,切记不可先删除,应该申请一个新的结点先指向,最后把位置都连上后,再将指定位置删除,而在使用delete删除后,还需将申请的对象指向空指针,而如果想直接清空链表,则需要从位指针开始反向遍历,挨个删除.
最后是一个简单的测试
