用C++类实现单链表的基本功能(附带开辟内存空间详解)

实现的功能

  1. 无参构造和有参构造,其中无参构造只创建了一个头结点。有参构造中实现了在定义单链表的对象时,可以指定出单链表的长度,并且输入对应单链表每个结点的值。
  2. 遍历函数,通过这个遍历,可以打印出单链表对象中所有结点的值(不包括头结点)。
  3. 获取长度函数,通过这个函数,可以求出单链表的长度,这里的长度不包括头结点,假如链表中有一个结点(不包括头结点),那么他的长度就是1。
  4. 插入结点函数,这个函数有两个参数,分别为要插入结点的位置(比如这个为位置为2,那么要插入节点的位置就是在链表中第二个结点后面插入一个新的结点),另一个参数是要插入的值
  5. 删除结点的函数,这个函数有一个参数,就是要删除结点的的下标。
  6. 析构函数,通过这个函数,可以把单链表中的所有结点(包括头结点)占用的内存空间释放掉,防止内存泄漏。

内存问题

  1. 这个单链表的每个结点(包括头结点)都存放在堆区,下面说一下怎样实现的这个功能
  2. 这个单链表类有一个私有属性,这个属性就是指针,比如我们通过这个单链表类创建了一个对象 list 。那么这个对象就会有一个指针属性,注意,这个指针存放在栈区
  3. 假如上面创建 list 对象时并没有传入参数,他就会调用这个类的无参构造,
mylist()//默认构造函数
	{
		this->m_head = new node;
		this->m_head->date = 0;
		this->m_head->p_next = NULL;
	}

  上面是这个类的无参构造实现,第一行代码的意思时我在堆区找一块内存空间存放node类型的数据

class  node
{
public:
	int date;
	node* p_next;
};

上面是node类,其中包含一个数据和一个指针。那么new node  的意思就是在堆区找一块内存空间用来存放一个node类型的数据(这块内存空间分两部分,一部分放数据,一部分放指针)。一旦用这个链表类定义一个无参数的对象时,他就会调用这个无参构造,生成一个头结点(这个头结点存放在堆区),这对象的私有数据成员m_head指针(存放在栈区)指向这个头结点(也就是指向存放这个头结点的堆区地址)。

如果用链表类定义有参数的对象时,这三行代码也是必须要有的,目的就是创建一个头结点。然后就是按照结点的顺序一个个的输入对应结点的值。

下面再说说怎样实现怎加结点,假如我们现在有一个头结点,如果曾加结点的话,那肯定要再定义一个指针,同时再在堆区开辟一块内存空间,并且新定义的一个指针指向这个新开辟的堆区空间,假定这个新指针的名字为 pnew  ,再定义一个临时指针ptemp  这个指针的左右就是一直指向这个链表的最后一个结点(因为插入结点的话,肯定要找到已有链表的最后一个结点)。我们的pnew指针这时候指向的堆区空间,那么就可以通过这个pnew来操控新开辟堆区空间(包含两部分,一部分为数据,一部分为指针),首先这个让这个堆区的指针指向空(最后一个结点的指针就是指向为空),然后在输入这结点的值。这样新开辟的堆区空间就处理好了,已经可以作为最后一个结点了,下面只要把这个新处理好的结点和链表连起来就可以了,因为ptemp是指向链表的最后一个结点,那么可以让ptemp指向pnew,这样就连接好了,重复上面的的操作就可以继续增加结点了。

为了方便理解上面一段话,下面看一个图片:

上面的堆区空间都是随机的,不需要连续(刚好满足链表的条件)。

关于上面的内存问题,我感觉是创建单链表时特别需要考虑的问题,像怎样插入结点,删除结点,我认为只要内存空间搞清楚了,上面的问也就迎刃而解了

代码

#include<iostream>
#include<string>
using namespace std;

//用类实现单链表

class  node
{
public:
	int date;
	node* p_next;
};

class mylist
{
public:
	mylist()//默认构造函数
	{
		this->m_head = new node;
		this->m_head->date = 0;
		this->m_head->p_next = NULL;
	}
	~mylist(); //析构函数  需要把链表中所有结点占用的空间都释放掉

	mylist(int& n);//有参构造函数,传入的参数为新链表的结点为n个

	int getlistsize();//获取该链表的长度

