基于c++的单向链表实现

        最近在学c++,但是网上基于c++的数据结构和算法教程很少,毕竟c++有stl,想用什么数据结构都直接调用了,可是找工作的话,面试官不会让你调库啊,所以自己尝试着用c++来实现各种数据结构和算法。好在网上关于数据结构的资料不少,只是语言有区别而已,还是有很多可以借鉴的地方。

        关于单向链表,原理这部分我觉得没什么可说的了,就是每个存数据的结点分为两部分,一部分是数据,一部分是指针,通过指针指向下一个结点,由此所有数据就一个接一个的连起来了。并且还得有个头结点,这个结点不存数据,只存指向第0个数据结点的指针,帮助我们找到这串数据。

        下面开始敲代码:

        1.首先就是结点,我们需要先声明和定义结点。它包含两种类型的数据,那肯定就得用结构体,或者类了,我这里就用类了,反正c++里这两者也没啥大区别。并且,为了提高泛用性,我用了模板,就是加了一行“template<class T>” 罢了,用T去代替什么int,float之类的数据类型。

template<class T>
class Node {
public:
	T value;
	Node* next;

	Node(T v,Node* n) {
		value = v;
		next = n;
	}
};

        结点这个类里就有两个成员变量:T类型的value和Node类型的指针next。顾名思义,value就是存的数据,next就是指向下一个结点的指针。还有一个有参构造函数,这个不写,后面就会稍微麻烦一点。

        2.接下来就是单向链表这个类了,我在这个类里肯定就要我之前说的“头结点”,不然怎么在内存中找到这些数据呢,还要有一个int类型的变量指示当前链表中存数据的结点个数。并且,我还在里面写了一些有功能的函数。还有要注意的是,由于结点用了模板<T>,那这个类也要用模板。

template<class T> 
class Link_list {
public:
	Node<T>* head;  //头结点,指向第一个存储的数据
	int N;          //记录链表的长度

	//初始化头结点和链表长度
	Link_list() {
		head = new Node<T>(NULL, NULL);  //在堆区开辟一块空间,这里的<T>不能省略
		N = 0;
	}

	//清空链表
	//只要让头结点不指向第一个数据结点就行了,这样就不能访问后面的数据了。
	//很简单,但缺点就是可能造成内存泄漏
	void clear_list() {
		head = NULL;
		N = 0;
	}

	//获得链表的长度
	int get_length() {
		return N;
	}

	//判断链表是否为空
	bool is_empty() {
		return N == 0;
	}

	//获得第i处的值
	T get_value(int i) {
		//要从头结点开始
		Node<T>* n = head;
		//这肯定要加个越界判断嘛
		if (i > N) {
			cout << "索引过大!" << endl;
			return NULL;   //函数返回值类型是T,所以用NULL。
		}
		//用for循环让n变成第i个数据结点(当h=i-1时,也就是最后一次循环,n就变成第i个数据节点了)
		for (int h = 0; h < i; h++) {
			n = n->next;
		}
		return n->value;
	}

	//尾插
	void insert(T v) {
		Node<T>* n = head;
		//for循环让n变成最后一个结点
		for (int h = 0; h < N; h++) {
			n = n->next;
		}
		//生成一个存有数据v的新结点ni
		Node<T>* ni = new Node<T>(v, NULL);
		//让原本链表的最后一个结点指向ni
		n->next = ni;
		//别忘了链表长度加一
		N++;
	}

	//在i处插入
	void insert(T v, int i) {
		Node<T>* n = head;  
		//这里是让n成为第i-1个数据结点
		for (int h = 0; h < i; h++) {
			n = n->next;
		}
		Node<T>* ni = new Node<T>(v, NULL);
		//让新结点指向原本第i处的结点
		//让第i-1处结点指向新结点
		ni->next = n->next;
		n->next = ni;
		N++;
	}

	//移除第i处的结点
	void remove(int i) {
		Node<T>* n = head;
		//让n变成第i-1处的结点
		for (int h = 0; h < i; h++) {
			n = n->next;
		}
		//把第i处结点(i-1的next)存的指针,也就是指向第i+1处的指针先存起来
		Node<T>* ntemp = n->next->next;
		//删掉第i处的结点(所以前面要先存下指针,不然就删掉了)
		delete n->next;
		//让第i-1处的结点指向原本第i+1处的结点
		n->next = ntemp;
		//删掉了结点,长度要减一
		N--;
	}

	//遍历打印链表的函数
	void printT() {
		Node<T>* n = head;
		for (int i = 0; i < N; i++) {
			cout << n->next->value << endl;
			n = n->next;
		}
	}
};

        值得注意的是,尾插和指定i处插入这两个函数的for循环,分别在N-1处和i-1处最后一次循环,但N是从1开始数的(三个数据,那N就是3),而i是从0开始数的(三个数据,最后一个数据在位置2)。

        3.来测试一下

