一周练习题目记录
- leetcode 206 反转链表
- leetcode 92 反转链表Ⅱ
- leetcode 138 复制带随机指针的链表
- leetcode 2 两数相加
- leetcode 146 LRU缓存机制
总结回顾
学会思想最重要
leetcode 206 反转链表
题目描述
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
思路回顾详解
-
首先明确链表的数据结构格式
-
- 代码表现如下
public class ListNode {
int val;
ListNode next;
ListNode () {}
ListNode (int val) {
this.val = val;
}
ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
迭代解法
public class Solution {
public ListNode reverseListNode(ListNode head) {
ListNode prev = null;
ListNode current = head;
while (current != null) {
ListNode next = current.next;
current.next = prev;
prev = current;
current = next;
}
return current;
}
}
递归解法
public class Solution {
public ListNode reverseListNode(ListNode head) {
// 递归第一步,判断终止条件
if(head == null || head.next == null) {
return head;
}
// 递归第二步,递的过程
ListNode newHead = reverseListNode(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}
leetcode 92 反转链表Ⅱ
题目描述
反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
迭代解法
- 首先我们需要反转链表的中间部分,所以返回的还是链表的头节点 head
- 需要找到反转链表的开始位置。所以要根据tou节点和开始位置去找。即 head = head.next
- 头节点的位置变动,为了确定返回的时候能够正确地返回头节点,所以需要一个哨兵节点 dummy.next = head
- 找到开始反转地节点位置之后,根据 n 确定反转地次数
- 反转结束之后重新组织链表结构
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
// 首先判断临界情况
if(head == null || left >= right){
return head;
}
ListNode dummy = new ListNode(-1);
dummy.next = head;
head = dummy;
for(int i = 1; i < left;i++) {
head = head.next;
}
ListNode preM = head;
ListNode mNode = head.next;
ListNode nNode = mNode;
ListNode behindN = nNode.next;
for(int i = left;i < right;i++) {
ListNode next = behindN.next;
behindN.next = nNode;
nNode = behindN;
behindN = next;
}
preM.next = nNode;
mNode.next = behindN;
return dummy.next;
}
}
递归解法
- 由简到难探寻递归解法
- 递归反转整个链表地解法如下
class Solution {
public ListNode reverse(ListNode head) {
if(head == null || head.next == null) {
return head;
}
ListNode newHead = reverse(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}
- 那么递归反转链表的前n个节点呢
- 反转整个链表,头节点变成尾部节点,其next节点指向null。那么反转前n个链表就是第n个节点变成头节点
- 即 head.next = nNode.next . 代码如下
class Solution {
ListNode nNode = null;
public ListNode reverseN(ListNode head, int n) {
if(n == 1) {
// 这里 nNode 作为 第n个节点的下一个节点
nNode = head.next;
// 返回第 n 个节点
return head;
}
ListNode newNode = reverseN(head.next, n-1);
head.next.next = head;
head.next = nNode.next;
return nNode;
}
}
- 那么反转链表的一部分 left-right 只要 left=1 即可满足递归条件
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if(left == 1) {
return reverseN(head, right);
}
head.next = reverseBetween(head.next, left-1, right-1);
return head;
}
}
leetcode 138 复制带随机指针的链表
题目描述
给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。
返回复制链表的头节点。
用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
你的代码 只 接受原链表的头节点 head 作为传入参数。
解法一:使用map集合
- 我们可以定义一个map集合存储节点为镜像,再为其建立关系
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
class Solution {
public Node copyRandomList(Node head) {
if(head == null) {
return null;
}
Map<Node, Node> copyMap = new HashMap<>();
// 这里为了不破坏链表结构,使用一个复制链表
Node puppetNode = head;
while(puppetNode != null) {
Node newNode = new Node(puppetNode.val);
copyMap.put(puppetNode, newNode);
puppetNode = puppetNode.next;
}
// 建立其映射关系
puppetNode = head;
while(puppetNode != null) {
if(puppetNode.next != null) {
copyMap.get(puppetNode).next = copyMap.get(puppetNode.next);
}
if(puppetNode.random != null) {
copyMap.get(puppetNode).random = copyMap.get(puppetNode.random);
}
puppetNode = puppetNode.next;
}
return copyMap.get(head);
}
}
解法二:回溯法
class Solution {
Map<Node, Node> copyMap = new HashMap<>();
public Node copyRandomList(Node head) {
if(head == null) {
return null;
}
// 注意终止条件是返回节点需要在集合中存在
if(copyMap.containsKey(head)) {
return copyMap.get(key);
}
//
Node newNode = new Node(head.val);
copyMap.put(head, newNode);
newNode.next = copyRandomList(head.next);
newNode.random = copyRandomList(head.random);
return copyMap.get(head);
}
}
解法三:原地复制
- 给链表的每一个节点后面增加一个其复制节点
- 然后遍历取出复制节点
class Solution {
public Node copyRandomList(Node head) {
if(head == null) {
return null;
}
Node puppetHead = head;
while (puppetHead != null) {
Node newNode = new Node(puppetHead.val);
Node next = puppetHead.next;
puppetHead.next = newNode;
newNode.next = next;
puppetHead = puppetHead.next.next;
}
puppetHead = head;
// 建立随机指针关系
while(puppetHead != null) {
if(puppetHead.random != null) {
puppetHead.next.random = puppetHead.random.next;
}
puppetHead = puppetHead.next.next;
}
Node result = head.next;
Node removeNode = head.next;
while(head != null && head.next != null) {
head.next = head.next.next;
head = head.next;
if(removeNode != null && removeNode.next != null) {
removeNode.next = removeNode.next.next;
removeNode = removeNode.next;
}
}
return result;
}
}
leetcode 2 两数相加
题目描述
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
the answer
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
if(l1 == null) {
return l2;
}
if(l2 == null) {
return l1;
}
ListNode result = new ListNode(-1);
ListNode cur = result;
int carry = 0;
while(l1 != null || l2 != null) {
int v1 = l1==null?0:l1.val;
int v2 = l2==null?0:l2.val;
int sum = v1+v2+carry;
carry = sum/10;
cur.next = new ListNode(sum%10);
cur = cur.next;
l1 = l1==null?null:l1.next;
l2 = l2==null?null:l2.next;
}
if(carry == 1) {
cur.next= new ListNode(carry);
}
return result.next;
}
}
leetcode 146 LRU缓存机制
题目描述
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。
实现 LRUCache 类:
LRUCache(int capacity) 以正整数作为容量 capacity 初始化 LRU 缓存
int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?
The Answer
- 双向链表维护数据结构
- map来存储数据
class LRUCache {
private class DouNode {
DouNode next;
DouNode prev;
int key;
int value;
public DouNode(int key, int value) {
this.key = key;
this.value = value;
}
}
DouNode head = null;
DouNode tail = null;
int capacity;
Map<Integer, DouNode> cacheMap = new HashMap<>();
public LRUCache(int capacity) {
this.capacity = capacity;
head = new DouNode(-1, -1);
tail = new DouNode(-1, -1);
head.next = tail;
tail.prev = head;
}
public int get(int key) {
if(!cacheMap.containsKey(key)) {
return -1;
}
DouNode nowNode = cacheMap.get(key);
// 取出当前节点
nowNode.prev.next = nowNode.next;
nowNode.next.prev = nowNode.prev;
// 因为节点被使用,移动其到头部
moveToHead(nowNode);
return nowNode.value;
}
public void put(int key, int value) {
if(get(key) != -1) {
cacheMap.get(key).value = value;
} else {
DouNode insert = new DouNode(key, value);
// 超出容量,移除尾部节点
if(cacheMap.size() == capacity) {
DouNode remvoeNode = tail.prev;
cacheMap.remove(removeNode.key);
removeNode.prev.next = removeNode.next;
removeNode.next.prev = removeNode.prev;
removeNode.next = null;
removeNode.prev = null;
}
cacheMap.put(key, insert);
moveToHead(insert);
}
}
private void moveToHead(DouNode mNode) {
mNode.next = head.next;
head.next = mNode;
mNode.prev = head;
mNode.next.prev = mNode;
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/
本文题目来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/lru-cache