数据结构与算法学习笔记(五)

1.双向链表

双向链表是一种数据结构,它允许在每个节点中存储指向前驱和后继元素的指针。它与单向链表的区别在于,在双向链表中,每个节点都有两个指向其它节点的指针,而单向链表只有一个。

双向链表有两个特殊的指针:头指针和尾指针。头指针指向第一个节点,而尾指针指向最后一个节点。由于双向链表可以从前往后或从后往前遍历,因此它比单向链表更加灵活。

双向链表,可以说是单链表上的部分扩展,与单向链表相比,双向链表的插入和删除操作更加高效,因为它可以直接访问前驱节点。但是,由于每个节点需要存储两个指针,因此双向链表需要更多的内存空间。

原理和单链表差不多,多了个指向前面的指针。

双链表图解

 和之前的单链表一样,需要两个类,一个类管理链表,一个类提供节点。

节点类:

class Node
{
public:
	string item;
	Node* Next;
	Node* Pre;
	
	Node(string str, Node* next, Node* pre)
	{
		item = str;
		Next = next;
		Pre = pre;
	}
};

管理类(基础部分):

class TwoWayLinkList
{
public:
	Node* Head;
	Node* Last;
	int N;

public:

	TwoWayLinkList()
	{
		Head = new Node("", NULL, NULL);
		Last = NULL;
		N = 0;
	}

头节点也是一个节点,但其中的item和Pre数据没有意义,只是做引导作用。

增添函数:

void insert(string str)
	{
		if (N == 0)
		{
			Node* new_node = new Node(str, NULL, Head);

			Head->Next = new_node;

			Last = new_node;

			N++;

			return;
		}

		Node* new_node = new Node(str, NULL, Last);

		Last->Next=new_node;

		Last = new_node;

		N++;
	}

要判断首次添加的原因是:

Last因为还没有数据,没有与Head建立关系(其实构造函数里也可以直接让Head->Next=Last),先让Head->Next等于new_node,再赋值给Last。

插入函数:

void insert(int i, string str)
	{
		if (i > N)
		{
			return;
		}
		Node* pre = Head;
		
		for (int index = 0; index < i; index++)
		{
			pre = pre->Next;
		}

		Node* curr = pre->Next;

		Node* new_node = new Node(str, curr, pre);

		pre->Next = new_node;

		curr->Pre = new_node;

		N++;
	}

老样子,找到要插入节点的前一个节点,让前节点与新节点建立Next和Pre联系,再让新节点与原节点建立Next和Pre联系。

删除函数:

	string remove(int i)
	{
		if (i > N)
		{
			return "";
		}
		Node* pre = Head;

		for (int index = 0; index < i; index++)
		{
			pre = pre->Next;
		}

		Node *curr = pre->Next;		//借过节点,调出下一节点

		Node* next = curr->Next;	//pre->Next->Next

		pre->Next = next;

		next->Pre = pre;

		N--;

		return curr->item;
	}

通俗点讲就是跳过原节点,让前节点与后节点建立Next,Pre联系,让其遗忘而删除(doge

在实际应用中,双向链表经常用于需要反向遍历数据的情况,如浏览器中的“前进”和“后退”按钮、编辑器中的“撤销”和“重做”操作等。

2.反转链表(单链表)

在单链表的基础上进行反转操作,Next和Pre转向,头节点指向尾节点,头节点的原后一位节点指向NULL。

head->n1->n2->n3->n4->n5->NULL

变成

head->n5->n4->n3->n2->n1->NULL

采用递归的思想,从最后前退更容易理解点,最后每次的reverse函数都在判断传入的下一个节点(Next)是否为空节点,以抓取尾节点,然后用头节点的Next指向它,此时尾节点的上一个reverse函数终于可以继续执行剩下的语句,由于reverse函数结束会返回当前节点,所以此时的pre被赋值为尾节点,再通过pre对尾节点的Next赋值,正巧,函数中的参数就为倒数第二节点,再让倒数第二节点的下一个节点为空,以备利用。(提醒,最后一次的reverse函数执行,没有变量接取)最好手动画图,理解更快。

revserse函数

void revserse()
	{
		if (isEmpty())
		{
			return;
		}
		reverse(Head->Next);
	}

	Node* reverse(Node *curr)		//反转链表
	{
		if (curr->Next == NULL)
		{
			Head->Next = curr;

			return curr;
		}

		Node* pre = reverse(curr->Next);

		pre->Next = curr;

		curr->Next = NULL;

		return curr;
	}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值