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;
}