142. 环形链表 II

思路:快慢指针,快慢指针相遇后,慢指针回到头,快慢指针步伐一致一起移动,相遇点即为入环点

/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
// 没有结点或只有一个非自环结点
if (head == null || head.next == null) {
return null;
} ListNode slow = head;
ListNode fast = head.next; while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next; if (fast == slow) {
//快指针从第一次相交点下一个节点开始移动, 慢指针重新从头开始移动
fast = fast.next; //注意
slow = head; while (fast != slow) {
fast = fast.next;
slow = slow.next;
} // 相遇点即为入环点
return fast;
}
} return null;
}
}

146. LRU 缓存机制

class LRUCache {
// 用 map 我们可以实现常数复杂度的get,但无法记录各个页面的先后顺序。
// 因此引入双向链表来记录页面先后顺序。 private Map<Integer, Node> map;
private Node head;
private Node tail;
private int capacity;
private int size = 0; public LRUCache(int capacity) {
this.map = new HashMap<>();
this.head = new Node(-1, -1);
this.tail = new Node(-1, -1);
head.next = tail;
tail.pre = head;
this.capacity = capacity;
} public int get(int key) {
if (map.containsKey(key)) {
Node temp = map.get(key);
moveToHead(temp);
return temp.val;
} else {
return -1;
}
} public void put(int key, int value) {
if (map.containsKey(key)) {
Node temp = map.get(key);
temp.val = value;
moveToHead(temp);
} else {
if (size >= capacity) {
Node tempTail = removeTail();
int tempKey = tempTail.key;
map.remove(tempKey);
size--;
} Node newNode = new Node(key, value);
map.put(key, newNode);
addHead(newNode);
size++;
}
} private void moveToHead(Node node) {
removeNode(node);
addHead(node);
} private Node removeTail() {
Node temp = tail.pre;
removeNode(temp); return temp;
} private void removeNode(Node node) {
node.next.pre = node.pre;
node.pre.next = node.next;
} private void addHead(Node node) {
node.next = head.next;
node.pre = head; head.next = node;
node.next.pre = node;
}
} class Node {
Node pre;
Node next;
int key;
int val; Node(int key, int val) {
this.key = key;
this.val = val;
}
} /**
* 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);
*/

148. 排序链表

思路:O(nlogn)的复杂度实现排序可以考虑 归并 或 快速 排序。

归并排序:

/**
* 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 sortList(ListNode head) {
return mergeSort(head);
} private ListNode mergeSort(ListNode head) {
// base case
if (head == null || head.next == null) {
return head;
} ListNode mid = getMidNode(head);
ListNode temp = mid.next;
mid.next = null; ListNode l1 = mergeSort(head);
ListNode l2 = mergeSort(temp); return merge(l1, l2);
} // 快慢指针找中点
private ListNode getMidNode(ListNode head) {
ListNode slow = head;
ListNode fast = head.next; while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
} return slow;
} // 合并两个链表
private ListNode merge(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(-1);
ListNode temp = dummy; while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
temp.next = l1;
l1 = l1.next;
} else {
temp.next = l2;
l2 = l2.next;
} temp = temp.next;
} if (l1 != null) {
temp.next = l1;
} else if (l2 != null) {
temp.next = l2;
} return dummy.next;
}
}

快速排序:

class Solution {
public ListNode sortList(ListNode head) {
return quickSort(head, null);
} // start, end 之间进行快速排序,左闭右开
private ListNode quickSort(ListNode start, ListNode end) {
// base case
if (start == end || start.next == end) {
return start;
}
// 选择第一个结点为pivot
ListNode pivot = start;
// left, right 都指向pivot
ListNode left = pivot, right = pivot; // 遍历 pivot 之后,end之前的所有结点,将小于 pivot 的放到 pivot 之前;否者放到 pivot 后面
ListNode cur = pivot.next;
while (cur != end) {
// 下面的插入操作会修改 cur.next
ListNode next = cur.next; if (cur.val < pivot.val) {
cur.next = left;
left = cur;
} else {
right.next = cur;
right = cur;
} cur = next;
} // 重要,将操作好的链表的末尾指向 end
right.next = end; ListNode pre = quickSort(left, pivot);
ListNode post = quickSort(pivot.next, end);
// 重要,前后两段链表通过 pivot 连接起来
pivot.next = post; return pre;
}
}

152. 乘积最大子数组

class Solution {
public int maxProduct(int[] nums) {
int len = nums.length; // 状态: dp[i][0]:以下标i结尾的子数组的最大乘积
// dp[i][1]:以下标i结尾的子数组的最小乘积
int[][] dp = new int[len][2]; //base case
dp[0][0] = nums[0];
dp[0][1] = nums[0]; int max = nums[0];
for (int i = 1; i < len; i++) {
if (nums[i] > 0) {
dp[i][0] = Math.max(nums[i], nums[i] * dp[i - 1][0]);
dp[i][1] = Math.min(nums[i], nums[i] * dp[i - 1][1]);
} else {
dp[i][0] = Math.max(nums[i], nums[i] * dp[i - 1][1]);
dp[i][1] = Math.min(nums[i], nums[i] * dp[i - 1][0]);
} max = Math.max(max, dp[i][0]);
} return max;
}
}

155. 最小栈

class MinStack {

    /** initialize your data structure here. */
