数据结构之链表

    链表是线性表的链式存储方式
    给每个元素加一个指针指向下一个元素的存储位置
    数据域存数据,指针域存指针

 

在C++中表示链表,需要有一个表示链表中单个结点的数据类型。从结点的属性来看,这样的一个数据类型不但需要存储的数据结构,还要有一个指向另一个相同类型结点的指针。

这里假设每个结点将存储一个类型为int的数据项,则可以声明以下类型来存放结点:

struct ListNode{
    int date;
    ListNode *next;
}
在以上代码中,ListNode是要存储在链表中的结点的类型,结构成员value是结点的数据部分,而另一个结构成员next则被声明为ListNode的指针,它是指向下一个结点的后续指针。

ListNode 结构有一个有趣的属性,它包含一个指向相同类型数据结构的指针,因此可以说是一个包含对自身引用的类型。像这样的类型称为自引用数据类型或自引用数据结构。

当我们声明了一个数据类型来表示结点之后,可以用这个数据类型来定义一个初始为空的链表,方法是定义一个用作链表头的指针并将其初始化为null:

ListNode *head = NULL;
现在可以创建一个链表,其中包含一个结点,将这个节点存储值为12.5:

head = new ListNode; //分配新结点
head->value = 12; //存储值
head->next = NULL; //表示链表的结尾
因为ListNode的数据类型包含了指向于先一个结点的指针,所以在没有下一个结点的情况之下,要使其等于null。

如果我们创建一个新结点,在其中存储一个值为13.5的值,将其作为链表中的第二个结点,代码如下所示:

ListNode *secondPtr = new ListNode;
secondPtr->value = 13;
secondPtr->next = nullptr; //第二个结点是链表的结尾
head->next = secondPtr; //第一个结点指向第二个
通过 head->next = secondPtr; 语句将链表头的后继指针改为指向第二个结点;指针 secondPtr->next 设置为 nullptr,可以使第二个结点成为链表的结尾。
 

 链表的函数封装

1.链表初始化:首先判断内存分配是否成功,再对头节点的数据域赋值,指针指向空

//初始化一个空链表

bool initLinkList(LinkList* &L) {
	L = new LinkList;

	if (!L) {
		return false;
	}

	L->date = 666;
	L->next = NULL;

	return true;
}

2.打印链表:此处打印链表:定义一个链表f为L头结点指向的地方,打印头结点(一般情况不进行头结点遍历),然后在判断f(L指向的地方)是否存在,之后一次类推进行信息打印

 bool LinkPrint(LinkList* &L) {
	 LinkList* f = NULL;
	 f = L->next;
	 cout << L->date << "\t";
	 while (f) {
		 cout << f->date << "\t";
		 f = f->next;
	 }
	 cout << endl;

	 return 0;
}

3.前插法 这里的前插法其实是在头结点和第1个节点之间插入,因为头结点之前没有空间分配

        此处先进行非法判断,再让头结点指向插入的元素,插入的元素指向原本头结点指向的地方

//前插法  插入的地方其实是头结点之后
bool LinkInsert_front(LinkList*& L, LinkNode* node) {
	if (!L || !node) {
		return false;
	}
	//L(头节点)指向node, node 指向L指向的地方  

	node->next = L->next;
	L->next = node;
	return true;
}

4.尾插法

尾插法接收在链表的尾部添加元素此处先进行非法判断,再定义一个last,使用循环到达链表的尾部,最后进行元素添加

//尾插法
bool LinkInsert_back(LinkList*& L, LinkNode* node) {
	if (!L || !node)
	{
		cout << "出错了" << endl;
		return false;
	}
	LinkNode* last;
	last = L; //注意这里应该是用指向L,
			  //而不是指向L->next,因为当一开始没有节点的时候
			  //如果用L->next,那么后面就超出范围了
	while (last->next) {
		last = last->next;
	}
	last->next = node;
	node->next = NULL;
	return true;
}

5.指定位置插入元素

此处是通过while进行判断找到需要插入的节点位置,再进行插入

//指定位置插入元素
bool LinkInsert(LinkList* &L,int i,int e) {
	if (!L) {
		cout << "出错了" << endl;
		return false;
	}
	LinkNode* f;
	LinkNode* s;
	int j = 0;
	f = L;
	while (f && (i - 1) > j) {
		f = f->next;
		j++;
	}
	if (!f || (i - 1 < j)) {
		cout << "插入失败" << endl;
		return false;
	}

	s = new LinkNode;
	s->date = e;
	s->next = f->next;
	f->next = s;

	return true;
}

6.获取指定位置的元素:找到位置的机制同上

bool Link_GetElem(LinkList*& L, int i, int& e) {
	//根据指定位置获取元素
	if (!L || !(L->next))
		return false;
	int index = 1;
	LinkList* p;
	p = L->next;
	while (p && index < i) {
		p = p->next;
		index++;
	}
	if (!p || index > i)
		return false;
	e = p->date;
	return true;
}

7.返回元素位置

bool Link_FindElem(LinkList*& L, int e, int& index) {
	//单链表根据值查询元素所在的位置
	if (!L || !(L->next))
		return false;
	int dex = 1;
	LinkList* p = L->next;
	while (p && (e != p->date)) {
		p = p->next;
		dex++;
	}
	if (!p)
		return false;
	index = dex;
	return true;
}

8.删除指定位置的元素

bool LinkDelete_index(LinkList*& L, int i) {
	if (!L || !(L->next))
		return false;
	LinkList* p = L;
	int index = 0;
	while (p && index < i - 1) {
		p = p->next;
		index++;
	}
	if (!p || index > i - 1) {
		return false;
	}
	LinkList* q = p->next;
	p->next = q->next;
	//delete q;释放空间
	return true;
}

9.销毁链表

bool LinkDestroy(LinkList*& L) {
	LinkList* p = L;
	cout << "列表嘎啦" << endl;
	while (p) {
		L = L->next;
		delete p;
		p = L;
	}
	return true;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值