链表:面试链表题目这么重要你确定不看看嘛?


不要纠结,干就完事了,熟练度很重要!!!多练习,多总结!!!

LeetCode21:合并两个有序链表

将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

解题思路

关于链表题目,我们要知道一个小技巧,那就是关于哨兵节点的设置!因为很多时候我们需要返回的是链表,这时候我们指定一个哨兵节点指向操作后链表的头节点!这时候其他节点就可以后移进行其他操作,不用直接返回哨兵的next节点即可得到链表!
回到本题,我们首先new一个哨兵节点,还有一个cur指针从哨兵节点出发,来记录排序的各个节点,其次本题需要借助双指针,分别指向两个链表的节点,每次我们对比两个链表相应指针指向元素的大小,将较小的节点加入cur,依次来进行排序

代码实现

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1==null){
            return l2;
        }
        if (l2==null){
            return l1;
        }
        ListNode pHead=new ListNode(0);
        ListNode cur=pHead;
        while (l1!=null&&l2!=null){
            if (l1.val<l2.val){
                cur.next=l1;
                l1=l1.next;
            }else {
                cur.next=l2;
                l2=l2.next;
            }
            cur=cur.next;
        }
        if (l1==null){
            cur.next=l2;
        }
        if (l2==null){
            cur.next=l1;
        }
        return pHead.next;
    }
}

LeetCode19:删除链表的倒数第N个节点

给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 n 保证是有效的。
进阶:
你能尝试使用一趟扫描实现吗?

解题思路

本题也是链表比较经典的双指针问题了!有一个查找倒数第n个节点,这题是删除倒数第n个节点,那么我们除了查找到倒数第n个,还要找到其前面的一个节点,然后直接p.next=p.next.next就把倒数第n个节点删除了!
那么很简单,除了哨兵节点外,我们在定义两个指针pre和cur,为了最后一个指向倒数第n个的前一个,一个指向倒数第n个。初始化时pre指向哨兵节点,cur指向链表头节点,pre和cur整好相差一个,这样就可以查找到前一个节点了!然后链表的头节点head先向后移动n次,然后pre和cur与head同时移动,直到head指向null为止!这时cur就指向倒数第n个节点,而pre就是前一个节点了!完成!

代码实现

class Solution {
    public ListNode removeNthFromEnd(ListNode head, int n) {
        if (n<=0){
            return null;
        }
        ListNode pHead=new ListNode(0);
        pHead.next=head;
        ListNode pre=pHead;
        ListNode cur=head;
        int k=1;
        while (k<=n){
            head=head.next;
            k++;
        }
        while (cur!=null&&head!=null){
            cur=cur.next;
            head=head.next;
            pre=pre.next;
        }
        pre.next=pre.next.next;
        return pHead.next;
    }
}

LeetCode141:环形链表

给定一个链表,判断链表中是否有环。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。

解题思路

链表成环检测,经典的快慢指针!除了哨兵节点的设置外,定义slow和fast两个指针,每次slow移动一步,fast移动两步,如果有环时,两指针会相遇,即可返回true,相反,如果没有环,那么fast指针将率先遍历到链表末尾即null,那么返回false

代码实现

public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head==null){
            return false;
        }
        ListNode slow=head;
        ListNode fast=head;
        while (fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            if (slow==fast){
                return true;
            }
        }
        if (slow!=fast){
            return false;
        }
        return false;
    }
}

LeetCode2:两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

解题思路

虽然是第2题,但是大厂也是考过的哦
不多说,开讲,设定一个变量用来保存每次相加的结果,同时还承担一个进位的功能。遍历两个链表,只要有一个链表不为空或者进位不为0,那么就继续执行。每次我们添加的节点val值,等于链表节点值可能两个链表节点相加,也可能只是某个链表的节点,因为没说两个链表一定是等长的!对10进行取余运算,即为本位相加的结果,同时我们要记录进位情况,进位就等于当前本位链表加法对10进行整除结果

代码实现

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if (l1==null){
            return l2;
        }
        if (l2==null){
            return l1;
        }
        ListNode phead=new ListNode(0);
        ListNode cur=phead;
        int tmp=0;
        while (l1!=null||l2!=null||tmp!=0){
            if (l1!=null){
                tmp+=l1.val;
                l1=l1.next;
            }
            if (l2!=null){
                tmp+=l2.val;
                l2=l2.next;
            }
            cur.next=new ListNode(tmp%10);
            tmp=tmp/10;
            cur=cur.next;
        }
        return phead.next;
    }
}

LeetCode146:LRU缓存机制

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作:获取数据 get 和 写入数据 put 。
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。

写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 该操作会使得密钥 2 作废
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 该操作会使得密钥 1 作废
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4

解题思路

LRU是一个非常重要的算法,底层类似LinkedHashMap即HashMap+双向链表结构!在Redis和操作系统中,设计缓存淘汰算法都能看到LRU的影子!!!很重要!!!

代码实现

public class LRUCache {
   class LinkedNode {
     int key;
     int value;
     LinkedNode prev;
     LinkedNode next;
   }
 
  private void addNode(LinkedNode node) {
    node.prev = head;
    node.next = head.next;
    head.next.prev = node;
    head.next = node;
  }

  private void removeNode(LinkedNode node){
    LinkedNode prev = node.prev;
    LinkedNode next = node.next;
    prev.next = next;
    next.prev = prev;
  }

  private void moveToHead(LinkedNode node){
    removeNode(node);
    addNode(node);
  }

  private LinkedNode popTail() {
    LinkedNode res = tail.prev;
    removeNode(res);
    return res;
  }

  private Hashtable<Integer, LinkedNode> cache = new Hashtable<Integer,LinkedNode>();
  private int size;
  private int capacity;
  private LinkedNode head, tail;

  public LRUCache(int capacity) {
    this.size = 0;
    this.capacity = capacity;
    head = new LinkedNode();
    tail = new LinkedNode();
    head.next = tail;
    tail.prev = head;
  }

  public int get(int key) {
    LinkedNode node = cache.get(key);
    if (node == null) return -1;
    moveToHead(node);
    return node.value;
  }

  public void put(int key, int value) {
    LinkedNode node = cache.get(key);

    if(node == null) {
      LinkedNode newNode = new LinkedNode();
      newNode.key = key;
      newNode.value = value;
      cache.put(key, newNode);
      addNode(newNode);
      ++size;
      if(size > capacity) {
        LinkedNode tail = popTail();
        cache.remove(tail.key);
        --size;
      }
    } else {
      node.value = value;
      moveToHead(node);
    }
  }
}

总结

本题来源于Leetcode中 归属于链表类型题目。
同许多在算法道路上不断前行的人一样,不断练习,修炼自己!
如有博客中存在的疑问或者建议,可以在下方留言一起交流,感谢各位!

觉得本博客有用的客官,可以给个点赞+收藏哦! 嘿嘿

喜欢本系列博客的可以关注下,以后除了会继续更新面试手撕代码文章外,还会出其他系列的文章!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值