最简单的单项链表
这种单向链表中只有一个指向下一个节点的指针next,直接用链表节点类IntSLLNode来获取链表节点,手动将每一个节点的next指针连接到下一个节点
具体的操作步骤看注释就行
#pragma once
using namespace std;
class IntSLLNode
{
public:
IntSLLNode();
IntSLLNode(int i, IntSLLNode* in=nullptr)//重载的构造函数,构造本节点时,本节点的next指针为空
{
this->next = in;//本节点的next指针指向新建结点
this->info = i;//初始化数据块
}
~IntSLLNode();
int info;//链表结点的数据块
IntSLLNode* next;//指向下一个节点的指针
private:
};
IntSLLNode::IntSLLNode()//默认构造函数
{
next = nullptr;//默认构造出来的指针置空,安全性考虑
info = 0;//数据块默认初始化为0
}
IntSLLNode::~IntSLLNode()
{
}
在使用该链表时,需要先产生一个指向IntSLLNode的指针p
就像这样:
#include"只有next的单向列表.h"
#include <iostream>
using namespace std;
int main()
{
IntSLLNode* p;//链表的头
p = new IntSLLNode(10);//另表头指向第一个数据为10的节点,并且这个10节点的next置空
p->next = new IntSLLNode(8);//链接一个新节点8
p->next->next = new IntSLLNode(50);//链接一个新节点50
cout << "遍历链表" << endl;
int i = 1;
for (IntSLLNode* n =p; n != nullptr; n = n->next,i++)
{
cout << "第" << i << "个节点的数据为" << n->info << endl;
}
}
一般的单向链表
这种链表除了有Node节点类还有一个List用来管理维护整个链表的所有节点
下面程序中设计了IntSLLNode类和IntSLList类来构建整个链表,IntSLList类相当于是整个链表的管家,其public部分中有维护整个链表的操作集
class IntSLLNode //链表节点
{
public:
IntSLLNode();
IntSLLNode(int i, IntSLLNode* otherNode = nullptr)//重载构造函数,用于生成节点
{
this->info = i;
this->next = otherNode;
}
~IntSLLNode();
int info;//数据块
IntSLLNode* next;//指向下一个节点的指针
private:
};
IntSLLNode::IntSLLNode()//默认构造函数
{
info = 0;
next = nullptr;
}
IntSLLNode::~IntSLLNode()
{
}
我喜欢把IntSLList类称为这个链表的表头,在这个表头中还有两个IntSLLNode*指针,head和tail,用来指向整个链表的首尾结点,链表的大部分重要操作也是基于这两个指针完成的。
class IntSLList
{
public:
IntSLList();
~IntSLList();
//为该链表增加操作集
int isEmpty()
{
return head == 0;
}
void addToHead(int);
void addToTail(int);
int deleteFromHead();//删除并且返回info
int deleteFromTail();
void deleteNode(int);
bool isInList(int)const;
private:
IntSLLNode* head, * tail;//与P不同的是,list表头设置了首尾指针
};
//IntSLList成员函数的实现
IntSLList::IntSLList()//list表头的默认构造函数,一个空list是没有node的
{
head = tail = nullptr;
}
IntSLList::~IntSLList()//list的析构
{
for (IntSLLNode* p=head; !isEmpty(); p = head->next)//遍历链表进行delete node操作
{
delete head;
head = p->next;
}
}
void IntSLList::addToHead(int el)
{
head = new IntSLLNode(el,head);//申请的新节点的next指针被初始化成原来的head节点,同时将新节点的空间地址给list的head指针!!!
if (tail == 0)//如果加入节点以前head=tail=nullptr,也就是空表,则把空的tail指针指向现在唯一的节点
{
tail = head;//list中只有唯一节点时,head与tail指向的是这个唯一节点
}
}
void IntSLList::addToTail(int el)
{
if (tail != nullptr)
{
tail->next = new IntSLLNode(el);//将新节点连接在原来尾节点的后面
tail = tail->next;//将原来尾节点后移一位
}
else//此种情况对应空表
{
head = tail = new IntSLLNode(el);
}
}
int IntSLList::deleteFromHead()
{
//有两类情况要考虑
//list中有节点
int info;
IntSLLNode* node;
if (!isEmpty())
{
//再分两种情况
if (head == tail)//唯一节点的情况
{
info = head->info;
node = head;
head = tail = nullptr;//删除后首尾指针置空
delete node;
return info;
}
else//多节点
{
info = head->info;
node = head;
head = head->next;//head节点后移即可
delete node;
return info;
}
}
//list中没有节点
else
{
cout << "空表,操作错误!" << endl;
return 0;
}
}
int IntSLList::deleteFromTail()
{
int el = tail->info;
IntSLLNode* temp = nullptr;
if (head == tail)//唯一节点
{
delete head;
head = tail = nullptr;
}
else//不唯一节点
{
for (temp = head; temp->next != tail; temp = temp->next);
delete tail;
tail = temp;
tail->next = nullptr;//新尾部的next指针置空
}
return el;
}
void IntSLList::deleteNode(int el)
{
if (head != nullptr)//如果list不空
{
if (head == tail && el == head->info)//唯一节点,且正好删除值等于节点的数据
{
delete head;
head = tail = nullptr;
}
else if(el==head->info)//有多个节点,且头结点数据干好等于删除的值
{
IntSLLNode* temp = head;
head = head->next;
delete temp;
}
else//多个节点,遍历查找删除的值
{
IntSLLNode* temp,*prev;//由于是单项表,还需要设计一个prev指针来保存目标位置的前一个节点
for (temp =head->next,prev=head
; el != temp->info && temp != nullptr
; temp = temp->next,prev=prev->next);//注意temp遍历到结尾的情况,此时temp=nullptr,说明list中没有目标值
if (temp != nullptr)//目标值在表中
{
prev->next = temp->next;
if (temp == tail)//如果删除的尾节点,则tail指针需要前移
tail = prev;
delete temp;
}
}
}
else
{
cout << "操作错误,空list" << endl;
}
}
bool IntSLList::isInList(int el)const
{
IntSLLNode* tmp;
for (tmp = head; tmp != nullptr && tmp->info != el; tmp = tmp->next);
return tmp != nullptr;
}
在设计链表增删操作的方法时,要注意增删位置在链表中的位置,就只有三种情况,head位置,tail位置和中间位置,不同位置对指针的连接方式会有不同,一般都要用if进行判断,这些在代码中已经解释的很详细了。除此之外,在首尾进行操作时,也要对节点数量进行判断,也有三种情况,最简单的情形是空链表,其次是单节点,再就是一般的多节点情形,针对不同的情形用if进行判断,并做好指针维护即可,这些在代码中也有详细的注释。
下面是简单的使用演示:
#include <iostream>
#include"普通单向链表.h"
using namespace std;
int main()
{
IntSLList* list = new IntSLList;
cout << "创建链表,检查状态:isEmpty()=" << list->isEmpty() << endl;
cout << "开始添加节点,输入-999退出" << endl;
int num;
int i = 1;
while (cin>>num)
{
if (num == -999)break;
list->addToTail(num);
cout << "(成功添加第" << i << "个节点)" << endl;
i = i + 1;
}
cout << "添加完成" << endl;
cout << "开始倒序弹出" << endl;
while (!list->isEmpty())
{
cout << list->deleteFromTail();
}
}