目录
链表
LeetCode-141. 环形链表
LeetCode-19. 删除链表的倒数第 N 个结点
LeetCode-234. 回文链表
LeetCode-206. 反转链表
LeetCode-160. 相交链表
LeetCode - 返回倒数第 k 个节点
LeetCode-21. 合并两个有序链表
LeetCode-83. 删除排序链表中的重复元素
剑指 Offer 06. 从尾到头打印链表
LeetCode-876. 链表的中间结点
字符串
LeetCode-5. 最长回文子串
LeetCode-136. LRU Cache
LeetCode-344. 反转字符串
LeetCode-3. 无重复字符的最长子串
数组
LeetCode-53. 最大子序和
LeetCode-136. Single Number
Leetcode-104. maximum-depth-of-binary-tree
LeetCode-287. 寻找重复数
LeetCode-26. 删除排序数组中的重复项
LeetCode-704. 二分查找
LeetCode-287. 寻找重复数
树
LeetCode-144. 二叉树的前序遍历
LeetCode-94. 二叉树的中序遍历
剑指 Offer 28. 对称的二叉树
剑指 Offer 27. 二叉树的镜像
LeetCode-102. 二叉树的层序遍历
剑指 Offer 55 - I. 二叉树的深度
Given a linked list, determine if it has a cycle in it.
To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to. If pos is -1, then there is no cycle in the linked list.
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
方法一:
首先创建一个以节点 ID 为键的 HashMap 集合,用来存储曾经遍历过的节点。然后同样是从头节点开始,依次遍历单链表的每一个节点。每遍历到一个新节点,就用新节点和 HashMap 集合当中存储的节点作比较,如果发现 HashMap 当中存在相同节点ID,则说明链表有环,如果 HashMap 当中不存在相同的节点 ID,就把这个新节点 ID 存入 HashMap,之后进入下一节点,继续重复刚才的操作。
这个方法在流程上和方法一类似,本质的区别是使用了 HashMap 作为额外的缓存。
假设从链表头节点到入环点的距离是 D,链表的环长是 S。而每一次 HashMap 查找元素的时间复杂度是 O(1), 所以总体的时间复杂度是 1*(D+S)=D+S,可以简单理解为 O(N)。而算法的空间复杂度还是 D+S-1,可以简单地理解成 O(N)。
import java.util.*;
public class Solution {
public boolean hasCycle(ListNode head) {
Set set = new HashSet<>();
while(head != null) {
if(set.contains(head)) {
return true;
}
set.add(head);
head = head.next;
}
return false;
}
}
方法二:
首先创建两个指针1和2(在 java 里就是两个对象引用),同时指向这个链表的头节点。然后开始一个大循环,在循环体中,让指针1每次向下移动一个节点,让指针2每次向下移动两个节点,然后比较两个指针指向的节点是否相同。如果相同,则判断出链表有环,如果不同,则继续下一次循环。
例如链表 A->B->C->D->B->C->D,两个指针最初都指向节点 A,进入第一轮循环,指针1移动到了节点 B,指针2移动到了 C。第二轮循环,指针1移动到了节点 C,指针2移动到了节点 B。第三轮循环,指针1移动到了节点 D,指针2移动到了节点 D,此时两指针指向同一节点,判断出链表有环。
此方法也可以用一个更生动的例子来形容:在一个环形跑道上,两个运动员在同一地点起跑,一个运动员速度快,一个运动员速度慢。当两人跑了一段时间,速度快的运动员必然会从速度慢的运动员身后再次追上并超过,原因很简单,因为跑道是环形的。
import java.util.*;
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null) {
return false;
}
ListNode fast = head.next;
ListNode slow = head;
while(fast != slow) {
if(fast == null || fast.next == null) {
return false;
}
fast = fast.next.next;
slow = slow.next;
}
return true;
}
}
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
public ListNode removeNthFromEnd (ListNode head, int n) {
if(head == null || n < 0) {
return null;
}
ListNode temp = new ListNode(0);
temp.next = head;
ListNode fast = temp;
ListNode slow = temp;
for(int i = 0; i <= n; i ++) {
if(fast == null) {
return temp.next;
}
fast = fast.next;
}
while(fast != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return temp.next;
}
请判断一个链表是否为回文链表。
public boolean isPalindrome(ListNode head) {
StringBuilder StringBuilder = new StringBuilder();
List list = new ArrayList<>();
while(head != null) {
list.add(head.val);
StringBuilder.append(head.val);
head = head.next;
}
StringBuilder revesStringBuilder = new StringBuilder();
for(int i = list.size() - 1; i >= 0; i --) {
revesStringBuilder.append(list.get(i));
}
if(StringBuilder.toString().equals(revesStringBuilder.toString())) {
return true;
}
return false;
}
反转一个单链表。
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
public ListNode reverseList(ListNode head) {
if(head == null) {
return null;
}
ListNode preNode = null;
ListNode nextNode = null;
while(head != null) {
nextNode = head.next;
head.next = preNode;
preNode = head;
head = nextNode;
}
return preNode;
}
编写一个程序,找到两个单链表相交的起始节点。
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lengthA = getLength(headA);
int lengthB = getLength(headB);
ListNode a = headA;
ListNode b = headB;
if(lengthA > lengthB) {
for(int i = 0; i < lengthA - lengthB; i ++) {
a = a.next;
}
} else {
for(int j = 0; j < lengthB - lengthA; j ++) {
b = b.next;
}
}
while(a != b) {
a = a.next;
b = b.next;
}
return a;
}
public int getLength(ListNode head){
int length = 0;
while(head != null) {
length ++;
head = head.next;
}
return length;
}
实现一种算法,找出单向链表中倒数第 k 个节点。返回该节点的值。
注意:本题相对原题稍作改动
示例:
输入: 1->2->3->4->5 和 k = 2
输出: 4
public int kthToLast(ListNode head, int k) {
if (head == null || k < 0) {
return 0;
}
ListNode fast = head;
ListNode slow = head;
for (int i = 0; i < k; i ++) {
fast = fast.next;
}
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
return slow.val;
}
将两个升序链表合并为一个新的升序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode head = new ListNode(-1);
ListNode current = head;
while(l1 != null && l2 != null) {
if(l1.val > l2.val) {
current.next = l2;
l2 = l2.next;
} else {
current.next = l1;
l1 = l1.next;
}
current = current.next;
}
if(l1 != null) {
current.next = l1;
}
if(l2 != null) {
current.next = l2;
}
return head.next;
}
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2
输出: 1->2
示例 2:
输入: 1->1->2->3->3
输出: 1->2->3
public ListNode deleteDuplicates(ListNode head) {
if(head == null) {
return null;
}
ListNode current = head;
while(current != null && current.next != null) {
if(current.val == current.next.val) {
current.next = current.next.next;
} else {
current = current.next;
}
}
return head;
}
输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。
示例 1:
输入:head = [1,3,2]
输出:[2,3,1]
public int[] reversePrint(ListNode head) {
if(head == null) {
return new int[0];
}
Stack stack = new Stack<>();
while(head != null) {
stack.push(head.val);
head = head.next;
}
List list = new ArrayList<>();
while(!stack.empty()) {
list.add(stack.pop());
}
int [] arrays = new int[list.size()];
for(int i = 0; i < list.size(); i ++) {
arrays[i] = list.get(i);
}
return arrays;
}
给定一个头结点为 head
的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
public ListNode middleNode(ListNode head) {
if(head == null) {
return null;
}
ListNode fast = head;
ListNode slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
Given a string s, find the longest palindromic substring in s. You may assume that the maximum length of s is 1000.
给定一个字符串 s
,找到 s
中最长的回文子串。你可以假设 s
的最大长度为 1000。
事实上,只需使用恒定的空间,我们就可以在 O(n^2)O(n2) 的时间内解决这个问题。
我们观察到回文中心的两侧互为镜像。因此,回文可以从它的中心展开,并且只有 2n−1 个这样的中心。
你可能会问,为什么会是 2n−1 个,而不是 n 个中心?原因在于所含字母数为偶数的回文的中心可以处于两字母之间(例如 \textrm{“abba”}“abba” 的中心在两个 \textrm{‘b’}‘b’ 之间)。
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) return "";
int start = 0, end = 0;
for (int i = 0; i < s.length(); i++) {
int len1 = expandAroundCenter(s, i, i);
int len2 = expandAroundCenter(s, i, i + 1);
int len = Math.max(len1, len2);
if (len > end - start) {
start = i - (len - 1) / 2;
end = i + len / 2;
}
}
return s.substring(start, end + 1);
}
private int expandAroundCenter(String s, int left, int right) {
int L = left, R = right;
while (L >= 0 && R < s.length() && s.charAt(L) == s.charAt(R)) {
L--;
R++;
}
return R - L - 1;
}
}
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and put.
get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
put(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
The cache is initialized with a positive capacity.
Follow up:
Could you do both operations in O(1) time complexity?
Example:
LRUCache cache = new LRUCache( 2 /* capacity */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // returns 1
cache.put(3, 3); // evicts key 2
cache.get(2); // returns -1 (not found)
cache.put(4, 4); // evicts key 1
cache.get(1); // returns -1 (not found)
cache.get(3); // returns 3
cache.get(4); // returns 4
class LRUCache {
private LinkedHashMap map;
private final int cacheSize;
public LRUCache(int capacity) {
cacheSize = capacity;
map = new LinkedHashMap(capacity, 0.75f, true) {
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > cacheSize;
}
};
}
public int get(int key) {
return map.getOrDefault(key, -1);
}
public void put(int key, int value) {
map.put(key, value);
}
}
/**
* 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);
*/
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。
示例 1:
输入:["h","e","l","l","o"]
输出:["o","l","l","e","h"]
示例 2:
输入:["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]
public void reverseString(char[] s) {
if(s == null || s.length == 0) {
return;
}
for(int i = 0; i < s.length/2; i ++) {
char temp = s[i];
s[i] = s[s.length - i - 1];
s[s.length - i - 1] = temp;
}
}
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
示例 1:
输入: s = "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
public int lengthOfLongestSubstring(String s) {
Set set = new HashSet<>();
int length = s.length();
int rk = -1, ans = 0;
for (int i = 0; i < length; i ++) {
if (i != 0) {
set.remove(s.charAt(i - 1));
}
while (rk + 1 < length && !set.contains(s.charAt(rk + 1))) {
set.add(s.charAt(rk + 1));
rk ++;
}
ans = Math.max(ans, rk - i + 1);
}
return ans;
}
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
Input: [-2,1,-3,4,-1,2,1,-5,4],
Output: 6
Explanation: [4,-1,2,1] has the largest sum = 6.
Follow up:
If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle.
给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
示例:
输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:
如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。
代码实现:
class Solution {
public int maxSubArray(int[] nums) {
int ans = nums[0];
int sum = 0;
for(int num:nums) {
if(sum > 0) {
sum = sum + num;
} else {
sum = num;
}
ans = (sum > ans)?sum:ans;
}
return ans;
}
}
一 题目描述
Given a non-empty array of integers, every element appears twice except for one. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
现在有一个整数类型的数组,数组中素只有一个元素只出现一次,其余的元素都出现两次。
注意:
你需要给出一个线性时间复杂度的算法,你能在不使用额外内存空间的情况下解决这个问题么?
二 题目分析
2.1 异或规则
该题目可以考虑使用异或,异或规则:
A:0 XOR n = n
B:n XOR n = 0
2.2 举例说明
假设数组为 2,3,2,3,4,5,4
那么结果应该为1
2^3=0000 0010 ^ 0000 0011 = 0000 0001 1
1^2=0000 0001 ^ 0000 0010 = 0000 0011 3
3^3=0000 0011 ^ 0000 0011 = 0000 0000 0
0^4=0000 0000 ^ 0000 0100 = 0000 0100 4
4^5=0000 0100 ^ 0000 0101 = 0000 0101 1
1^4=0000 0001 ^ 0000 0100 = 0000 0101 5
整个过程如上,最后结果为5
三 题目解答
public class Solution {
public int singleNumber(int[] A) {
int result = 0;
for(int i=0; i< A.length; i++) {
result ^= A[i];
}
return result;
}
}
一 题目描述
求给定二叉树的最大深度,
最大深度是指树的根结点到最远叶子结点的最长路径上结点的数量。
Given a binary tree, find its maximum depth.
The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.
二 题目求解
2.1 递归解法
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public int maxDepth(TreeNode root) {
if(root == null) return 0;
int lDepth = maxDepth(root.left);
int rDepth = maxDepth(root.right);
return 1+(lDepth>rDepth?lDepth:rDepth);
}
}
2.2 使用 queue 进行层序遍历
public int maxDepth(TreeNode root) {
if (root == null)
return 0;
Queue queue = new LinkedList();
int res = 0;
queue.add(root);
while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
TreeNode node = queue.poll();
if (node.left != null)
queue.add(node.left);
if (node.right != null)
queue.add(node.right);
}
res++;
}
return res;
}
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。
public int findDuplicate(int[] nums) {
if(nums == null || nums.length == 0) {
return 0;
}
Set set = new HashSet<>();
for(int num : nums) {
if(set.contains(num)) {
return num;
}
set.add(num);
}
return -1;
}
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。
public int removeDuplicates(int[] nums) {
if(nums == null || nums.length == 0) {
return 0;
}
int k = 0;
int p = nums[0];
for(int i = 1; i < nums.length; i++) {
if(nums[i] != nums[k]) {
k++;
nums[k] = nums[i];
}
}
return k + 1;
}
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例 1:
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
public int search(int[] nums, int target) {
if(nums == null || nums.length < 0) {
return -1;
}
int left = 0;
int right = nums.length - 1;
while(left <= right) {
int point = (left + right)/2;
if(nums[point] == target) {
return point;
}
if(nums[point] < target) {
left = point + 1;
}
if(nums[point] > target) {
right = point - 1;
}
}
return -1;
}
给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。
示例 1:
输入:nums = [1,3,4,2,2]
输出:2
示例 2:
输入:nums = [3,1,3,4,2]
输出:3
public int findDuplicate(int[] nums) {
if(nums == null || nums.length == 0) {
return 0;
}
Set set = new HashSet<>();
for(int num : nums) {
if(set.contains(num)) {
return num;
}
set.add(num);
}
return -1;
}
给你二叉树的根节点 root
,返回它节点值的 前序 遍历。
输入:root = [1,null,2,3]
输出:[1,2,3]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
public List preorderTraversal(TreeNode root) {
List list = new ArrayList<>();
preorderTraversalVal(root, list);
return list;
}
public void preorderTraversalVal(TreeNode root, List list) {
if(root == null) {
return;
}
list.add(root.val);
preorderTraversalVal(root.left, list);
preorderTraversalVal(root.right, list);
}
给定一个二叉树的根节点 root
,返回它的 中序 遍历。
输入:root = [1,null,2,3]
输出:[1,3,2]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
public List inorderTraversal(TreeNode root) {
List list = new ArrayList<>();
inorderTraversalVal(root, list);
return list;
}
public void inorderTraversalVal(TreeNode root, List list) {
if (root == null) {
return;
}
inorderTraversalVal(root.left, list);
list.add(root.val);
inorderTraversalVal(root.right, list);
}
请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
public boolean isSymmetric(TreeNode root) {
if(root == null) {
return true;
}
return symmetric(root.left, root.right);
}
public boolean symmetric(TreeNode left, TreeNode right) {
if(left == null && right == null) {
return true;
}
if(left == null || right == null) {
return false;
}
return left.val == right.val && symmetric(left.left, right.right) && symmetric(left.right, right.left);
}
请完成一个函数,输入一个二叉树,该函数输出它的镜像。
示例 1:
输入:root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
public TreeNode mirrorTree(TreeNode root) {
if(root == null) {
return null;
}
Stack stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()) {
TreeNode node = stack.pop();
if(node.left != null) {
stack.push(node.left);
}
if(node.right != null) {
stack.push(node.right);
}
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
}
return root;
}
给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。
public List> levelOrder(TreeNode root) {
if(root == null) {
return new ArrayList<>();
}
Deque queue = new ArrayDeque<>();
queue.offer(root);
List> list = new ArrayList<>();
while(!queue.isEmpty()) {
List levelList = new ArrayList<>();
int size = queue.size();
for(int i = 0; i < size; i ++) {
TreeNode treeNode = queue.poll();
levelList.add(treeNode.val);
if(treeNode.left != null) {
queue.offer(treeNode.left);
}
if(treeNode.right != null) {
queue.offer(treeNode.right);
}
}
list.add(levelList);
}
return list;
}
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
例如:
给定二叉树 [3,9,20,null,null,15,7],
返回它的最大深度 3 。
public int maxDepth(TreeNode root) {
if(root == null) {
return 0;
}
return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
}
public int maxDepth(TreeNode root) {
if(root == null) {
return 0;
}
int result = 0;
List list = new ArrayList<>();
list.add(root);
while(!list.isEmpty()) {
List temp = new ArrayList<>();
for(TreeNode treeNode : list) {
if(treeNode.left != null) {
temp.add(treeNode.left);
}
if(treeNode.right != null) {
temp.add(treeNode.right);
}
}
list = temp;
result ++;
}
return result;
}