C++单链表(未完...持续更新中)

单链表是一种很重要的数据结构,进行了理论的学习后,我们如何进行实操呢?话不多说,上操作!

目录

1.节点类Node的定义

2链表类 l 的定义

3.链表的创建(目前使用的是尾插法)

4.链表长度的获取

5.输出链表

6.对链表的操作

实例在结尾喔!


一:节点类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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值