private Deque<Integer> stack;
// 用额外一个栈存储最小值
private Deque<Integer> minstack;
public MinStack() {
stack = new LinkedList<>();
minstack = new LinkedList<>();
} public void push(int val) {
stack.push(val);
if (minstack.isEmpty() || val < minstack.peek()) {
minstack.push(val);
} else {
minstack.push(minstack.peek());
}
} public void pop() {
stack.pop();
minstack.pop();
} public int top() {
return stack.peek();
} public int getMin() {
return minstack.peek();
}
} /**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/

160. 相交链表

思路:指向两个链表的指针一起分别向后移,如果没有剩余元素就移动到另一条链表,然后继续后移。如果有交点,相遇时就不为null

/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode lA = headA, lB = headB; while (lA != lB) {
lA = lA == null ? headB : lA.next;
lB = lB == null ? headA : lB.next;
} return lA;
}
}

169. 多数元素

思路一:用map记录每个元素的出现次数:

class Solution {
public int majorityElement(int[] nums) {
int threshold = nums.length / 2; Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
if (map.get(num) > threshold) {
return num;
}
} throw new IllegalArgumentException("majority element not exist!");
}
}

思路二:排序:

class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length / 2];
}
}

思路三:摩尔投票,很简单:记录当前字符和出现频次,如果字符相同则频次加1,否则减1,减1后若频次为0则直接更换当前字符和频次。

class Solution {
public int majorityElement(int[] nums) {
int curElement = nums[0];
int cnt = 1; for (int i = 1; i < nums.length; i++) {
if (curElement == nums[i]) {
cnt++;
} else {
cnt--;
if (cnt == 0) {
curElement = nums[i];
cnt = 1;
}
}
} return curElement;
}
}

198. 打家劫舍

思路:动态规划

class Solution {
public int rob(int[] nums) {
int len = nums.length;
//状态: dp[i] 表示 有i间房屋时可以偷窃的最大金额
int[] dp = new int[len + 1]; //base case
dp[0] = 0;
dp[1] = nums[0]; int max = nums[0];
for (int i = 2; i < len + 1; i++) {
// 第 i - 1 个房间 可以偷, 也可以不偷
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i - 1]);
max = Math.max(max, dp[i]);
} return max;
}
}

200. 岛屿数量

思路:DFS。

  • 对于二维矩阵中象成一个对每个节点来说,他有上、下、左、右四个邻居,可以将每个岛屿都看成一个图。
  • 从任意一个陆地进入开始遍历,遍历完1次就代表发现了一个岛屿。 注:图不像树那样是有向的,遍历可能会访问重复结点,一般需要用额外结构表示结点是否已经被访问过。此题可以直接在矩阵上将1修改为2表示结点已经访问过。

在原矩阵中标记是否访问过:

class Solution {
public int numIslands(char[][] grid) {
int count = 0;
for (int i = 0; i < grid.length; i++) {
for (int j = 0; j < grid[0].length; j++) {
if (grid[i][j] == '1') {
dfs(grid, i, j);
count++;
}
}
} return count;
} private void dfs(char[][] grid, int row, int col) {
//base case
if (!inArea(grid, row, col)) {
return;
} if (grid[row][col] != '1') {
return;
} //已访问
grid[row][col] = '2'; dfs(grid, row + 1, col);
dfs(grid, row - 1, col);
dfs(grid, row, col + 1);
dfs(grid, row, col - 1);
} private boolean inArea(char[][] grid, int row, int col) {
return row >= 0 && row < grid.length &&
col >= 0 && col < grid[0].length;
}
}

使用额外空间标记是否已经访问过:

class Solution {

    // 标记是否访问过
private boolean[][] visited; public int numIslands(char[][] grid) {
int row = grid.length;
int col = grid[0].length;
visited = new boolean[row][col]; int cnt = 0; for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (grid[i][j] == '1' && !visited[i][j]) {
dfs(grid, i, j);
cnt++;
}
}
} return cnt;
} private void dfs (char[][] grid, int i, int j) {
if (!inArea(grid, i, j)) {
return;
} if (grid[i][j] != '1' || visited[i][j]) {
return;
} visited[i][j] = true; dfs(grid, i + 1, j);
dfs(grid, i, j + 1);
dfs(grid, i - 1, j);
dfs(grid, i, j - 1);
} private boolean inArea(char[][] grid, int row, int col) {
return row >= 0 && row < grid.length &&
col >= 0 && col < grid[0].length;
}
}

推荐阅读:岛屿类问题的通用解法、DFS 遍历框架最大人工岛

206. 反转链表

思路一:迭代法:

/**
* 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 reverseList(ListNode head) {
//始终指向当前结点的前一个结点
ListNode pre = null;
//当前结点
ListNode cur = head; while (cur != null) {
//用来保存当前结点的下一个结点
ListNode next = cur.next; cur.next = pre;
pre = cur; cur = next;
} return pre;
}
}

思路二:递归(锻炼递归思维):

  • 首先给出函数定义,如此题:ListNode reverseList(ListNode head)

    1. 反转以head为头结点的链表
    2. 返回反转后链表的头结点
  • 不要用脑袋去模拟递归栈,根据函数的定义去处理递归的子问题,即具体到一个结点要做的事情

  • 确定base case

class Solution {
public ListNode reverseList(ListNode head) {
if (head == null) {
return head;
} //base case, 当链表只有一个结点时退出递归
if (head.next == null) {
return head;
} /**
* 具体到头结点来说,反转当前链表只需要两步:
* 1.反转以head.next为头的链表
* 2.将head插入head.next为头的链表反转之后的链表末尾
*/
ListNode vhead = reverseList(head.next); //根据递归函数定义,返回反转之后的链表头 //反转之后head.next位于链表尾部,将head插入head.next之后
head.next.next = head;
head.next = null; return vhead;
}
}

