2. 线性表 - 单链表的反转,相交算法
2.1 单链表的反转
反转链表
,就是实现链表整体“反过来”,将头变成尾、尾变成头
2.1.1 迭代反转链表
迭代反转法
的实现思想是从当前链表的首元节点开始,一直遍历至链表的最后一个节点,这期间会逐个改变所遍历到的节点的指针域,另其指向前一个节点- 具体思路流程图和实现代码如下:
// 迭代反转法
link* Iteration_reverse(link* head) {
if (head == NULL || head->next == NULL) {
return head;
}
link* beg = NULL;
link* mid = head;
link* end = head->next;
while ( end ) { // end为NULL时结束
mid->next = beg;
beg = mid;
mid = end;
end = end->next;
}
mid->next = beg; // 结束时,需要将最后一个和前面连接上
return mid;
}
2.1.2 递归反转链表
递归反转法
的实现思想是从链表的尾节点开始,依次向前遍历,遍历过程依次改变各节点的指向,即另其指向前一个节点。- 具体思路流程图和实现代码如下:
// 递归反转法
link* Recursive_reverse(link* head) {
if (head == NULL || head->next == NULL) {
return head; // 递归的出口,也是原始链表是否需要反转的初始判断
}
link* new_head = Recursive_reverse(head->next);
// 递归到new_head指向最后一个结点, 此时head指向的倒数第二个
head->next->next = head; // 递归返回中,head逐渐向前移动
head->next = NULL;
return new_head;
}
2.1.3 头插法反转链表
头插法
是指在原有链表的基础上,依次将位于链表头部的节点摘下,然后采用从头部插入的方式生成一个新链表- 具体思路流程图和实现代码如下:
//头插法反转链表
link* head_reverse(link* head) {
if (head == NULL || head->next == NULL) {
return head;
}
link* new_head = NULL;
link* temp = NULL; // temp是一个不可缺少的接力棒
while (head) {
temp = head; // 摘除需要反转的链表的结点
head = head->next; // head向后移动
temp->next = new_head; // 将摘下的结点指向新的结点
new_head = temp; // new_head向后指向新的头结点
}
return new_head;
}
2.1.4 就地逆置法反转链表
就地逆置法和
头插法的实现思想类似,唯一的区别在于,头插法是通过建立一个新链表实现的,而就地逆置法则是直接对原链表做修改,从而实现将原链表反转- 具体思路流程图和实现代码如下:
// 就地逆置法
link* local_reverse(link* head) {
if (head == NULL || head->next == NULL) {
return head;
}
link* beg = head ;
link* end = head->next ;
while (end) {
beg->next = end->next; //将 end 从链表中摘除
end->next = head; //将 end 移动至链表头
head = end; // head 接替成为头
end = beg->next; //end 回到beg后面去
}
return head;
}
2.2 单链表的相交
- 单链表相交,则意味着它们有公共的节点,公共节点的数量可以是 1 个或者多个。根据单链表的特性:
每个节点有且仅有 1 个指针域
,因此相交示意图如下:
- 思路一 :分别遍历链表 1 和链表 2,对于链表 1 中的每个节点,依次和链表 2 中的各节点进行比对,查看它们的存储地址是否相同,如果相同,则表明它们相交;反之则表明它们不相交。此算法使用嵌套循环,时间复杂度为
O(n2)
。
//L1 和 L2 为 2 个单链表,函数返回 True 表示链表相交,返回 False 表示不相交
bool LinkIntersect(link * L1, link * L2) {
link * p1 = L1;
link * p2 = L2;
while (p1) //逐个遍历 L1 链表中的各个节点
{
while (p2) { //遍历 L2 链表,针对每个 p1,依次和 p2 所指节点做比较
if (p1 == p2) { //p1、p2 中记录的就是各个节点的存储地址,直接比较即可
return True;
}
p2 = p2->next;
}
p1 = p1->next;
}
return False;
}
- 思路2 :2 个单链表相交有一个必然结果,即
这 2 个链表相交则最后一个节点必定相同
,因此只需要比较两个结点最后一个结点即可。该算法的时间复杂度就缩小为O(n)
。
//L1 和 L2 为 2 个单链表,函数返回 True 表示链表相交,返回 False 表示不相交
bool LinkIntersect(link * L1, link * L2) {
link * p1 = L1;
link * p2 = L2;
while (p1->next) { //找到 L1 链表中的最后一个节点
p1 = p1->next;
}
while (p2->next) //找到 L2 链表中的最后一个节点
{
p2 = p2->next;
}
if (p1 == p2) { //判断 L1 和 L2 链表最后一个节点是否相同
return True;
}
return False;
}
- 思路3 : r两个链表相交,则部分节点的数量一定是相等的。按照短链表的长度,依次比较每个结点,如果相交则会找到相同的结点。
bool LinkIntersect(link * L1, link * L2) {
link * plong = L1;
link * pshort = L2;
link * temp = NULL;
int num1 = 0, num2 = 0, step = 0;
while (plong) { //得到 L1 的长度
num1++;
plong = plong->next;
}
while (pshort) //得到 L2 的长度
{
num2++;
pshort = pshort->next;
}
//重置plong和pshort,使plong代表较长的链表,pshort代表较短的链表
plong = L1;
pshort = L2;
step = num1 - num2;
if (num1 < num2) {
plong = L2;
pshort = L1;
step = num2 - num1;
}
temp = plong; //在plong链表中找到和pshort等长度的子链表
while (step) {
temp = temp->next;
step--;
}
while (temp && pshort) { //逐个比较temp和pshort链表中的节点是否相同
if (temp == pshort) {
return True;
}
temp = temp->next;
pshort = pshort->next;
}
return False;
}
感谢阅读 若有错误 敬请见谅!!!