	void traverselist();//遍历链表

	void insertnode(int  position, int  date);//在链表的指定位置插入结点

	void deletenode(int position);//要删除的结点位置

private:
	node* m_head;
};

//创建新链表函数的实现
mylist::mylist(int& n)//n代表创建链表是插入结点的个数
{
	//首先创建一个头结点,注意如果调用有参构造,就不会调用无参构造
	this->m_head = new node;
	this->m_head->date = 0;
	this->m_head->p_next = NULL;

	node* pnew;//用来存放新结点的值
	node* ptemp;//这个结点的作用见下面的注释
	ptemp = this->m_head;//头结点
	int i = n;
	while (i--)
	{
		pnew = new node;//开辟在堆区
		//默认的头结点为第0个结点(指针指向第一个结点,头结点无数据)
		cout << "请输入第" << n - i << "个结点的值" << endl;
		cin >> pnew->date;
		pnew->p_next = NULL;//最后一个指向为空
		//ptemp这个结点的作用是一直作为链表最后一个结点
		ptemp->p_next = pnew;
		ptemp = pnew;
	}
}

//获取链表的长度(头结点不算)
int mylist::getlistsize()
{
	int count = 0;
	node* p = this->m_head;
	while (p->p_next != NULL)
	{
		count++;
		p = p->p_next;
	}

	return count;
}

//遍历操作
void mylist::traverselist()
{
	node* p = this->m_head;
	int i = 0;
	while (p->p_next != NULL)
	{
		cout << "第" << ++i << "个结点的值为: " << p->p_next->date << endl;
		p = p->p_next;
	}
}

//在指定的位置后插入结点
void mylist::insertnode(int  position, int date)
{
	//position代表在第几个结点后面插入新结点
	int len = getlistsize();
	//判断一下插入的位置是否大于现有链表的长度
	if ((position > len) || position < 0)//这里position和len都可以为0
	{
		cout << "插入位置非法!" << endl;
		exit(0);
	}
	else
	{
		node* pnew = new node;//要插入的结点
		node* ptemp = NULL;//
		ptemp = this->m_head;
		pnew->date = date;
		for (int i = 0; i < position; i++)
		{
			ptemp = ptemp->p_next;
		}
		pnew->p_next = ptemp->p_next;
		ptemp->p_next = pnew;
	}
}

//删除指定的结点
void mylist::deletenode(int position)
{
	int len = getlistsize();
	if ((position > len) || position <= 0)//len都可以为0
	{
		cout << "删除位置非法!" << endl;
		exit(0);
	}
	else
	{
		node* p = this->m_head;
		for (int i = 0; i < position - 1; i++)
		{
			p = p->p_next;
		}
		node* p1 = p->p_next;//把要删除的结点存起来
		p->p_next = p->p_next->p_next;
		delete p1;
	}
}

//析构函数,释放链表占用的空间
mylist::~mylist()
{
	node* temp;
	while (m_head->p_next != NULL)
	{
		temp = m_head->p_next;
		delete m_head;
		m_head = temp;
	}
	delete m_head;//释放最后一个结点
}

//测试案例
void test01()
{
	int  ans;
	cout << "请输入要创建一个长度为多少的链表" << endl;
	cin >> ans;
	mylist l(ans);

	cout << "--------------" << endl;
	cout << "链表所有的值分别为:" << endl;
	if (ans == 0)
	{
		cout << "该链表为空!!!" << endl;
	}
	else
	{
		l.traverselist();
	}
	cout << "--------------" << endl;

	cout << l.getlistsize() << endl;

	int key, value;
	cout << "请输入你要在第几个结点后面插入新的结点的位置: " << endl;
	cin >> key;
	cout << "请输入要插入的值:" << endl;
	cin >> value;
	l.insertnode(key, value);

	cout << "--------------" << endl;
	cout << "插入结点后  链表所有的值分别为:" << endl;
	l.traverselist();
	cout << "--------------" << endl;

	cout << "请输入要删除的结点位置" << endl;
	cin >> key;
	l.deletenode(key);

	cout << "--------------" << endl;
	cout << "删除对应结点后   链表所有的值分别为:" << endl;
	l.traverselist();
	cout << "--------------" << endl;
}

int main()
{
	test01();
	system("pause");
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44820625/article/details/106565029