单链表:
我们先定义一个用来实现链表的节点类node
为了能相对轻松的展示链表的实现,我们将节点类的结构定义的简单一点
class node { //定义一个链表的节点
public:
int data_;
node* next = nullptr; //定义一个指向下一个节点的指针next
node(int data):data_(data) {} //对节点进行初始化
};
ostream& operator<<(ostream& os, const node& n) { //重载左移运算符实现对node类的打印输出
os << n.data_ << endl;
return os;
}
接下来是链表的初始化以及一些基本功能的实现:
class linklist { //定义一个链表
private: //定义一个此链表的头指针head
node* head;
public:
linklist(): head(nullptr){}
void insert(int pos, int data) { //向指定位置插入数据
if (pos < 1) { //判断数据所要插入的位置是否有效
cout<< "Invalid position!" << endl;
return;
}
node* newnode = new node(data);
if (pos == 1) { //将数据插入列表头部时的操作
newnode->next = head;
head = newnode;
}
else {
node* temp = head; //定义一个指针temp,让它与head指向同位置
for (int i = 1; i < pos - 1&&temp != nullptr; ++i) {
temp = temp->next; //找到插入位置的前一个节点
}
if (temp == nullptr) {
delete newnode;
cout<<"Position out of range" << endl;
return;
}
newnode->next = temp->next;
temp ->next= newnode;
}
}
void add(int data) { //向链表尾部添加数据
node *newnode = new node(data);
if (head == nullptr) {
head = newnode;
}
else {
node* temp = head;
while (temp->next != nullptr) {
temp = temp->next;
}
temp->next = newnode;
}
}
void del(int pos) { //删除指定位置数据
if (pos < 1|| head == nullptr){ //判断删除位置是否有效且列表是否为空
cout << "Invalid position!" << endl;
return;
}
if (pos == 1) { //删除第一个数据
node* todelete = head;
head = head->next;
delete todelete; //释放内存
}
else {
node* temp = head;
for (int i = 1; i < pos - 1 && temp != nullptr; ++i) {
temp = temp->next; //找到所要删除数据位置的上一个节点
}
if (temp == nullptr) {
cout << "Position out of range" << endl;
return;
}
node* todelete = temp->next;
temp->next = temp->next->next; //删除指定位置的节点
delete todelete; //释放内存
}
}
void destroy() {
while (head != nullptr) {
node* todestroy = head;
head = head->next;
delete todestroy;
}
}
int length() {
node* temp = head;
int length = 0;
while (temp != nullptr) {
temp = temp->next;
length++;
}
return length;
}
node getvalue(int pos) {
node* temp = head;
for (int i = 1; i < pos; ++i) {
temp = temp->next;
}
return *temp;
}
};
为了方便我们在单链表的实现中将头结点定义为一个node类型的指针,名为head。其实还可以将头节点定义成一个data_为0(0代表空值)的node类,我们在之后双向链表以及循环链表的实现中采用此种方式。
我们一共实现了链表的在指定位置插入数据,在尾部添加数据,删除指定位置数据,销毁链表,求链表长度以及获取指定位置的值这几种基本方法。
向指定位置插入数据时,我们应当注意以下几点:
1.指定的插入位置值是否为一个有效数据?
2.指定的插入位置值是否超出链表当前的长度?(假如链表此时长度为2,但要向第八个位置插入数据,这显然是错误的,在实现的方法中应当正确处理这个问题)。
3.是否释放了已废弃节点的内存?
在删除指定位置的数据时,我们同样也要关注这些问题。
双向链表:
双向链表包含两个链接域和一个数据域,它比单链表多了一个指向前一个节点的prior指针。它的优点是:1.可以双向遍历整个链表。2.在某些情况下更容易进行删除操作。3.在拥有某个节点引用的情况下可以直接访问此节点的前驱节点或后继节点而不用从链表头部开始遍历,以下是双向链表的相关代码
#include<iostream>
using namespace std;
class dnode {
public:
int data_;
dnode* prior=nullptr;
dnode* next = nullptr;
dnode(int data):data_(data){}
};
ostream& operator<<(ostream& os, const dnode& d) {
os << d.data_ << endl;
return os;
}
class doubly_linklist {
private:
dnode* head;
public:
doubly_linklist() {
dnode* newnode = new dnode(0);
head = newnode;
}
void insert(int pos, int data) {
if (pos < 1) {
cout << "Invalid position!" << endl;
return;
}
dnode* newnode = new dnode(data);
if (pos == 1) {
newnode->next=head->next;
head->next = newnode;
newnode->prior = head;
}
else {
dnode* temp = head->next;
for (int i = 1; i < pos - 1&&temp!=nullptr; ++i) {
temp = temp->next;
}
if (temp == nullptr) {
delete newnode;
cout<< "Position out of range" << endl;
return;
}
if (temp->next == nullptr) {
temp->next = newnode;
newnode->prior = temp;
return;
}
newnode->next = temp->next;
temp->next = newnode;
newnode->prior = temp;
newnode->next->prior = newnode;
}
}
void del(int pos) {
if (pos < 1 || head ->next== nullptr) {
cout << "Invalid position!" << endl;
return;
}
if (pos == 1) {
dnode* todelete = head->next;
head->next = head->next->next;
delete todelete;
}
else {
dnode* temp = head->next;
for (int i = 1; i < pos - 1&&temp != nullptr; ++i) {
temp = temp->next;
}
if (temp == nullptr) {
cout<<"Position out of range" << endl;
return;
}
dnode* todelete = temp->next;
if (todelete->next == nullptr) {
temp->next = nullptr;
delete todelete;
return;
}
temp->next = temp->next->next;
temp->next->prior = temp;
delete todelete;
}
}
int length() {
int length = 0;
if (head== nullptr) {
return -1;
}
dnode* temp = head->next;
while (temp != nullptr) {
temp = temp->next;
length++;
}
return length;
}
dnode getvalue(int pos) {
dnode* temp = head->next;
if (pos < 1) {
cout<<"Invalid position!" << endl;
}
else {
for (int i = 1; i < pos; ++i) {
temp = temp->next;
}
}
return *temp;
}
void destroy() {
while (head->next != nullptr) {
dnode* todestroy = head->next;
head->next = todestroy->next;
if (head->next != nullptr) {
head->next->prior = head;
}
delete todestroy;
}
delete head;
head = nullptr;
}
void print1() {
if (head->next == nullptr) {
cout << "This doubly list is not exist!";
return;
}
dnode* temp = head->next;
while (temp!= nullptr) {
cout << *temp<<endl;
temp = temp->next;
}
}
void print2() {
if (head->next == nullptr) {
cout<< "This doubly list is not exist!";
return;
}
dnode* temp = head->next;
while (temp->next != nullptr) {
temp = temp->next;
}
while (temp!= head) {
cout << *temp << endl;
temp = temp->prior;
}
}
};
在实现双向链表中,我们要格外注意一个问题:潜在的空指针访问错误。