今天群里大佬给我抛出了一个问题,单链表反转。
非常仁慈的给了我10分钟的时间,结果我连链表的数据结构都没写完,果然我还是太菜了。
虽说我也是看过数据结构的,但是一,时间有点久了;二,当时看的时候就没怎么写代码。
所以欠的债要还,硬着头皮也要写。
最开始我就冒出了一个想法,及其幼稚,思路就是从链表中取值,然后把所有值在外部排序完,再放回链表中,但是我一看这根本就是离题了啊!可是暂时没有什么别的想法,想着写写试试吧,结果发现这么写好像也挺麻烦的,就放弃了。
然后也想过递归方式实现,但是我还是太年轻了,对于递归理解的不是很到位,当时认为使用递归的话,不好找前一个节点,这样就没办法设置当前节点的下一个节点。
总之,我还是百度了,看了下思路。
两种方法:递归和遍历。
下面的方法参考了文章:点这里
递归
首先是递归
public static SingleLinkedListNode reversal(SingleLinkedListNode node){
if (node == null || node.next == null){
return node;
}else {
SingleLinkedListNode headNode = reversal(node.next);
node.next.next = node;
node.next = null;
return headNode;
}
}
public static SingleLinkedListNode reversal(SingleLinkedListNode node){
if (node == null || node.next == null){
return node;
}else {
SingleLinkedListNode headNode = reversal(node.next);
node.next.next = node;
node.next = null;
return headNode;
}
}
思路就是从最后一个节点开始,向前反转。
这里有一个比较关键的点,也是我之前以为递归行不通的症结就是:递归会入栈,而栈中保存了节点所有信息,并且不是设置当前节点的下一节点为前一节点,而是设置当前节点的下一节点的next为当前节点。
遍历
接着是遍历法
public static SingleLinkedListNode reversal2(SingleLinkedListNode node){
SingleLinkedListNode previousNode = null;
SingleLinkedListNode currentNode = node;
SingleLinkedListNode headNode = null;
while (currentNode != null){
SingleLinkedListNode nextNode = currentNode.next;
if (nextNode == null){
headNode = currentNode;
}
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
return headNode;
}
public static SingleLinkedListNode reversal2(SingleLinkedListNode node){
SingleLinkedListNode previousNode = null;
SingleLinkedListNode currentNode = node;
SingleLinkedListNode headNode = null;
while (currentNode != null){
SingleLinkedListNode nextNode = currentNode.next;
if (nextNode == null){
headNode = currentNode;
}
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
return headNode;
}
这里的思路是从前往后反转,每经过一个节点,就将当前节点的next进行反转,指向之前的节点。
遍历另一版
我这里自己思考实现了另一版,但是没有上面那个优雅,且考虑的不够全面
public static SingleLinkedListNode reversal3(SingleLinkedListNode node){
SingleLinkedListNode previousNode = null;
SingleLinkedListNode currentNode = node;
SingleLinkedListNode nextNode = null;
while (currentNode.next != null){
nextNode = currentNode.next;
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
currentNode.next = previousNode;
return currentNode;
}
public static SingleLinkedListNode reversal3(SingleLinkedListNode node){
SingleLinkedListNode previousNode = null;
SingleLinkedListNode currentNode = node;
SingleLinkedListNode nextNode = null;
while (currentNode.next != null){
nextNode = currentNode.next;
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
currentNode.next = previousNode;
return currentNode;
}
虽然运行起来的时候没什么问题,但是我在考虑的时候确实忽略了入参链表为空的情况的。
完整代码
完整代码如下
/**
* @Auther: yubt
* @Description: 单链表反转
* @Date: Created in 11:04 2018/9/27
* @Modified By:
*/
public class Reversal_linkedList {
private static class SingleLinkedListNode {
private int data;
private SingleLinkedListNode next;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public SingleLinkedListNode getNext() {
return next;
}
public void setNext(SingleLinkedListNode next) {
this.next = next;
}
@Override
public String toString() {
return "SingleLinkedListNode{" +
"data=" + data +
", next=" + next +
'}';
}
}
// 递归法
// 运行正常,debug会栈溢出,因为toString()方法
public static SingleLinkedListNode reversal(SingleLinkedListNode node){
if (node == null || node.next == null){
return node;
}else {
SingleLinkedListNode headNode = reversal(node.next);
node.next.next = node;
node.next = null;
return headNode;
}
}
// 遍历法
public static SingleLinkedListNode reversal2(SingleLinkedListNode node){
SingleLinkedListNode previousNode = null;
SingleLinkedListNode currentNode = node;
SingleLinkedListNode headNode = null;
while (currentNode != null){
SingleLinkedListNode nextNode = currentNode.next;
if (nextNode == null){
headNode = currentNode;
}
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
return headNode;
}
public static SingleLinkedListNode reversal3(SingleLinkedListNode node){
SingleLinkedListNode previousNode = null;
SingleLinkedListNode currentNode = node;
SingleLinkedListNode nextNode = null;
while (currentNode.next != null){
nextNode = currentNode.next;
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
currentNode.next = previousNode;
return currentNode;
}
public static void main(String[] args) {
SingleLinkedListNode node1 = new SingleLinkedListNode();
node1.setData(1);
SingleLinkedListNode node2 = new SingleLinkedListNode();
node2.setData(2);
SingleLinkedListNode node3 = new SingleLinkedListNode();
node3.setData(3);
SingleLinkedListNode node4 = new SingleLinkedListNode();
node4.setData(4);
SingleLinkedListNode node5 = new SingleLinkedListNode();
node5.setData(5);
node1.setNext(node2);
node2.setNext(node3);
node3.setNext(node4);
node4.setNext(node5);
// System.out.println(node1);
// SingleLinkedListNode reversalNode = reversal(node1);
// System.out.println(reversalNode);
// SingleLinkedListNode reversalNode2 = reversal2(node1);
// System.out.println(reversalNode2);
SingleLinkedListNode reversalNode3 = reversal3(node1);
System.out.println(reversalNode3);
}
}
/**
* @Auther: yubt
* @Description: 单链表反转
* @Date: Created in 11:04 2018/9/27
* @Modified By:
*/
public class Reversal_linkedList {
private static class SingleLinkedListNode {
private int data;
private SingleLinkedListNode next;
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public SingleLinkedListNode getNext() {
return next;
}
public void setNext(SingleLinkedListNode next) {
this.next = next;
}
@Override
public String toString() {
return "SingleLinkedListNode{" +
"data=" + data +
", next=" + next +
'}';
}
}
// 递归法
// 运行正常,debug会栈溢出,因为toString()方法
public static SingleLinkedListNode reversal(SingleLinkedListNode node){
if (node == null || node.next == null){
return node;
}else {
SingleLinkedListNode headNode = reversal(node.next);
node.next.next = node;
node.next = null;
return headNode;
}
}
// 遍历法
public static SingleLinkedListNode reversal2(SingleLinkedListNode node){
SingleLinkedListNode previousNode = null;
SingleLinkedListNode currentNode = node;
SingleLinkedListNode headNode = null;
while (currentNode != null){
SingleLinkedListNode nextNode = currentNode.next;
if (nextNode == null){
headNode = currentNode;
}
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
return headNode;
}
public static SingleLinkedListNode reversal3(SingleLinkedListNode node){
SingleLinkedListNode previousNode = null;
SingleLinkedListNode currentNode = node;
SingleLinkedListNode nextNode = null;
while (currentNode.next != null){
nextNode = currentNode.next;
currentNode.next = previousNode;
previousNode = currentNode;
currentNode = nextNode;
}
currentNode.next = previousNode;
return currentNode;
}
public static void main(String[] args) {
SingleLinkedListNode node1 = new SingleLinkedListNode();
node1.setData(1);
SingleLinkedListNode node2 = new SingleLinkedListNode();
node2.setData(2);
SingleLinkedListNode node3 = new SingleLinkedListNode();
node3.setData(3);
SingleLinkedListNode node4 = new SingleLinkedListNode();
node4.setData(4);
SingleLinkedListNode node5 = new SingleLinkedListNode();
node5.setData(5);
node1.setNext(node2);
node2.setNext(node3);
node3.setNext(node4);
node4.setNext(node5);
// System.out.println(node1);
// SingleLinkedListNode reversalNode = reversal(node1);
// System.out.println(reversalNode);
// SingleLinkedListNode reversalNode2 = reversal2(node1);
// System.out.println(reversalNode2);
SingleLinkedListNode reversalNode3 = reversal3(node1);
System.out.println(reversalNode3);
}
}
所以这些基础的东西还是要时不时就回顾一下,不会就赶紧补,有遗忘就复习,总之要夯实基础,虽然工作中不太可能用的上,但是只会业务逻辑,天天增删改查也太low了吧。。。
附加题
结果大佬又提了一个问题,存心搞我。
说存在 0 <= m < n <= l
,l
为链表长度,然后要反转m
和n
之间的部分。
吭呲瘪肚一下午,写了一坨勉强能用的吧
// m,n 从0起
public static SingleLinkedListNode specialReversal(SingleLinkedListNode node, int m, int n){
SingleLinkedListNode headNode = node;
SingleLinkedListNode beforeM = node;
SingleLinkedListNode afterM = null;
SingleLinkedListNode mBetweenN = null;
SingleLinkedListNode afterN = null;
if (m == 0){
beforeM = null;
afterM = node;
}else {
// 截断m处的节点
for (int i = 0; node != null; i++) {
// 操作next节点偏移量+1
if (i + 1 == m) {
afterM = node.next;
node.next = null;
}
node = node.next;
}
}
// 截断n处节点,并获得m和n之间的链表
mBetweenN = afterM;
for (int j = 0; j < n - m; j++){
afterM = afterM.next;
}
afterN = afterM.next;
afterM.next = null;
// 对m和n间的链表进行反转
SingleLinkedListNode reversalMN = reversal2(mBetweenN);
// 将n之后的链表连接回来
SingleLinkedListNode tmp = reversalMN;
while (reversalMN.next != null){
reversalMN = reversalMN.next;
}
reversalMN.next = afterN;
if (m != 0) {
// 将m之前的链表连接回来
while (beforeM.next != null) {
beforeM = beforeM.next;
}
beforeM.next = tmp;
}else {
// 由于m为0,无链表,所以直接替换头部节点
headNode = tmp;
}
// 返回头部节点
return headNode;
}
// m,n 从0起
public static SingleLinkedListNode specialReversal(SingleLinkedListNode node, int m, int n){
SingleLinkedListNode headNode = node;
SingleLinkedListNode beforeM = node;
SingleLinkedListNode afterM = null;
SingleLinkedListNode mBetweenN = null;
SingleLinkedListNode afterN = null;
if (m == 0){
beforeM = null;
afterM = node;
}else {
// 截断m处的节点
for (int i = 0; node != null; i++) {
// 操作next节点偏移量+1
if (i + 1 == m) {
afterM = node.next;
node.next = null;
}
node = node.next;
}
}
// 截断n处节点,并获得m和n之间的链表
mBetweenN = afterM;
for (int j = 0; j < n - m; j++){
afterM = afterM.next;
}
afterN = afterM.next;
afterM.next = null;
// 对m和n间的链表进行反转
SingleLinkedListNode reversalMN = reversal2(mBetweenN);
// 将n之后的链表连接回来
SingleLinkedListNode tmp = reversalMN;
while (reversalMN.next != null){
reversalMN = reversalMN.next;
}
reversalMN.next = afterN;
if (m != 0) {
// 将m之前的链表连接回来
while (beforeM.next != null) {
beforeM = beforeM.next;
}
beforeM.next = tmp;
}else {
// 由于m为0,无链表,所以直接替换头部节点
headNode = tmp;
}
// 返回头部节点
return headNode;
}
我的思路是把mn之间的链表截断出来,然后反转之后再接回去。
大佬的思路呢,是直接对mn之间的节点进行交换,比我这个简单
把他的代码贴出来把
val dummy = new ListNode(0)
dummy.next = head
var pre = dummy
for (_ <- 0 until m - 1)
pre = pre.next
val start = pre.next
var tail = start.next
for (_ <- 0 until n - m) {
start.next = tail.next
tail.next = pre.next
pre.next = tail
tail = start.next
}
dummy.next
先这样吧。