读完这一篇之后再也不怕面试官考链表了

工欲善其事,必先利其器

上一篇介绍了leetcode上的接雨水问题的解决方案,这个算法的重点在于对问题的抽象能力,但是往往大多数算法,只具备对问题的抽象能力是不够的,还需要数据结构的基础。

接下来的几天,忘忧将总结一些数据结构的基础知识,包括链表,树,栈,图等,万丈高楼平地起,在开始学习算法之前,一定要把数据结构的基础掌握扎实。

今天忘忧将要介绍的是数据结构中最基础的一种——链表。

链表和数组的对比

  1. 数组在物理内存中是连续的单位,使用数组前需要预先申请出一块连续的空间;链表在物理内存中是非连续的,由前一个节点保存后一个节点的位置,在使用时可动态分配空间。
  2. 数组定位第n个位置的元素,可通过数组的下标直接定位到,时间复杂度为O(1);链表定位第n个节点,需要从头节点开始,依次向后查找,时间复杂度为O(n)。
  3. 数组在第n个位置上移除或者新增元素需要平移n位置后续位置的元素,链表在第n个位置上删除或者新增元素,只需修改n的前置节点即可。

链表的遍历

链表的遍历,只需找到头节点,然后依次向后遍历即可,直到最后一个节点(没有后置节点的节点)结束。

代码实现:

public static void traversalList(ListNode head){
    while(head != null){
        System.out.println(head.val);
        head = head.next;
    }
}

删除第n个节点

删除链表的第n(从0开始计数)个节点,需要先遍历,找到n的前置节点,即第n-1个节点,然后将n-1的next修改为n的后置节点即可;

代码实现:

/**
 * 删除第n个位置上的节点
 * @param head 头节点
 * @param n 位置
 */
public static ListNode deleteNode(ListNode head, int n){
    //链表为null,不操作
    if(head == null){
        return null;
    }
    //如果移除第0个位置的节点,即移除头节点,只需将头节点后移一位
    if(n == 0){
        return head.next;
    }
    //n节点的前置节点,即第n-1个节点
    ListNode preNode = head;
    for (int i = 0; i < n - 1; i++) {
        if(preNode == null){
            break;
        }
        preNode = preNode.next;
    }
    //如果删除的位置超过链表长度,不删除,直接返回原链表
    if(preNode == null || preNode.next == null){
        return head;
    }
    //将n的前置节点的next改为n的后置节点
    preNode.next = preNode.next.next;
    return head;
}

在第n个位置新增节点

在链表的第n个(从0开始计数)位置新增节点,第一步,找到第n-1个节点;第二步,将新增节点的next设置为n-1的next(即当前的n节点);第三步,将n-1的next设置为新增节点。

代码实现:

/**
 * 在第n个位置新增节点
 * @param head 头节点
 * @param newNode 要新增的节点
 * @param n 位置
 */
public static ListNode insertNode(ListNode head, ListNode newNode, int n){
    if(n == 0){
        newNode.next = head;
        return newNode;
    }
    //n节点的前置节点,即第n-1个节点
    ListNode preNode = head;
    for (int i = 0; i < n - 1; i++) {
        if(preNode == null){
            break;
        }
        preNode = preNode.next;
    }
    //n的前置为null,新增失败
    if(preNode == null){
        return head;
    }
    newNode.next = preNode.next;
    preNode.next = newNode;
    return head;
}

常见面试题

反转链表

手写反转链表是在初中级和应届生工程师面试当中,出场率非常高的一个题目,在此忘忧给大家整理一下思路和代码实现,希望大家在实际面试当中遇到之后能够得心应手。

代码实现:

/**
 * 反转链表
 * @param head 当前链表头节点
 * @return 反转后的链表
 */
public static ListNode reverseList(ListNode head) {
    if(head == null){
        return null;
    }
    ListNode result = null;
    do{
        ListNode tempNode = head;
        head = head.next;
        tempNode.next = result;
        result = tempNode;
    }while(head != null);
    return result;
}

判断链表有环

思路:声明两个指针,一个指针每次往前移动一步,另一个指针每次往前移动两部。如果有一瞬间,跑的快的指针与跑的慢的指针重合时,则判定为链表有环,如果指针最终指向了null,则判定为无环。类似体育测试的时候,如果无限的围着操场跑下去,跑的快的同学总是将跑得慢的同学“套圈”。

如上图,跑的快的指针终于在多跑一圈之后,在“5”号节点的位置与跑的满的指针在此重逢了!

/**
 * 判断链表有环
 * @return 是否有环
 */
private static boolean cycle(ListNode head){
    if(head == null){
        return false;
    }
    ListNode aNode = head;
    ListNode bNode = head;
    while(bNode.next != null && bNode.next.next != null){
        if(bNode.next.next == aNode.next){
            return true;
        }
        bNode = bNode.next.next;
        aNode = aNode.next;
    }
    return false;
}

其他景点面试题

  • 链表环的大小
  • 链表环的入口

关于链表的扩展

  • Java中ArrayList和LinkedList的区别(参考数组和链表的区别)

结束语:正如某人所言,凡事到最后必将皆大欢喜。

发布了11 篇原创文章 · 获赞 169 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/u013054715/article/details/104832666