LeetCode Hot 100
1.两数之和
暴力解法:时间/空间复杂度 O(N²),O(1)
class Solution {
public int[] twoSum(int[] nums, int target) {
for(int i=0;i<nums.length;i++){
for(int j=i+1;j<nums.length;j++){
if(nums[i] + nums[j] == target){
return new int[]{i,j};
}
}
}
return new int[0];
}
}
哈希表解法:时间/空间复杂度 O(N),O(N)
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> hashRes = new HashMap<Integer, Integer>();
for(int i = 0;i<nums.length;i++){
if(hashRes.containsKey(target - nums[i])){
return new int[]{hashRes.get(target - nums[i]),i};
}
hashRes.put(nums[i],i);
}
return new int[0];
}
}
2.字母异位词分组
用排序方法做。
时间复杂度:O(nklogk),空间复杂度:O(nk)。
// getOrDefault(K,defaultValue) - 返回与指定键K关联的值。如果找不到键,则返回defaultValue。
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
Map<String, List<String>> res = new HashMap<String, List<String>>();
for(String str : strs){
char[] target = str.toCharArray();
Arrays.sort(target);
String key = new String(target);
List<String> list = res.getOrDefault(key,new ArrayList<String>());
list.add(str);
res.put(key,list);
}
return new ArrayList<List<String>>(res.values());
}
}
3.最长连续序列
class Solution {
public int longestConsecutive(int[] nums) {
// 创建HashSet集合,存储数组中的整数,并且去重
Set<Integer> nums_set = new HashSet<Integer>();
for(int num:nums){
nums_set.add(num);
}
int maxLength = 0;
for(int num : nums_set){
//如果集合中不包含num-1的值,那么说明这个数字可能是连续序列的起点
if(!nums_set.contains(num - 1)) {
int currentNum = num;
int currentLength = 1;
//当集合中包含当前数+1的值时,说明该序列可以继续延申
while(nums_set.contains(currentNum + 1)){
currentNum += 1;
currentLength += 1;
}
//取最长连续序列
maxLength = Math.max(maxLength,currentLength);
}
}
return maxLength;
}
}
4.移动零
class Solution {
public void moveZeroes(int[] nums) {
int left = 0;
int right = 0;
while(right < nums.length){
if(nums[right] != 0){
int n = nums[left];
nums[left] = nums[right];
nums[right] = n;
left++;
}
right++;
}
}
}
5.盛最多的水
看到“左右”想到双链表
class Solution {
public int maxArea(int[] height) {
int areaMax = 0;
int l = 0;
int r = height.length-1;
while(l < r){
areaMax = Math.max(areaMax,Math.min(height[l],height[r])*(r-l));
if(height[l] < height[r]){
l++;
}else{
r--;
}
}
return areaMax;
}
}
6.三数之和
“去重”是关键
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
Arrays.sort(nums);
for(int i=0; i < nums.length; i++){
if(nums[i]>0){
return res;
}
if(i>0 && nums[i] == nums[i-1]){
continue;
}
int l = i+1;
int r = nums.length - 1;
while(r>l){
if(nums[i] + nums[l] + nums[r]>0){
r--;
}else if(nums[i] + nums[l] + nums[r]<0){
l++;
}else {
List<Integer> list = new ArrayList<Integer>();
list.add(nums[i]);
list.add(nums[l]);
list.add(nums[r]);
res.add(list);
while(r > l && nums[r] == nums[r-1]){
r--;
}
while(r > l && nums[l] == nums[l+1]){
l++;
}
l++;
r--;
}
}
}
return res;
}
}
8. 无重复字符的最长字串
思路:
滑动窗口设置左右指针
使用哈希存储不重复的值
如果hash种存在即将划入的元素,删除左指针节点,滑动窗口左指针
如果不存在,存储右节点
求最大值
class Solution {
public int lengthOfLongestSubstring(String s) {
// 滑动窗口
// 最大长度
int maxLength = 0;
// 使用哈希表存储不重复的值
HashSet<Character> hash = new HashSet<>();
//开始滑动
//设定左右边界
int l = 0;
for(int r = 0; r < s.length(); r++){
//如果Hash表中存储了即将滑入的元素 -> 移除窗口左侧的元素
while(hash.contains(s.charAt(r))){
//删除左指针的元素
hash.remove(s.charAt(l));
// 滑动窗口左侧右移
l++;
}
// 存储右侧元素
hash.add(s.charAt(r));
//求最大值
maxLength = Math.max(maxLength,r - l + 1);
}
return maxLength;
}
}
9. 找到字符串中所有字母异位词
class Solution {
public List<Integer> findAnagrams(String s, String p) {
int [] s1 = new int[26];
int [] p1 = new int[26];
for(int i = 0; i < p.length(); i++){
p1[p.charAt(i) - 'a']++;
}
List<Integer> res = new ArrayList<>();
for(int l = 0,r = 0; r < s.length(); r++ ){
s1[s.charAt(r) - 'a']++;
if(r - l + 1 >p.length())s1[s.charAt(l++) - 'a']--;
if(r - l + 1 == p.length()){
if(isSame(s1,p1)){
res.add(l);
}
}
}
return res;
}
boolean isSame(int[] a,int [] b){
for(int i = 0;i<a.length;i++){
if(a[i] != b[i]) return false;
}
return true;
}
}
10.和为k的子数组
class Solution {
public int subarraySum(int[] nums, int k) {
int res = 0;
// 记录和为k的子数组的数量
int count = 0;
// 前缀和
int pre = 0;
// 存储前缀和出现的次数
Map<Integer,Integer> map = new HashMap<>();
// 记录0出现的次数为1
map.put(0,1);
// 对数组进行遍历
for(int i = 0; i < nums.length; i++){
// 存储前缀和,每一项前缀和=现在的数+遍历数组
pre += nums[i];
// 如数组中存在前缀和 - k
if(map.containsKey(pre - k)){
// 那么记录出现的次数
count += map.get(pre - k);
}
// 存放前缀出现的次数
map.put(pre,map.getOrDefault(pre,0) + 1);
}
return count;
}
}
13.最大子数组合
动态规划
判断数组长度是否为空
定义一个dp数组用于判断
判断当前dp值 与 上一个dp值+当前值大小
判断当前结果是否大于当前值,如果大于保当前结果,如果小于保存当前值
class Solution {
public int maxSubArray(int[] nums) {
if(nums.length == 0){
return 0;
}
int res = nums[0];
int[] dp = new int[nums.length];
dp[0] = nums[0];
for(int i = 1; i<nums.length; i++){
dp[i] = Math.max((dp[i-1] + nums[i]),nums[i]);
res = res > dp[i] ? res : dp[i];
}
return res;
}
}
14. 合并区间
class Solution {
public int[][] merge(int[][] intervals) {
// 如果数组为空返回空
if(intervals.length == 0){
return new int[0][2];
}
// 结果
List<int[]> res = new LinkedList<>();
// 将数组按升序排序
Arrays.sort(intervals,(x,y) -> Integer.compare(x[0],y[0]));
// 左边界
int start = intervals[0][0];
// 右边界
int rightBound = intervals[0][1];
for(int i = 1; i < intervals.length; i++){
// 如果右数组的左边界 大于 左数组的右边界
if(intervals[i][0] > rightBound){
// 将数组加入
res.add(new int[]{start,rightBound});
// 更新左右边界
start = intervals[i][0];
rightBound = intervals[i][1];
}else{
// 更新右边界,在右边界和新右边界中取最大
rightBound = Math.max(rightBound,intervals[i][1]);
}
}
res.add(new int[]{start,rightBound});
return res.toArray(new int[res.size()][]);
}
}
15. 除自身以外数组的乘积
class Solution {
public int[] productExceptSelf(int[] nums) {
//数组长度
int n = nums.length;
// 左数组 = 数组左*左-1
int[] left = new int[n];
// 右数组 = 数组右*右-1
int[] right = new int[n];
// 结果集
int[] res = new int[n];
left[0] = 1;
right[n-1] = 1;
for(int i = 1; i < n; i++){
left[i] = left[i-1] * nums[i-1];
}
for(int i = n-2; i > -1; i--){
right[i] = right[i+1] * nums[i+1];
}
for(int i = 0; i < n; i++){
res[i] = left[i] * right[i];
}
return res;
}
}
17. 矩阵置零
class Solution {
public void setZeroes(int[][] matrix) {
int c = matrix.length;
int r = matrix[0].length;
boolean flagCol0 = false;
boolean flagRow0 = false;
for(int i = 0; i < c; i++){
if(matrix[i][0] == 0){
flagCol0 = true;
}
}
for(int j = 0; j < r; j++){
if(matrix[0][j] == 0){
flagRow0 = true;
}
}
for(int i = 1; i < c; i++){
for(int j = 1; j < r;j++){
if(matrix[i][j] == 0){
matrix[i][0] = matrix[0][j] = 0;
}
}
}
for(int i = 1; i < c; i++){
for(int j = 1; j < r;j++){
if(matrix[i][0] == 0 || matrix[0][j] == 0){
matrix[i][j] = 0;
}
}
}
if(flagCol0){
for(int i = 0; i < c; i++){
matrix[i][0] = 0;
}
}
if(flagRow0){
for(int j = 0; j < r; j++){
matrix[0][j] = 0;
}
}
}
}
18. 螺旋矩阵
class Solution {
public List<Integer> spiralOrder(int[][] matrix) {
List<Integer> res = new ArrayList<>();
if(matrix.length == 0){
return res;
}
int left = 0;
int right = matrix[0].length - 1;
int up = 0;
int down = matrix.length -1;
int col,row;
while(true){
for(col = left; col <= right; ++col){
res.add(matrix[up][col]);
}
if(++up > down){
break;
}
for(row = up; row <= down; ++row){
res.add(matrix[row][right]);
}
if(--right < left){
break;
}
for(col = right; col>= left; --col){
res.add(matrix[down][col]);
}
if(--down < up){
break;
}
for(row = down; row >= up; --row){
res.add(matrix[row][left]);
}
if(++left > right){
break;
}
}
return res;
}
}
19. 旋转图像
class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
// 水平翻转
for(int i = 0; i < n / 2; ++i){
for(int j = 0; j < n; ++j){
int temp = matrix[i][j];
matrix[i][j] = matrix[n-1-i][j];
matrix[n-1-i][j] = temp;
}
}
// 主对角线翻转
for(int i = 0; i < n; ++i){
for(int j = 0; j < i; ++j){
int temp = matrix[i][j];
matrix[i][j] = matrix[j][i];
matrix[j][i] = temp;
}
}
}
}
20.相交链表
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == null || headB == null){
return null;
}
ListNode tempA = headA;
ListNode tempB = headB;
while(tempA != tempB){
tempA = tempA == null ? headB : tempA.next;
tempB = tempB == null ? headA : tempB.next;
}
return tempA;
}
}
21. 反转链表
方法1:迭代
class Solution {
public ListNode reverseList(ListNode head) {
ListNode p = head;//
ListNode res = null;//null
while(p != null){
ListNode next = p.next;//
p.next = res;//
res = p;//
p = next;//
}
return res;
}
}
方法2:递归
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}
23.回文链表
/**
* 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 boolean isPalindrome(ListNode head) {
//只有一个节点或空链表时,属于回文链表
if(head == null || head.next == null){
return true;
}
//当链表只有两个节点,判断两个节点是否相等
if(head.next.next == null){
return head.val == head.next.val;
}
//定义快慢指针,寻找中位数
ListNode fast = head;
ListNode slow = head;
//表长可能为奇数,可能为偶数
while(fast.next != null && fast.next.next != null){
slow = slow.next;
fast = fast.next.next;
}
//此时得到中位数slow,通过slow把表划分为两个区间
//得到右链表的反转链表
ListNode right = reverse(slow.next);
//和左链表进行比较
ListNode left = head;
//left和right同时后移,直至right指向null
while(right != null){
if(left.val != right.val){
return false;
}else{
right = right.next;
left = left.next;
}
}
return true;
}
//反转链表
private 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;
}
}
环形链表 I
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while(slow != fast){
if(fast == null || fast.next == null){
return false;
}
slow = slow.next;
fast = fast.next.next;
}
return true;
}
}
环形链表 II
方法1:hash
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null || head.next == null){
return null;
}
Set<ListNode> set = new HashSet<>();
ListNode pos = head;
while(pos != null){
if(set.contains(pos)){
return pos;
}else{
set.add(pos);
}
pos = pos.next;
}
return null;
}
}
方法2:快慢指针
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
ListNode b = fast;
ListNode a = head;
while(a != b){
a = a.next;
b = b.next;
}
return a;
}
}
return null;
}
}
合并两个有序链表
class Solution {
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode nodeHead = new ListNode(-1);
ListNode node = nodeHead;
while(list1 != null && list2 != null){
if(list1.val >= list2.val){
node.next = list2;
list2 = list2.next;
} else {
node.next = list1;
list1 = list1.next;
}
node = node.next;
}
node.next = list1 == null ? list2 : list1;
return nodeHead.next;
}
}
删除链表的倒数第N个结点
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode node = new ListNode(0,head);
int length = getListLength(head);
ListNode cur = node;
for(int i = 1;i< length - n + 1; i++){
cur = cur.next;
}
cur.next = cur.next.next;
ListNode res = node.next;
return res;
}
public int getListLength(ListNode head){
int length = 0;
while(head != null){
length++;
head = head.next;
}
return length;
}
}
两两交换链表中的节点
方法1:递归
class Solution {
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode newHead = head.next;
head.next = swapPairs(newHead.next);
newHead.next = head;
return newHead;
}
}
方法2:迭代
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode pre = new ListNode(0);
pre.next = head;
ListNode temp = pre;
while(temp.next != null && temp.next.next != null){
ListNode node1 = temp.next;
ListNode node2 = temp.next.next;
temp.next = node2;
node1.next = node2.next;
node2.next = node1;
temp = node1;
}
return pre.next;
}
}
K个一组翻转链表
设置虚拟头节点,虚拟头节点的下一个节点指向头节点
设置前后指针,记录链表前后位置
翻转,找到尾节点
判断临界条件
记录尾节点的下一个节点,头节点的上一个节点
头节点和尾节点断开
将头节点和尾节点连起来
继续移动前后指针
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
//虚拟头节点 从0开始
ListNode node = new ListNode(0);
//虚拟头节点的下一个位置指向原来的头节点
node.next = head;
//引入一个指针,记录pre的位置,pre指针指向翻转子链表头节点的前一个位置
//当对1,2,3子链表进行翻转时,要记录子链表前一个指针的位置
//翻转后需要把指针的next指针指向翻转后的子链表
ListNode pre = node;
// end 指针,指向翻转子链表的尾节点,同样指向虚拟头节点
ListNode end = node;
// 翻转
while(end.next != null){
// 找到翻转子链表的尾节点,移动k步
for(int i = 0; i < k && end != null; i++){
end = end.next;
}
// 判断临界条件
if(end == null){
break;
}
// next 记录翻转子链表尾节点的下一个节点
ListNode next = end.next;
ListNode start = pre.next;
// 翻转子链表尾节点 和 后面断开
end.next = null;
//翻转子链表头节点 和 前面断开
pre.next = null;
// 翻转链表,翻转后和前面的节点连接起来
pre.next = reverseList(start);
//翻转后和后面的节点连接起来
start.next = next;
//翻转后将pre指针移动到start的位置,继续进行下一次的翻转
//移动两个指针pre 和 end
pre = start;
end = start;
}
return node.next;
}
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;
}
}
链表的中间结点
class Solution {
public ListNode middleNode(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}
排序链表
思路:
1.找到链表中间节点
2.将链表分成N个子链表
3.合并N个子链表
class Solution {
public ListNode sortList(ListNode head) {
//递归终止条件
if(head == null || head.next == null){
return head;
}
//递归操作,1:找中间节点mid
ListNode mid = middle(head);
//找到中间节点的下一个节点
ListNode next = mid.next;
//断开中间节点和下一个节点next
mid.next = null;
//现在已经将链表分别两个链表
//接着对左右两个子链表分别排序
ListNode left = sortList(head);
ListNode right = sortList(next);
//两个链表排序后进行合并
return mergeList(left,right);
}
public ListNode middle(ListNode head){
ListNode slow = head;
ListNode fast = head.next;
while(fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
public ListNode mergeList(ListNode node1,ListNode node2){
ListNode newNode = new ListNode(0);
ListNode node = newNode;
while(node1 != null && node2 != null){
if(node1.val >= node2.val){
node.next = node2;
node2 = node2.next;
}else{
node.next = node1;
node1 = node1.next;
}
node = node.next;
}
node.next = node1 == null ? node2 : node1;
return newNode.next;
}
}
随机链表的复制
class Solution {
Map<Node,Node> cachedNode = new HashMap<Node,Node>();
public Node copyRandomList(Node head) {
if(head == null){
return null;
}
if(!cachedNode.containsKey(head)){
Node newHead = new Node(head.val);
cachedNode.put(head,newHead);
newHead.next = copyRandomList(head.next);
newHead.random = copyRandomList(head.random);
}
return cachedNode.get(head);
}
}
LRU缓存
class LRUCache extends LinkedHashMap<Integer,Integer>{
private int capacity;
public LRUCache(int capacity){
super(capacity,0.75F,true);
this.capacity = capacity;
}
public int get(int key){
return super.getOrDefault(key,01);
}
public void put(int key,int value){
super.put(key,value);
}
@Override
protected boolean removeEldestEntry(Map,Entry<Integer,Integer> eldest){
return size() > capacity;
}
}
class LRUCache {
//双向链表
class DLinkedNode {
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
public DLinkedNode() {}
public DLinkedNode(int _key, int _value){
key = _key;
value = _value;}
}
private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
private int size;
private int capacity;
private DLinkedNode head,tail;
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
//使用伪头部和伪尾部节点
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail;
tail.prev = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
if(node == null){
return -1;
}
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if(node == null){
DLinkedNode newNode = new DLinkedNode(key,value);
cache.put(key,newNode);
addToHead(newNode);
++size;
if(size > capacity){
DLinkedNode tail = removeTail();
cache.remove(tail.key);
--size;
}
}
else{
node.value = value;
moveToHead(node);
}
}
private void addToHead(DLinkedNode node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(DLinkedNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveToHead(DLinkedNode node) {
removeNode(node);
addToHead(node);
}
private DLinkedNode removeTail(){
DLinkedNode res = tail.prev;
removeNode(res);
return res;
}
}
二叉树的中序遍历
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
inorder(root,res);
return res;
}
public void inorder(TreeNode root,List<Integer> res){
if(root == null){
return;
}
inorder(root.left,res);
res.add(root.val);
inorder(root.right,res);
}
}
二叉树的最大深度
class Solution {
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}else{
int leftHeight = maxDepth(root.left);
int rightHeight = maxDepth(root.right);
return Math.max(leftHeight,rightHeight) + 1;
}
}
}
翻转二叉树
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root == null){
return null;
}
TreeNode left = invertTree(root.left);
TreeNode right = invertTree(root.right);
root.left = right;
root.right = left;
return root;
}
}
对称二叉树
class Solution {
public boolean isSymmetric(TreeNode root) {
return check(root.left,root.right);
}
public boolean check(TreeNode left,TreeNode right){
if(left == null && right == null){
return true;
}
if( left == null || right == null){
return false;
}
return left.val == right.val &&
check(left.left,right.right) &&
check(left.right,right.left);
}
}
二叉树的直径
class Solution {
private int res = 0;
public int diameterOfBinaryTree(TreeNode root) {
int maxLength = depth(root);
return res;
}
public int depth(TreeNode node){
if(node == null){
return 0;
}
int left = depth(node.left);
int right = depth(node.right);
res = Math.max(left+right,res);
return Math.max(left,right) + 1;
}
}
二叉树的层序遍历
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<List<Integer>>();
if(root == null){
return res;
}
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
while(!queue.isEmpty()){
List<Integer> level = new ArrayList<Integer>();
int size = queue.size();
for(int i = 1;i <= size; i++){;
TreeNode node = queue.poll();
level.add(node.val);
if(node.left != null){
queue.offer(node.left);
}
if(node.right != null){
queue.offer(node.right);
}
}
res.add(level);
}
return res;
}
}
将有序数组转化为二叉搜索树
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return sort(nums,0,nums.length - 1);
}
public TreeNode sort(int[] nums, int left, int right){
if(left > right){
return null;
}
int mid = (left + right) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = sort(nums,left,mid -1);
root.right = sort(nums,mid +1,right);
return root;
}
}