面试中又被问到链表反转,OMG~ 码它,Mark它!(含递归实现、非递归实现、创建链表、打印链表、测试程序的完整代码——Java版)

碎碎念

链表的反转,可谓在面试中的高频题目了,经常会被问到。像微软,Facebook,amazon,Adobe等大公司的面试或笔试题,也都会出现这个题目。今天又被问到了,OMG~然而我不怕。看完今天这篇文章,相信你们也能在面试官面前帅气的写下这个算法了。

实现链表的反转,无非以下两种思路:

  • 思路一:遍历一遍链表,依次改变指针的指向。

    时间上,只将整个链表遍历了一遍,所以时间复杂度是O(n)级别的;
    空间上,会开辟三个指针的空间,所以空间复杂度是O(1)级别的。

  • 思路二:遍历原来的链表,将元素依次放入栈中,再一个个弹出。这时再遍历一遍链表,用弹出栈顶的元素直接更新给链表节点的值。

    效率低,且开辟了额外的空间。最重要一点,它改变了链表中节点的值,而通常情况下,改变链表值是不允许的。所以这种思路不提倡。

下面就来具体介绍一下如何帅气地实现链表的反转。

链表节点的数据结构

  • 一个数据域
  • 一个指向下个节点的next域(可以理解为C++中的next指针)
/**
 * 链表中的节点
 */
private static class ListNode {
    
    
    /**
     * 节点的值
     */
    private int val;

    /**
     * 节点的指针域,指向下一个节点
     */
    private ListNode next;

    /**
     * 构造函数
     *
     * @param value
     */
    public ListNode(int value) {
    
    
        val = value;
    }
}

链表反转实现

非递归

在这里插入图片描述
复杂度分析

  • 只将整个链表遍历了一遍,时间复杂度是O(n)级别的
  • 空间复杂度上,因为开了三个指针的空间,所以是O(1)
/**
 * 实现链表反转(非递归方式 - 修改指针)
 * 时间复杂度:O(n)
 * 空间复杂度:O(1)
 *
 * @param head
 * @return
 */
public static ListNode reverse(ListNode head) {
    
    

    ListNode cur = head;

    ListNode pre = null;

    while (cur != null) {
    
    
        // 保存next指针,指向当前节点的下一个节点
        ListNode next = cur.next;

        // 当前节点指针反转,指向它的前一个节点
        cur.next = pre;

        // pre指向cur
        pre = cur;

        // cur指向next
        cur = next;
    }

    return pre;
}

递归

递归,算的上是一种优雅的写法了~

/**
* 实现链表反转(递归方式)
 *
 * @param node
 * @return
 */
public static ListNode reverseRecursion(ListNode node) {
    
    
    if (node.next == null || node == null) {
    
    
        return node;
    }
    ListNode tempNode = reverse(node.next);
    node.next.next = node;
    node.next = null;
    return tempNode;
}

链表的创建

非递归

/**
 * 通过数组创建链表(非递归方式)
 *
 * @param arr
 * @return
 */
public static ListNode createLinkedList(int[] arr) {
    
    

    if (null == arr || arr.length == 0) {
    
    
        return null;
    }

    // 创建头节点
    ListNode head = new ListNode(arr[0]);

    // 当前节点curNode指向head
    ListNode curNode = head;

    for (int i = 1; i < arr.length; i++) {
    
    
        // 当前节点的next域指向新创建的节点
        curNode.next = new ListNode(arr[i]);

        // 当前节点向后移
        curNode = curNode.next;
    }

    // 返回链表的头节点
    return head;
}

递归

/**
 * 通过数组创建链表(递归方式)
 *
 * @param arr
 * @return
 */
public static ListNode createLinkedListRecursion(int[] arr) {
    
    
    if (null == arr || arr.length == 0) {
    
    
        return null;
    }

    ListNode head = new ListNode(arr[0]);
    ListNode tempNode = createLinkedList(ArrayUtil.sub(arr, 1, arr.length));
    head.next = tempNode;

    // 返回链表的头节点
    return head;
}

Tips:
数组的截取,这里用到了ArrayUtil工具类,它是hutool里的一个工具类。hutool,是一个小而全的Java类库,谐音“糊涂”,然而使用起来可一点不糊涂,可谓真正实现了让Java语言也可以小甜甜(好冷~不过真的是这样)。感兴趣的可以使用一下,如果是Maven程序的话,可以直接在pom文件里添加这个依赖。

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.3.1</version>
</dependency>

