- 写于2019年6月13日、6月17日
文章目录
- [142. 环形链表 II](https://leetcode.com/problems/linked-list-cycle-ii/)
- [146. LRU缓存机制](https://leetcode.com/problems/lru-cache/)
- [148. 排序链表](https://leetcode.com/problems/sort-list/)
- [152. 乘积最大子序列](https://leetcode.com/problems/maximum-product-subarray/)
- [200. 岛屿数量](https://leetcode.com/problems/number-of-islands/)
142. 环形链表 II
① 题目描述
中文题目:https://leetcode-cn.com/problems/linked-list-cycle-ii/
② 使用HashMap
- 使用
HashMap
存储所有的非空结点,如果发现结点已经存在,便返回该结点。 - 代码如下,运行时间
8ms
:
public ListNode detectCycle(ListNode head) {
HashMap<ListNode, Integer> map = new HashMap<>();
while (head != null) {
if (map.containsKey(head)) {
return head;
}
map.put(head, 1);
head = head.next;
}
return null;
}
③ 快慢指针(不行!只能判断是否有环)
- 之前在
环形链表
中,使用了快慢指针,于是想使用快慢指针,发现不可以!快慢指针只能判断是否存在环。 - 以
head = [3,2,0,-4], pos = 1
为例,发现在结点0处,slow和fast指针发生相遇,并非在结点2处相遇!
- 代码如下:
public ListNode detectCycle(ListNode head) {
if (head == null) {
return null;
}
ListNode slow = head, fast = head.next;
while (slow != fast) {
if (fast == null || fast.next == null) {
return null;
}
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
146. LRU缓存机制
① 题目描述
中文题目:https://leetcode-cn.com/problems/lru-cache/
② 自己的想法(使用List+哈希表)
- 使用List数组作为缓存,最近使用的放到
index = 0
的位置,最近最少使用的放到尾部。 - 将key和value设计为Node类,List中存放node对象。
- 使用哈希表存储对应的结点,方便get时实现线性查找。
- get时,若结点不在哈希表中,肯定不在List中,直接返回-1;如果结点在哈希表中,需要返回实际的只,并更新List中对应结点的热度(将他从原始的位置删除,然后在头部添加);
- put时,若结点在哈希表中,需要更新List中对应结点的value并将其放置到头部;如果不在哈希表中,而此时缓存已满,则需要删除List末尾的结点和哈希表中对应的结点,并将新节点加入到List和哈希表中;否则,直接在List头部添加结点,并在哈希表中添加结点。
- 哈希表和List有着
相辅相承
的作用。 - 代码如下,运行时间
240ms
:
class Node {
public int key;
public int value;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
public class LRUCache {
private List<Node> cache;
private HashMap<Integer, Node> map;
private int capacity;
public LRUCache(int capacity) {
cache = new ArrayList<>();// index=0,表示最近被访问的结点;尾部表示最近最少使用的结点
map = new HashMap<>();
this.capacity = capacity;
}
public int get(int key) {
if (map.containsKey(key)) {// 结点已经存在,更新cache中的热度
Node node = map.get(key);
deleteNode(node.key, node.value);// 先删除对应的结点,再在头部添加新结点
cache.add(0, new Node(node.key, node.value));
return node.value;
}
return -1;// 结点不存在,返回-1
}
public void put(int key, int value) {
if (map.containsKey(key)) {// 该节点已经存在,删除cache中已经存在的结点,为更新热度和值做准备
Node node = map.get(key);
deleteNode(node.key, node.value);
} else if (cache.size() == capacity) {// 结点不存在,但是已经超过容量,删除cache和哈希中的最后一个结点
Node node = cache.get(capacity - 1);
cache.remove(capacity - 1);
map.remove(node.key);
}
map.put(key, new Node(key, value));// 更新哈希表和cache
cache.add(0, new Node(key, value));
}
public void deleteNode(int key, int value) {
for (int i = 0; i < cache.size(); i++) {
if (cache.get(i).key == key && cache.get(i).value == value) {
cache.remove(i);
break;
}
}
}
}
148. 排序链表
① 题目描述
中文题目:https://leetcode-cn.com/problems/sort-list/
② 自己的想法:借助List
- 第一次遍历,获取链表结点的值存入
List
中;对List
排序后,第二次遍历,更改每个结点的值。 - 时间复杂度: O ( 3 n ) O(3n) O(3n),更改结点值的同时需要遍历List;空间复杂度 O ( n ) O(n) O(n)。
- 代码如下,运行时间
13ms
:
public ListNode sortList(ListNode head) {
if (head == null) {
return head;
}
List<Integer> list = new ArrayList<>();
ListNode p = head;
while (p != null) {
list.add(p.val);
p = p.next;
}
Collections.sort(list);
p = head;
int i = 0;
while (p != null) {
p.val = list.get(i++);
p = p.next;
}
return head;
}
152. 乘积最大子序列
① 题目描述
中文题目:https://leetcode-cn.com/problems/maximum-product-subarray/
② 暴力法
- 直接上代码,运行时间
86ms
:
public int maxProduct(int[] nums) {
int result = Integer.MIN_VALUE;
for (int i = 0; i < nums.length; i++) {
int temp = 1;
for (int j = i; j < nums.length; j++) {
temp = temp * nums[j];
if (temp > result) {
result = temp;
}
}
}
return result;
}
200. 岛屿数量
① 题目描述
中文题目:https://leetcode-cn.com/problems/number-of-islands/
② DFS
- 发现基于矩阵的搜索,基本都是用
dfs
,而且需要借助visited
数组,避免重复访问。 - 把每个字符为1的位置,都视为一种可能的岛屿的起始点,不断向周围扩展,直到访问至边界。
- 访问的方向一般都是:上、下、左、右。
- 停止访问的条件是:
i < 0 || i >= row || j < 0 || j >= col
- 代码如下,运行时间
1ms
:
public int numIslands(char[][] grid) {
if (grid.length==0||grid[0].length==0){
return 0;
}
int result = 0;
boolean[][] visited = new boolean[grid.length][grid[0].length];
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (!visited[i][j] && grid[i][j] == '1') {
result++;
dfs(grid, i, j, visited);
}
}
}
return result;
}
public void dfs(char[][] grid, int i, int j, boolean[][] visited) {
if (i >= 0 && i < grid.length && j >= 0 && j < grid[0].length// 行、列合法
&& !visited[i][j] && grid[i][j] == '1') {// 未被访问,是陆地
visited[i][j]=true;// 标记为被访问过
dfs(grid, i, j - 1, visited);// 左边位置
dfs(grid, i, j + 1, visited);// 右边位置
dfs(grid, i - 1, j, visited);// 上方位置
dfs(grid, i + 1, j, visited);// 下方位置
}
}