单链表是一种很重要的数据结构,进行了理论的学习后,我们如何进行实操呢?话不多说,上操作!
目录
实例在结尾喔!
一:节点类Node的定义
单链表可以理解为是由一个个节点组成的,所以节点类的定义是很有必要的。
一个节点包括数据域c(类型可变)和指向下一个节点的next指针。
//节点类Node
class Node {
public:
char c;
Node* next;
Node(){};//默认构造函数
Node(char cc) :c(cc), next(NULL) {};//通过重载构造函数以便进行节点的初始化
};
这里可以通过Node()和Node(参数)进行节点的初始化。
二:链表类的定义
链表类的定义可以说是核心了,很多关于链表的相关定义和操作都在其中声明;
目前的单链表的成员函数包括了:
公有成员:createl(创建)、showl(输出)、insert(插入)、getlength(长度的获取)
私有成员:head(指向节点类Node的指针),length(链表长度)
//链表类l
class l {
public:
l();
//~l();
void createl();
void showl();
void insert(l&, char, int);
int getlength();
private:
Node* head;
int length;
};
- 注1:这里的析构函数~l()是对链表的释放操作的声明,由于会对后续链表的插入操作造成影响,这里把相关的定义和后续的声明注释掉。
- 注2:成员函数insert中的参数"l&"是链表类l的引用,而非链表类l,方便对链表的相关值(如length)进行修改。
相关成员函数的定义如下:
1.构造函数l()
构造函数使得链表类l对象在创建时就调用构造函数使用Node()声明并定义了一个头节点head,并将链表长度length置为0
//利用构造函数进行链表的初始化,其实就是头结点的创建(包括数据域c、next指针、链表长度length)
l::l() {
head = new Node();
head->next = NULL;//头结点的next指针先初始化为空
length = 0;//理所当然
}
对应的析构函数进行链表的释放(这里注释掉了喔!)
//利用析构函数释放链表
//l::~l() {
// Node* p, * pre;
// pre = head;
// p = pre->next;
// while (p != nullptr) {
// delete pre;
// pre = p;
// p = pre->next;
// }
// delete pre;
//}
三.链表的创建createl()
采用尾插法,通过I/O流创建链表
//尾插法进行单链表的创建
void l::createl() {
Node* tail, * tmp;
tail = head;
char cc;
while (cin >> cc, cc != '0') {
tmp = new Node();
tmp->c = cc;
tail->next = tmp;
tail = tmp;
length++;//统计链表长度
}
tail->next = nullptr;//将链表最后一个节点的next指针设为空
}
四.链表长度的获取getlength()
//输出链表长度
int l::getlength() {
return length;
}
五.链表的输出showl()
新建指向Node类的指针tmp,初始化tmp=head->next;使其指向头结点head的下一个节点(即链表的第一个有效节点),从这里开始遍历,结合条件 tmp != NULL 循环输出链表各个节点的数据域c
//输出链表
void l::showl() {
Node* tmp = head->next;
while (tmp != NULL) {
cout << tmp->c << "||";
tmp = tmp->next;
}
cout << endl;
}
六.对链表的操作
1.插入操作inster()
这里只有一个要说明的点,这里的insert的其中一个参数是对链表类对象的引用(l&el),参数取什么名字无所谓,可以是el也可以是其他的什么,最好不要将参数设为链表对象,因为这样不方便后续对链表某些值(如length)的操作。当然,将引用改为指针也是可以的,没有问题!
其他具体的分析都在代码中!
//在链表中插入元素,这里的(l&el)是要操作的链表的引用,c是要插入的数据,pos是要插入的位置
void l::insert(l& el, char c, int pos) {
Node* pi;
pi = new Node(c);
int count = 1;//计数
int len = el.getlength();//取出链表长度以便进行异常判断
Node* tmp = head;
//异常处理:对于所选的插入位置小于1和大于当前链表长度的情况作出异常处理
if (pos <= 0 || pos > len+1) {
cout << "\nsomething wrong with your op!" << endl; return;//这里直接retuen退出即可
}
//待插入元素在链表结尾的情况单独处理
if (pos == len+1) {
for (int i = 0; i < pos-1; i++) {
tmp = tmp->next;
}
tmp->next = pi;
pi->next = NULL;
}
//和尾部不同,头结点的存在使得插入在首部和中间的情况相同处理
else {
while (tmp->next != NULL) {
if (count == pos) {
pi->next = tmp->next;
tmp->next = pi;
break;
}
count++;
tmp = tmp->next;
}
}
el.length++;
//多嘴一句,这里体现出此函数的参数使用(l&el)即"链表的引用"的重要性,
//如果是参数是链表el而非链表的引用(l&el)的话,是改不了所选链表"el"的length值的
}
All of the code:
#include <iostream>
#include <string.h>
using namespace std;
//节点类Node
class Node {
public:
char c;
Node* next;
Node(){};//默认构造函数
Node(char cc) :c(cc), next(NULL) {};//通过重载构造函数以便进行节点的初始化
};
//链表类l
class l {
public:
l();
//~l();
void createl();
void showl();
void insert(l&, char, int);
int getlength();
private:
Node* head;
int length;
};
//利用构造函数进行链表的初始化,其实就是头结点的创建(包括数据域c、next指针、链表长度length)
l::l() {
head = new Node();
head->next = NULL;//头结点的next指针先初始化为空
length = 0;//理所当然
}
//利用析构函数释放链表
//l::~l() {
// Node* p, * pre;
// pre = head;
// p = pre->next;
// while (p != nullptr) {
// delete pre;
// pre = p;
// p = pre->next;
// }
// delete pre;
//}
//尾插法进行单链表的创建
void l::createl() {
Node* tail, * tmp;
tail = head;
char cc;
while (cin >> cc, cc != '0') {
tmp = new Node();
tmp->c = cc;
tail->next = tmp;
tail = tmp;
length++;//统计链表长度
}
tail->next = nullptr;//将链表最后一个节点的next指针设为空
}
//在链表中插入元素,这里的(l&el)是要操作的链表的引用,c是要插入的数据,pos是要插入的位置
void l::insert(l& el, char c, int pos) {
Node* pi;
pi = new Node(c);
int count = 1;//计数
int len = el.getlength();//取出链表长度以便进行异常判断
Node* tmp = head;
//异常处理:对于所选的插入位置小于1和大于当前链表长度的情况作出异常处理
if (pos <= 0 || pos > len+1) {
cout << "\nsomething wrong with your op!" << endl; return;//这里直接retuen退出即可
}
//待插入元素在链表结尾的情况单独处理
if (pos == len+1) {
for (int i = 0; i < pos-1; i++) {
tmp = tmp->next;
}
tmp->next = pi;
pi->next = NULL;
}
//和尾部不同,头结点的存在使得插入在首部和中间的情况相同处理
else {
while (tmp->next != NULL) {
if (count == pos) {
pi->next = tmp->next;
tmp->next = pi;
break;
}
count++;
tmp = tmp->next;
}
}
el.length++;
//多嘴一句,这里体现出此函数的参数使用(l&el)即"链表的引用"的重要性,
//如果是参数是链表el而非链表的引用(l&el)的话,是改不了所选链表"el"的length值的
}
//输出链表
void l::showl() {
Node* tmp = head->next;
while (tmp != NULL) {
cout << tmp->c << "||";
tmp = tmp->next;
}
cout << endl;
}
//输出链表长度
int l::getlength() {
return length;
}
int main() {
int pos;
char c;
l moyi;//生成一个moyi链表,她会自动调用构造函数进行头节点的初始化(包括数据域c、next指针、链表长度length)
moyi.createl();
moyi.showl();
cout << "\n" << "链表目前长度为: " << moyi.getlength() << endl;
getchar();
cout << "\n输入插入元素的值和位置: " << endl;
cin >> c;
getchar();
cin >> pos;
getchar();
cout << "将" << c << "插入在第" << pos << "个位置上" << endl;
moyi.insert(moyi, c, pos);
cout << endl;
moyi.showl();
cout << "\n" << "链表目前长度为: " << moyi.getlength() << endl;
cout << endl;
system("pause");
return 0;
}
实例:
主函数中,我先创建一个链表对象moyi,再调用createl通过输入流创建一个有效链表,然后调用showl输出链表,再通过输入流传入insert的参数对链表进行插入操作,最后再调用showl输出完成插入操作后的链表及状态。
好了好了,当然还是会继续更新的啦,欢迎关注喔!o.O