其他分享
首页 > 其他分享> > 数据结构基础:P4.4-树(二)--->线性结构之习题选讲:逆转链表

数据结构基础:P4.4-树(二)--->线性结构之习题选讲:逆转链表

作者:互联网

本系列文章为浙江大学陈越、何钦铭数据结构学习笔记,前面的系列文章链接如下
数据结构基础:P1-基本概念
数据结构基础:P2.1-线性结构—>线性表
数据结构基础:P2.2-线性结构—>堆栈
数据结构基础:P2.3-线性结构—>队列
数据结构基础:P2.4-应用实例—>多项式加法运算
数据结构基础:P2.5-应用实例—>多项式乘法与加法运算-C实现
数据结构基础:P3.1-树(一)—>树与树的表示
数据结构基础:P3.2-树(一)—>二叉树及存储结构
数据结构基础:P3.3-树(一)—>二叉树的遍历
数据结构基础:P3.4-树(一)—>小白专场:树的同构-C语言实现
数据结构基础:P4.1-树(二)—>二叉搜索树
数据结构基础:P4.2-树(二)—>二叉平衡树
数据结构基础:P4.3-树(二)—>小白专场:是否同一棵二叉搜索树-C实现

文章目录


一、逆转链表

1.1 什么是抽象的链表

题目:给了你一个单链表的头节点,再给你一个整数K。然后要求你把每K个节点逆转一下,然后最后返回给用户的是那个逆转以后链表的头结点的指针。

比较有意思的一个问题是:有同学在论坛上面发言时候说,链表这个东西是不是只对C和C++语言的程序才有意义,我用的是JAVA, JAVA里头没有指针,没有指针哪来的链表呢?所以在这里我们很有必要重申一下,到底什么是链表:

我们说链表是一个抽象的数据结构,链表中间的结点需要两个域来存,一块是有地方来存它的基础数据,另外一个很重要的特征是需要有一块地方来存指针,这个指针指向下一个结点。C和C++只是提供了一种特殊的机制可以实现这个指针的功能,一个抽象的指针指的就是地址。任何一种形式去存了下一个结点的位置,这个东西就叫做指针。


我们来看一下我们这道题目的样例输入。第一行是给了这个单链表头结点的位置、链表结点数量、需要反转的结点数量。接着下面几行分别给出了每个结点的位置、在链表中的位次、每个结点下一个结点的位置。
在这里插入图片描述
那我们在编程实现的时候到底怎么来存这样一个链表呢?一个非常直截了当的方法就是整个模拟一个真实的链表在内存里面存在的状态。也就是说,我开一个充分大的结构数组来代表内存空间,这种情况下,什么是一个结点所处的位置?结点的位置就是它在这个数组里面的下标,而元素的下标是可以用一个整数来表示的。在这里我们说到一个指针的时候,实际上我们说的是一个整数,这个整数记录的是下一个结点在这个数组里面的下标而已。因此,链表对应的结构如下:
在这里插入图片描述


1.2 链表逆转算法

接下来我们来看怎么样实现单链表的逆转。在这个单链表的前面我个人习惯加一个头结点,虽然说在一定程度上是浪费了空间,但是如果这个头结点的空间对你不是那么紧要的话,那么加一个头结点会使后面的很多操作变得比较简单。这是我们原始的链表的形态。
在这里插入图片描述
我们希望完成了逆序以后,整个链表应该是长得这个样子的:前面四个结点的方向是从4指到3指到2指到1的,然后这个头结点要指在4这个位置,而1后面接的是剩余部分的这个链表。
在这里插入图片描述


那么怎么去实现它呢?

①我们需要两个指针,第一个指针我把它命名叫做 new,指的是新的那个已经逆转好的链表它的头结点的位置。它的初始的位置显然可以指向第一个结点,我们认为第一个结点是已经逆转好了的。然后另外一个指针叫做old,他指向的是当前还没有完成逆转的那个老链表的头结点,我们让其初始位置指向 new的后面结点。
②我们来看看反转链表的具体操作:
那我们要做第一件事情就是要把这个2的指针逆过来指向1,但是我在做这个操作之前我得先把3的这个位置给记住。所以我需要另外一个tmp指针去指向3,把这个位置记住。否则的话,我把2的这个指针一转向之后,后面这段链表就被丢掉了。
在这里插入图片描述
记住了3的位置之后我就可以把2的指针反向, 让他指向1。
在这里插入图片描述
完成了这一步以后我们就可以把这三个指针向后位移。
在这里插入图片描述
然后把old的这个指针转过去指向这个新的头结点
在这里插入图片描述
然后继续向后移动,继续执行前面两步的操作。
在这里插入图片描述
这样一直重复,直到达到K个结点,我们这里K是4。
在这里插入图片描述
停下来以后我们发现:1的next指针现在还指向2,这是不对的,1应该指向5。我怎么知道5是什么呢,5就是当前还没有逆转的这个老的链表的头结点。然后我这个空的头结点他的方向应该指向当前这个已经逆转好的链表的头结点,也就是指向这个 new 所指的位置。
在这里插入图片描述

对应伪代码如下:

Ptr Reverse( Ptr head, int K )
{ 
	cnt = 1;
	new = head->next;
	old = new->next;
	while ( cnt < K ) {
		tmp = old->next;
		old->next = new;
		new = old; old = tmp;
		cnt++;
	}
	head->next->next = old;
	return new;
}

1.3 测试数据

我们在设计一道题目的测试数据的时候,至少有两个方面是一定要考虑到的。

最一般的情形
①也就是像我们的样例,给了你一个很平常的链表,然后有一部分是需要反转的,尾巴是不需要反转的。
边界测试
②在题目里面给定的是地址是一个5位的整数,那么我的地址就一定要取到上下界,就是5个0和5个9的情况。
③元素的个数正好是 k 的整数倍,所以你整个的链表每一段都正好需要全反转的。
④K 就等于 N,整个的大链表做一个完整的反转
⑤最小的 k 就是1,就意味着哪个结点都不需要反转。
⑥最大(最后剩K-1不反转)、最小N
⑦有多余结点。加了一些多余的结点让不合适的算法挂掉。

标签:结点,指向,选讲,链表,习题,数据结构,逆转,指针
来源: https://blog.csdn.net/InnerPeaceHQ/article/details/122296329