需要开通vip的题目暂时跳过
点击链接可跳转到所有刷题笔记的导航链接
给定二叉搜索树(BST)的根节点和要插入树中的值,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。
解答
public TreeNode insertIntoBST(TreeNode root, int val) {
if(root == null)return new TreeNode(val);
if(root.val < val){
root.right = insertIntoBST(root.right,val);
}else{
root.left = insertIntoBST(root.left,val);
}
return root;
}
分析
设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。
请实现 KthLargest 类:
解答
PriorityQueue<Integer> queue = new PriorityQueue<>();
int k;
public KthLargest(int k, int[] nums) {
this.k = k;
Arrays.sort(nums);
for (int i = nums.length - 1; i >= 0 && k != 0; i--) {
queue.add(nums[i]);
k--;
}
}
public int add(int val) {
if(queue.size() < k){
queue.add(val);
return queue.peek();
}
int curMax = queue.poll();
if (curMax > val)
queue.add(curMax);
else queue.add(val);
return queue.peek();
}
分析
不使用任何内建的哈希表库设计一个哈希集合
具体地说,你的设计应该包含以下的功能
class MyHashSet {
boolean[] nums;
/** Initialize your data structure here. */
public MyHashSet() {
nums = new boolean[1000001];
}
public void add(int key) {
nums[key] = true;
}
public void remove(int key) {
nums[key] = false;
}
/** Returns true if this set contains the specified element */
public boolean contains(int key) {
return nums[key];
}
}
不使用任何内建的哈希表库设计一个哈希映射
具体地说,你的设计应该包含以下的功能
解答
class MyHashMap {
Bucket[] buckets = new Bucket[739];
/** Initialize your data structure here. */
public MyHashMap() {
}
/** value will always be non-negative. */
public void put(int key, int value) {
int index = key % 739;
if(buckets[index] == null){
buckets[index] = new Bucket(key,value);
}else{
Bucket bucket = buckets[index];
while(bucket.key != key && bucket.next!=null){
bucket = bucket.next;
}
if(bucket.key == key){
bucket.val = value;
}else{
bucket.next = new Bucket(key,value);
}
}
}
/** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
public int get(int key) {
int index = key % 739;
if(buckets[index] == null)return -1;
Bucket bucket = buckets[index];
while(bucket != null && bucket.key != key){
bucket = bucket.next;
}
if(bucket != null){
return bucket.val;
}
return -1;
}
/** Removes the mapping of the specified value key if this map contains a mapping for the key */
public void remove(int key) {
int index = key % 739;
if(buckets[index] == null)return;
Bucket bucket = buckets[index];
if(bucket.next == null && bucket.key == key){
buckets[index] = null;
return;
}
Bucket pre = null;
while(bucket != null && bucket.key != key){
pre = bucket;
bucket = bucket.next;
}
if(bucket == null)return;
if(pre != null){
pre.next = bucket.next;
bucket.next = null;
}else{
Bucket first = buckets[index];
buckets[index] = first.next;
first.next = null;
}
}
}
class Bucket{
int key;
int val;
Bucket next;
public Bucket(int key,int val){
this.key = key;
this.val = val;
}
}
分析
设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:val 和 next。val 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。
在链表类中实现这些功能:
解答
public class ListNode {
int val;
ListNode next;
ListNode prev;
ListNode(int x) { val = x; }
}
class MyLinkedList {
int size;
// sentinel nodes as pseudo-head and pseudo-tail
ListNode head, tail;
public MyLinkedList() {
size = 0;
head = new ListNode(0);
tail = new ListNode(0);
head.next = tail;
tail.prev = head;
}
/** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
public int get(int index) {
// if index is invalid
if (index < 0 || index >= size) return -1;
// choose the fastest way: to move from the head
// or to move from the tail
ListNode curr = head;
if (index + 1 < size - index)
for(int i = 0; i < index + 1; ++i) curr = curr.next;
else {
curr = tail;
for(int i = 0; i < size - index; ++i) curr = curr.prev;
}
return curr.val;
}
/** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
public void addAtHead(int val) {
ListNode pred = head, succ = head.next;
++size;
ListNode toAdd = new ListNode(val);
toAdd.prev = pred;
toAdd.next = succ;
pred.next = toAdd;
succ.prev = toAdd;
}
/** Append a node of value val to the last element of the linked list. */
public void addAtTail(int val) {
ListNode succ = tail, pred = tail.prev;
++size;
ListNode toAdd = new ListNode(val);
toAdd.prev = pred;
toAdd.next = succ;
pred.next = toAdd;
succ.prev = toAdd;
}
/** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
public void addAtIndex(int index, int val) {
// If index is greater than the length,
// the node will not be inserted.
if (index > size) return;
// [so weird] If index is negative,
// the node will be inserted at the head of the list.
if (index < 0) index = 0;
// find predecessor and successor of the node to be added
ListNode pred, succ;
if (index < size - index) {
pred = head;
for(int i = 0; i < index; ++i) pred = pred.next;
succ = pred.next;
}
else {
succ = tail;
for (int i = 0; i < size - index; ++i) succ = succ.prev;
pred = succ.prev;
}
// insertion itself
++size;
ListNode toAdd = new ListNode(val);
toAdd.prev = pred;
toAdd.next = succ;
pred.next = toAdd;
succ.prev = toAdd;
}
/** Delete the index-th node in the linked list, if the index is valid. */
public void deleteAtIndex(int index) {
// if the index is invalid, do nothing
if (index < 0 || index >= size) return;
// find predecessor and successor of the node to be deleted
ListNode pred, succ;
if (index < size - index) {
pred = head;
for(int i = 0; i < index; ++i) pred = pred.next;
succ = pred.next.next;
}
else {
succ = tail;
for (int i = 0; i < size - index - 1; ++i) succ = succ.prev;
pred = succ.prev.prev;
}
// delete pred.next
--size;
pred.next = succ;
succ.prev = pred;
}
}
分析
实现函数 ToLowerCase(),该函数接收一个字符串参数 str,并将该字符串中的大写字母转换成小写字母,之后返回新的字符串。
解答
public String toLowerCase(String str) {
char[] chars = str.toCharArray();
for(int i = 0;i < chars.length;i++){
if(chars[i] >= 'A' && chars[i] <= 'Z'){
chars[i] = (char)(chars[i] - 'A' + 'a');
}
}
return new String(chars);
}
分析
给定一个包含 [0,n ) 中独特的整数的黑名单 B,写一个函数从 [ 0,n ) 中返回一个不在 B 中的随机整数。
对它进行优化使其尽量少调用系统方法 Math.random() 。
提示:
解答
Map<Integer, Integer> m;
Random r;
int wlen;
public Solution(int n, int[] b) {
m = new HashMap<>();
r = new Random();
wlen = n - b.length;
Set<Integer> w = new HashSet<>();
for (int i = wlen; i < n; i++) w.add(i);//wlen范围外的名单
for (int x : b) w.remove(x);//移除黑名单,剩下wlen范围外白名单
Iterator<Integer> wi = w.iterator();迭代白名单
for (int x : b)//遍历黑名单
if (x < wlen)//在wlen范围内,映射到wlen范围外的白名单
m.put(x, wi.next());
}
public int pick() {
int k = r.nextInt(wlen);
return m.getOrDefault(k, k);
}
分析
给定两个字符串s1, s2,找到使两个字符串相等所需删除字符的ASCII值的最小和。
解答
public int minimumDeleteSum(String s1, String s2) {
int[][] dp = new int[s1.length() + 1][s2.length() + 1];
//初始化
for (int i = s1.length() - 1; i >= 0; i--) {
dp[i][s2.length()] = dp[i+1][s2.length()] + s1.codePointAt(i);
}
for (int j = s2.length() - 1; j >= 0; j--) {
dp[s1.length()][j] = dp[s1.length()][j+1] + s2.codePointAt(j);
}
for (int i = s1.length() - 1; i >= 0; i--) {
for (int j = s2.length() - 1; j >= 0; j--) {
if (s1.charAt(i) == s2.charAt(j)) {
dp[i][j] = dp[i+1][j+1];
} else {
dp[i][j] = Math.min(dp[i+1][j] + s1.codePointAt(i),
dp[i][j+1] + s2.codePointAt(j));
}
}
}
return dp[0][0];
}
分析
给定一个正整数数组 nums。
找出该数组内乘积小于 k 的连续的子数组的个数。
解答
public int numSubarrayProductLessThanK(int[] nums, int k) {
if (k <= 1) return 0;
int prod = 1, ans = 0, left = 0;
for (int right = 0; right < nums.length; right++) {
prod *= nums[right];
while (prod >= k) prod /= nums[left++];
ans += right - left + 1;
}
return ans;
}
分析
给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每笔交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。
返回获得利润的最大值。
注意:这里的一笔交易指买入持有并卖出股票的整个过程,每笔交易你只需要为支付一次手续费。
解答
//方法1
public int maxProfit(int[] prices, int fee) {
int len = prices.length;
int[][] dp = new int[len][2];
dp[0][0] = 0;
dp[0][1] = -prices[0];
for(int i = 1;i < len;i++){
for(int j = 0;j < i;j++){
dp[i][0] = Math.max(dp[j][0],dp[j][1] + prices[i] - fee);
dp[i][1] = Math.max(dp[j][1],dp[j][0] - prices[i]);
}
}
return dp[len-1][0];
}
//方法2
public int maxProfit(int[] prices, int fee) {
int len = prices.length;
int[][] dp = new int[len][2];
dp[0][0] = 0;
dp[0][1] = -prices[0];
for(int i = 1;i < len;i++){
dp[i][0] = Math.max(dp[i-1][0],dp[i-1][1] + prices[i] - fee);
dp[i][1] = Math.max(dp[i-1][1],dp[i-1][0] - prices[i]);
}
return dp[len-1][0];
}
分析
Range 模块是跟踪数字范围的模块。你的任务是以一种有效的方式设计和实现以下接口。
解答
class RangeModule {
TreeSet<Interval> ranges;
public RangeModule() {
ranges = new TreeSet();
}
public void addRange(int left, int right) {
Iterator<Interval> itr = ranges.tailSet(new Interval(0, left - 1)).iterator();
while (itr.hasNext()) {
Interval iv = itr.next();
if (right < iv.left) break;
left = Math.min(left, iv.left);
right = Math.max(right, iv.right);
itr.remove();
}
ranges.add(new Interval(left, right));
}
public boolean queryRange(int left, int right) {
Interval iv = ranges.higher(new Interval(0, left));
return (iv != null && iv.left <= left && right <= iv.right);
}
public void removeRange(int left, int right) {
Iterator<Interval> itr = ranges.tailSet(new Interval(0, left)).iterator();
ArrayList<Interval> todo = new ArrayList();
while (itr.hasNext()) {
Interval iv = itr.next();
if (right < iv.left) break;
if (iv.left < left) todo.add(new Interval(iv.left, left));
if (right < iv.right) todo.add(new Interval(right, iv.right));
itr.remove();
}
for (Interval iv: todo) ranges.add(iv);
}
}
class Interval implements Comparable<Interval>{
int left;
int right;
public Interval(int left, int right){
this.left = left;
this.right = right;
}
public int compareTo(Interval that){
if (this.right == that.right) return this.left - that.left;
return this.right - that.right;
}
}
分析
有两种特殊字符。第一种字符可以用一比特0来表示。第二种字符可以用两比特(10 或 11)来表示。
现给一个由若干比特组成的字符串。问最后一个字符是否必定为一个一比特字符。给定的字符串总是由0结束。
解答
public boolean isOneBitCharacter(int[] bits) {
for(int i = 0;i < bits.length;){
if(i == bits.length - 1 && bits[i] == 0)return true;
if(bits[i] == 1)i += 2;
else i++;
}
return false;
}
分析
给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。
解答
//方法1 暴力
public int findLength(int[] A, int[] B) {
int max = 0;
int temp = 0;
for(int i = 0;i < A.length;i++){
for(int j = 0;j < B.length;j++){
int a = i,b = j;
while(a < A.length && b < B.length){
if(A[a] == B[b]){
temp++;
a++;
b++;
}else {
break;
}
}
max = Math.max(temp,max);
temp = 0;
}
}
max = Math.max(temp,max);
return max;
}
//方法2 dp
public int findLength(int[] A, int[] B) {
int lenA = A.length,lenB = B.length;
int[][] dp = new int[lenA + 1][lenB + 1];
int max = 0;
for(int i = 0;i < lenA;i++){
for(int j = 0;j < lenB;j++){
if(A[i] == B[j]){
dp[i + 1][j + 1] = dp[i][j] + 1;
max = Math.max(dp[i + 1][j + 1],max);
}
}
}
return max;
}
//方法3
public int findLength(int[] A, int[] B) {
if (A.length < B.length)
return findLengthHelper(A, B);
return findLengthHelper(B, A);
}
public int findLengthHelper(int[] A, int[] B) {
int aLength = A.length, bLength = B.length;
//total是总共运行的次数
int total = aLength + bLength - 1;
int max = 0;
for (int i = 0; i < total; i++) {
//下面一大坨主要判断数组A和数组B比较的起始位置和比较的长度
int aStart = 0;
int bStart = 0;
int len = 0;
if (i < aLength) {
aStart = aLength - i - 1;
bStart = 0;
len = i + 1;
} else {
aStart = 0;
bStart = i - aLength + 1;
len = Math.min(bLength - bStart, aLength);
}
if(len > max){
int maxlen = maxLength(A, B, aStart, bStart, len);
max = Math.max(max, maxlen);
}
}
return max;
}
public int maxLength(int[] A, int[] B, int aStart, int bStart, int len) {
int max = 0, count = 0;
for (int i = 0; i < len; i++) {
if (A[aStart + i] == B[bStart + i]) {
count++;
max = Math.max(max, count);
} else {
count = 0;
}
}
return max;
}
分析
提交结果
给定一个整数数组,返回所有数对之间的第 k 个最小距离。一对 (A, B) 的距离被定义为 A 和 B 之间的绝对差值。
解答
public int smallestDistancePair(int[] nums, int k) {
Arrays.sort(nums);
int max = nums[nums.length - 1] - nums[0];
int low = 0;
int high = max;
while(low < high){
int mid = low + ((high - low) >> 1);
int left = 0;
int count = 0;
for(int right = 0;right < nums.length;right++){
while(nums[right] - nums[left] > mid)left++;
count += right - left;
}
if(count >= k){
high = mid;
}else{
low = mid + 1;
}
}
return low;
}
分析
给出一个字符串数组words组成的一本英语词典。从中找出最长的一个单词,该单词是由words词典中其他单词逐步添加一个字母组成。若其中有多个可行的答案,则返回答案中字典序最小的单词。
解答
//方法1
class Solution {
public String longestWord(String[] words) {
Arrays.sort(words,new Comparator<String>(){
public int compare(String s1,String s2){
if(s1.length() != s2.length()){
return s1.length() - s2.length();
}
for(int i = 0;i < s1.length();i++){
if(s1.charAt(i) > s2.charAt(i))
return 1;
else if(s1.charAt(i) < s2.charAt(i))
return -1;
}
return 0;
}
});
TrieTree tree = new TrieTree();
int max = 0;
String res = "";
for(String word : words){
TrieTree t = tree;
for(int i = 0;i < word.length();i++){
if(word.length() == 1){
if(t.children[word.charAt(i) - 'a'] == null){
t.children[word.charAt(i) - 'a'] = new TrimTree();
}
if(max == 0){
max = 1;
res = word;
}
}else{
if(i != word.length() - 1){
if(t.children[word.charAt(i) - 'a'] == null)break;
t = t.children[word.charAt(i) - 'a'];
}else{
if(t.children[word.charAt(i) - 'a'] == null){
t.children[word.charAt(i) - 'a'] = new TrieTree();
}
if(word.length() > max){
max = word.length();
res = word;
}
}
}
}
}
return res;
}
}
class TrieTree{
TrieTree[] children;
public TrieTree(){
children = new TrieTree[26];
}
}
分析