最新文章

  1. 移动端报表JS开发示例
  2. java基本数据类型取值范围
  3. [杂谈]交通工具orca card
  4. 浅谈JavaSccript函数与对象
  5. Android常见开源解决方案
  6. document.createDocumentFragment 方法
  7. Hibernate3提供的属性的延迟加载功能
  8. 怎么利用composer创建laravel项目
  9. Linux之正则表达式
  10. C-Linux_定时器示例使用
  11. 【JVM】参数配置
  12. python return 返回多个值
  13. IOS 将图片转换为圆角图
  14. equals 与 == 的区别
  15. mysql 常用语句记录
  16. python-day21--序列化模块模块
  17. 【ruby题目】以|为分割点,将arr转换为二维数组
  18. oracle rowid 研究
  19. spring mongodb查询
  20. Knockout结合Bootstrap创建动态UI--产品列表管理

热门文章

  1. NOIP模拟测试17「入阵曲&amp;#183;将军令&amp;#183;星空」
  2. 理解css行高(line-height)
  3. uniapp 打包IOS 更新AppStore版本
  4. 试着给VuePress添加登录授权支持,基于v-dialogs
  5. 05 找出占用CPU、内存过高的进程
  6. flex PopUpManager createPopUp方式弹出窗口
  7. LeSS 的诞生(一):大规模团队该何去何从
  8. AcWing 239. 奇偶游戏
  9. docker下创建redis cluster集群
  10. yum的卸载和安装