链表的打印

/**
 * 打印链表
 *
 * @param head
 */
public static void printLinkedList(ListNode head) {
    
    

    ListNode curNode = head;

    while (curNode != null) {
    
    
        // 打印当前节点
        System.out.print(curNode.val + " -> ");

        // 当前节点向后移
        curNode = curNode.next;
    }
    System.out.println("NULL");
}

完整代码

package basic.linkedlist;

import cn.hutool.core.util.ArrayUtil;

/**
 * @Description:链表反转
 * @Author: during
 * @Date: 2020/4/21
 */
public class ReverseLinkedList {
    
    

    /**
     * 链表中的节点
     */
    private static class ListNode {
    
    
        /**
         * 节点的值
         */
        private int val;

        /**
         * 节点的指针域,指向下一个节点
         */
        private ListNode next;

        /**
         * 构造函数
         *
         * @param value
         */
        public ListNode(int value) {
    
    
            val = value;
        }
    }

    /**
     * 通过数组创建链表(非递归方式)
     *
     * @param arr
     * @return
     */
    public static ListNode createLinkedList(int[] arr) {
    
    

        if (null == arr || arr.length == 0) {
    
    
            return null;
        }

        // 创建头节点
        ListNode head = new ListNode(arr[0]);

        // 当前节点curNode指向head
        ListNode curNode = head;

        for (int i = 1; i < arr.length; i++) {
    
    
            // 当前节点的next域指向新创建的节点
            curNode.next = new ListNode(arr[i]);

            // 当前节点向后移
            curNode = curNode.next;
        }

        // 返回链表的头节点
        return head;
    }

    /**
     * 通过数组创建链表(递归方式)
     *
     * @param arr
     * @return
     */
    public static ListNode createLinkedListRecursion(int[] arr) {
    
    
        if (null == arr || arr.length == 0) {
    
    
            return null;
        }

        ListNode head = new ListNode(arr[0]);
        ListNode tempNode = createLinkedList(ArrayUtil.sub(arr, 1, arr.length));
        head.next = tempNode;

        // 返回链表的头节点
        return head;
    }

    /**
     * 打印链表
     *
     * @param head
     */
    public static void printLinkedList(ListNode head) {
    
    

        ListNode curNode = head;

        while (curNode != null) {
    
    
            // 打印当前节点
            System.out.print(curNode.val + " -> ");

            // 当前节点向后移
            curNode = curNode.next;
        }
        System.out.println("NULL");

    }

    /**
     * 实现链表反转(非递归方式 - 修改指针)
     * 时间复杂度:O(n)
     * 空间复杂度:O(1)
     *
     * @param head
     * @return
     */
    public static ListNode reverse(ListNode head) {
    
    

        ListNode cur = head;

        ListNode pre = null;

        while (cur != null) {
    
    
            // 保存next指针,为当前节点的next域
            ListNode next = cur.next;

            // 当前节点指针反转,指向它的前一个节点
            cur.next = pre;

            // pre指向cur
            pre = cur;

            // cur指向next
            cur = next;
        }

        return pre;
    }

    /**
     * 实现链表反转(递归方式)
     *
     * @param node
     * @return
     */
    public static ListNode reverseRecursion(ListNode node) {
    
    
        if (node.next == null || node == null) {
    
    
            return node;
        }
        ListNode tempNode = reverse(node.next);
        node.next.next = node;
        node.next = null;
        return tempNode;
    }

    /**
     * 测试
     *
     * @param args
     */
    public static void main(String[] args) {
    
    
        int count = 100;
        int[] arr = new int[count];
        for(int i = 0; i< count; i++) {
    
    
            arr[i] = i;
        }

        ListNode head = createLinkedList(arr);
        /*ListNode head = createLinkedListRecursion(arr);*/
        printLinkedList(head);

        ListNode reverseHead = reverse(head);
        /*ListNode reverseHead = reverseRecursion(head);*/
        printLinkedList(reverseHead);
    }
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.during</groupId>
    <artifactId>algorithm</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.1</version>
        </dependency>
    </dependencies>

</project>

猜你喜欢

转载自blog.csdn.net/u011886447/article/details/105656525