int main() {
	//用int类型,所以<int>
	Link_list<int> list1;
	cout << list1.is_empty() << endl;
	//尾插法插五个数进去
	list1.insert(0);
	list1.insert(1);
	list1.insert(2);
	list1.insert(3);
	list1.insert(4);
	cout <<"长度为:"<< list1.get_length() << endl;
	cout << list1.is_empty() << endl;
	cout << "-----------" << endl;
	list1.printT();
	cout << "-----------" << endl;
	cout << "第0个:" << list1.get_value(0) << endl;
	//在位置3处插一个值
	list1.insert(31, 3);
	cout << "长度为:" << list1.get_length() << endl;
	cout << "-----------" << endl;
	list1.printT();
	cout << "-----------" << endl;
	//移除位置0处的结点
	list1.remove(0);
	list1.printT();
	system("pause");
	return 0;
}

运行结果:

         应该没什么问题,最后,欢迎大家评论区批评指正!

完整代码:

#include<iostream>
using namespace std;

template<class T>
class Node {
public:
	T value;
	Node* next;

	Node(T v,Node* n) {
		value = v;
		next = n;
	}
};

template<class T> 
class Link_list {
public:
	Node<T>* head;  //头结点,指向第一个存储的数据
	int N;          //记录链表的长度

	//初始化头结点和链表长度
	Link_list() {
		head = new Node<T>(NULL, NULL);  //在堆区开辟一块空间,这里的<T>不能省略
		N = 0;
	}

	//清空链表
	//只要让头结点不指向第一个数据结点就行了,这样就不能访问后面的数据了。
	//很简单,但缺点就是可能造成内存泄漏
	void clear_list() {
		head = NULL;
		N = 0;
	}

	//获得链表的长度
	int get_length() {
		return N;
	}

	//判断链表是否为空
	bool is_empty() {
		return N == 0;
	}

	//获得第i处的值
	T get_value(int i) {
		//要从头结点开始
		Node<T>* n = head;
		//这肯定要加个越界判断嘛
		if (i > N) {
			cout << "索引过大!" << endl;
			return NULL;   //函数返回值类型是T,所以用NULL。
		}
		//用for循环让n变成第i个数据结点(当h=i-1时,也就是最后一次循环,n就变成第i个数据节点了)
		for (int h = 0; h < i; h++) {
			n = n->next;
		}
		return n->value;
	}

	//尾插
	void insert(T v) {
		Node<T>* n = head;
		//for循环让n变成最后一个结点
		for (int h = 0; h < N; h++) {
			n = n->next;
		}
		//生成一个存有数据v的新结点ni
		Node<T>* ni = new Node<T>(v, NULL);
		//让原本链表的最后一个结点指向ni
		n->next = ni;
		//别忘了链表长度加一
		N++;
	}

	//在i处插入
	void insert(T v, int i) {
		Node<T>* n = head;  
		//这里是让n成为第i-1个数据结点
		for (int h = 0; h < i; h++) {
			n = n->next;
		}
		Node<T>* ni = new Node<T>(v, NULL);
		//让新结点指向原本第i处的结点
		//让第i-1处结点指向新结点
		ni->next = n->next;
		n->next = ni;
		N++;
	}

	//移除第i处的结点
	void remove(int i) {
		Node<T>* n = head;
		//让n变成第i-1处的结点
		for (int h = 0; h < i; h++) {
			n = n->next;
		}
		//把第i处结点(i-1的next)存的指针,也就是指向第i+1处的指针先存起来
		Node<T>* ntemp = n->next->next;
		//删掉第i处的结点(所以前面要先存下指针,不然就删掉了)
		delete n->next;
		//让第i-1处的结点指向原本第i+1处的结点
		n->next = ntemp;
		//删掉了结点,长度要减一
		N--;
	}

	//遍历打印链表的函数
	void printT() {
		Node<T>* n = head;
		for (int i = 0; i < N; i++) {
			cout << n->next->value << endl;
			n = n->next;
		}
	}
};

int main() {
	//用int类型,所以<int>
	Link_list<int> list1;
	cout << list1.is_empty() << endl;
	//尾插法插五个数进去
	list1.insert(0);
	list1.insert(1);
	list1.insert(2);
	list1.insert(3);
	list1.insert(4);
	cout <<"长度为:"<< list1.get_length() << endl;
	cout << list1.is_empty() << endl;
	cout << "-----------" << endl;
	list1.printT();
	cout << "-----------" << endl;
	cout << "第0个:" << list1.get_value(0) << endl;
	//在位置3处插一个值
	list1.insert(31, 3);
	cout << "长度为:" << list1.get_length() << endl;
	cout << "-----------" << endl;
	list1.printT();
	cout << "-----------" << endl;
	//移除位置0处的结点
	list1.remove(0);
	list1.printT();
	system("pause");
	return 0;
}

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值