链表
不要纠结,干就完事了,熟练度很重要!!!多练习,多总结!!!
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中 归属于链表类型题目。
同许多在算法道路上不断前行的人一样,不断练习,修炼自己!
如有博客中存在的疑问或者建议,可以在下方留言一起交流,感谢各位!
觉得本博客有用的客官,可以给个点赞+收藏哦! 嘿嘿
喜欢本系列博客的可以关注下,以后除了会继续更新面试手撕代码文章外,还会出其他系列的文章!