线性表的链式实现:链表

线性表的链式实现:链表

简介:

在线性表中插入,删除元素时需要移动很多元素,如果数据元素本身很大,就比较耗时。另外,如果线性表的元素个数非常多,可能无法申请到一个足够大的内存块,为了克服这些缺点,可以考虑用所谓的链表来表示线性表。链表在插入,删除上比顺序表更为省时,但是在查找上效率则不如顺序表,下面简单介绍一下链表以及链表的一些基本操作

所有代码:

#include<iostream>

//这里实现几个基本的线性表操作
//1.初始化操作
//2.检测线性表是否为空
//3.将线性表清空
//4.将线性表中第i个元素的位置返回
//5.在线性表查找元素
//6.插入
//7.删除
//8.返回线性表中的元素个数

using namespace std;

//头结点的作用:让第一个元素节点也有前驱,使得其和其他节点一致,简化了代码的操作


//然后定义表示整个链表的类List,将LNode作为其内部嵌套类

class List
{
	//定义一个数据元素的节点类型
	struct LNode//struct定义的类的成员默认是public的
	{
		char data;
		LNode* next = nullptr;//next是指向下一个元素结点的指针变量
	};
	LNode* head;//指向头结点的指针变量
	
public:
	List() :head(new LNode()){};//初始化一个不含任何数据只有头结点的空链表

	void clear();//清空线性表

	bool ListEmpty();//判断链表是否为空

	bool insert(const int i, const char& e);//插入元素操作(在i处插入)

	bool erase(const int i);//删除元素操作(删除i处元素)

	
	bool push_front(const char& e);//在头部插入元素
	bool pop_front();//在头部删除元素

	bool get(const int i, char& e)const;//读取i元素
	bool set(const int i, const char e);//修改i元素
	
	int size()const;//查询表长

	//定义一个locate函数定义序号是i的数据元素
	LNode* locate(const int i)const;
	
};

//单链表的清空算法思路:
//1.声明结点p和q
//2.将第一个结点赋值给p
//3.循环:(1)将下一结点赋值给q (2)释放p (3)将q赋值给p

void List::clear()
{
	LNode* p;
	LNode* q;
	p = head->next;//指向头结点的下一个结点,即第一个结点
	while (p != NULL)
	{
		q = p->next;
		delete p;
		p = q;
	}
	head->next = NULL;//最后将头结点的指针域置空
}

bool List::ListEmpty()
{
	if (head->next==NULL)//头结点指向为空
	{
		return true;
	}
	return false;
}

//这里我们定义一个locate成员函数用于定位序号是i的数据元素
List::LNode* List::locate(const int i)const
{
	if(i<0)//插入位置不合法
	{
		return nullptr;
	}
	LNode* p = head;
	int j = 0;//计数器j为0
	while (p&&j<i)//当p不指向链表尾部时,一直循环
	{
		p = p->next;
		j++;
	}
	return p;

	//如果返回的指针是空指针,说明没找到;如果非空,则p指向序号为i的结点
}


//插入元素操作(在i处插入)
bool List::insert(const int i, const char& e)
{
	LNode* p = locate(i - 1);
	if (p)
	{
		LNode* q = new LNode;
		if (!q)//q为空
		{
			return false;
		}
		q->data = e;
		q->next = p->next;
		p->next = q;

		return true;
	}
	return false;
	
}

bool List::erase(const int i)
{
	LNode* p = locate(i - 1);

	//删除操作,这里我们使用q保存要删除的结点地址,p是q的前驱
	if (p)
	{
		LNode* q = p->next;
		p->next = q->next;
		delete q;
		return true;
	}
	return false;
}


//在头部插入元素
bool List::push_front(const char &e)
{
	LNode* p = new LNode;//创建一个新的结点
	if (!p)//如果p是空指针,内存分配失败
	{
		return false;
	}

	p->data = e;//将新数据放入p的指针域中
	p->next = head->next;
	head->next = p;
}

//在头部删除元素
bool List::pop_front()
{
	//首先检测是否是空表
	if (!head->next)//空表的条件?
	{
		return false;
	}

	LNode* p = head->next;//p指向要删除的首结点
	head->next = p->next;//绕过首结点
	delete p;//删除原来的首结点

	return true;
}




//查询表长
//从头结点走到尾结点,看看遇到了多少数据结点
int List::size()const
{
	LNode* p(head);
	int i = 0;//i是一个计数器
	p = p->next;
	
	while (p)
	{
		i++;
		p = p->next;
	}
	return i;
	  
}

//读取i元素
bool List::get(const int i,char& e)const
{
	LNode* p = locate(i);
	if (p)
	{
		e = p->data;

		return true;
	}
	return false;
}



//修改i元素
bool List::set(const int i, const char e)
{
	LNode* p = locate(i);
	if (p)
	{
		p->data = e;
		return true;
	}
	return false;
}


//定义一个打印函数
void print(const List&l)
{
	char e;
	for (int i = 0; i <=l.size(); i++)
	{
		l.get(i, e);
		cout << e << '\t';
	}
}

int main()
{
	List l;

	//注意,头插法的元素顺序和插入顺序相反
	l.push_front('a');
	l.push_front('b');
	l.push_front('c');

	print(l);

	//注意,在vector容器中i表示插入元素的下标,而这里i则直接表示插入元素的位置
	l.insert(1, 'd');

	print(l);

	char e;
	l.get(1, e);
	cout << e << endl;

	l.erase(1);
	print(l);

	l.set(1, 'z');
	print(l);
}

//问题:尾插法以及尾指针的使用方式

代码详解:

1.链表的初始化:

源代码中使用了构造函数的初始化列表来初始化链表

List() :head(new LNode()){};//初始化一个不含任何数据只有头结点的空链表

当然也可以定义一个尾结点,这里先不做解释

2.清空单链表:

单链表的清空算法思路:
1.声明结点p和q
2.将第一个结点赋值给p
3.循环:(1)将下一结点赋值给q (2)释放p (3)将q赋值给p

void List::clear()
{
	LNode* p;
	LNode* q;
	p = head->next;//指向头结点的下一个结点,即第一个结点
	while (p != NULL)
	{
		q = p->next;
		delete p;
		p = q;
	}
	head->next = NULL;//最后将头结点的指针域置空
}

3.单链表的插入:

bool List::insert(const int i, const char& e)
{
	LNode* p = locate(i - 1);
	if (p)
	{
		LNode* q = new LNode;
		if (!q)//q为空
		{
			return false;
		}
		q->data = e;
		q->next = p->next;
		p->next = q;

		return true;
	}
	return false;
	
}

4.单链表的删除

bool List::erase(const int i)
{
	LNode* p = locate(i - 1);

	//删除操作,这里我们使用q保存要删除的结点地址,p是q的前驱
	if (p)
	{
		LNode* q = p->next;
		p->next = q->next;
		delete q;
		return true;
	}
	return false;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值