需要开通vip的题目暂时跳过
点击链接可跳转到所有刷题笔记的导航链接
给定一个仅包含数字 0-9
的字符串和一个目标值,在数字之间添加二元运算符(不是一元)+
、-
或 *
,返回所有能够得到目标值的表达式。
public ArrayList<String> answer;
public String digits;
public long target;
public void backTrack(int index, long currentVal,long preVal,long value,List<String> tmp){
if(index == digits.length()){
if(value == target && currentVal == 0){
StringBuilder stringBuilder = new StringBuilder();
for(String str:tmp){
stringBuilder.append(str);
}
answer.add(stringBuilder.toString().substring(1,stringBuilder.length()));
}
return;
}
currentVal = currentVal * 10 + Character.getNumericValue(digits.charAt(index));
String current_str = Long.toString(currentVal);
if(currentVal > 0){
backTrack(index+1,currentVal,preVal,value,tmp);
}
tmp.add("+");
tmp.add(current_str);
backTrack(index+1,0,currentVal,value+currentVal,tmp);
tmp.remove(tmp.size()-1);
tmp.remove(tmp.size()-1);
if(tmp.size()>0){
tmp.add("-");
tmp.add(current_str);
backTrack(index+1,0,-currentVal,value-currentVal,tmp);
tmp.remove(tmp.size()-1);
tmp.remove(tmp.size()-1);
tmp.add("*");
tmp.add(current_str);
backTrack(index+1,0,currentVal * preVal,value - preVal + (currentVal * preVal),tmp);
tmp.remove(tmp.size()-1);
tmp.remove(tmp.size()-1);
}
}
public List<String> addOperators(String num, int target) {
if(num.length() == 0)return new ArrayList<>();
this.digits = num;
this.answer = new ArrayList<>();
this.target = target;
backTrack(0,0,0,0,new ArrayList<>());
return this.answer;
}
分析
回溯法
递归出口,当前位置index等于字符串长度时,若结果等于target并且 当前参与运算的数字是0,则将找到的结果拼接成字符串加入到答案集合中。
若不满足 则返回上一层递归。回溯
首先是找数字,因为数字并不是只有一位的,所以需要找到计算的数字currentVal。然后递归
backTrack(index + 1, currentVal, preVal, value, tmp);
添加+号,以及数字。然后递归将value+上当前数字。并记录下这一次的操作值。preVal = currentVal传入下一层递归。
backTrack(index + 1, 0, currentVal, value + currentVal, tmp);
当tmp.size()>0时 可走下面两种情况
添加-号,以及数字。然后递归,将value-当前数字。并记录下这一次的操作值。 传入下一层递归backTrack(index+1,0,-currentVal,value-currentVal,tmp);
添加*号,以及数字。然后递归,注意因为遇到了乘法,所以之前的运算需要重置。例如2-3 * 4 还未进行到 * 4的时候,结果为-1.实际是乘法先计算。
所以之前会先记录下-3这一步操作值。将当前结果先还原,然后乘法优先计算得到结果 -1 - (-3) + (-3*4) = -10
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
public void moveZeroes(int[] nums) {
int index = 0;
for(int i = 0;i<nums.length;i++){
if(nums[i] == 0){
index++;
}else{
nums[i - index] = nums[i];
}
}
for(int j = nums.length-index;j<nums.length;j++){
nums[j] = 0;
}
}
分析
提交结果
给定一个迭代器类的接口,接口包含两个方法: next() 和 hasNext()。设计并实现一个支持 peek() 操作的顶端迭代器 – 其本质就是把原本应由 next() 方法返回的元素 peek() 出来。
class PeekingIterator implements Iterator<Integer> {
private LinkedList<Integer> queue;
public PeekingIterator(Iterator<Integer> iterator) {
// initialize any member here.
queue = new LinkedList<>();
while(iterator.hasNext()){
queue.add(iterator.next());
}
}
// Returns the next element in the iteration without advancing the iterator.
public Integer peek() {
return queue.peek();
}
// hasNext() and next() should behave the same as in the Iterator interface.
// Override them if needed.
@Override
public Integer next() {
return queue.removeFirst();
}
@Override
public boolean hasNext() {
return !queue.isEmpty();
}
}
分析
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
public int findDuplicate(int[] nums) {
int fast = 0;
int slow = 0;
while(true){
fast = nums[nums[fast]];
slow = nums[slow];
if(fast == slow){
slow = 0;
while(slow!=fast){
slow = nums[slow];
fast = nums[fast];
}
return slow;
}
}
}
分析
这题的寻找重复的数字,其实就是寻找一个链表中环路的入口,类似141、142题。
例如数组[3,1,3,4,2]可以可以看成如下
第一行表示索引,第二行是指,指向下一个索引。可以发现图中的环路3->-4->2->3 .所以这题就是要找到环路的入口3.
和之前141、142题目的步骤类似。使用快慢指针,快指针走两步 慢指针走一步。
即fast = nums[nums[fast]]; slow = nums[slow];
当两个指针相遇的时候。如图所示
假设起点到入口的距离为k。入口到相遇点距离为x,相遇点到入口为y。所以当他们相遇的时候慢指针走了k+x,快指针走了k+y+2x
因为两者是两倍关系,列等式可以得出y=k。
所以在两者相遇的时候,将慢指针放到原点。此时两个指针同步的前进,当再一次相遇的时候就是在入口的地方。
根据 百度百科 ,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机。
给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态:1 即为活细胞(live),或 0 即为死细胞(dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:
如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
如果死细胞周围正好有三个活细胞,则该位置死细胞复活;
根据当前状态,写一个函数来计算面板上所有细胞的下一个(一次更新后的)状态。下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。
public void gameOfLife(int[][] board) {
List<int[]> oneToZero = new ArrayList<>();//用于存储1变成0的位置
List<int[]> zeroToOne = new ArrayList<>();//用于存储0变成1的位置
//周围8个位置
int[] row = {
-1,-1,-1,0,0,1,1,1};
int[] column = {
-1,0,1,-1,1,-1,0,1};
for(int i = 0;i < board.length;i++){
for(int j = 0;j<board[0].length;j++){
int aroundOne = 0;
int aroundZero = 0;
int current = board[i][j];//当前位置
for(int k = 0;k<8;k++){
//遍历周围8个位置
int rowindex = i + row[k];
int columnindex = j +column[k];
// 统计周围的数量
if(rowindex >= 0 && rowindex < board.length && columnindex >=0 && columnindex < board[0].length){
if(board[rowindex][columnindex] == 1){
aroundOne++;
if(aroundOne > 3)break;
}
}
}
// 满足规则1和3 加入到队列中等待变更
if(current == 1 && (aroundOne < 2 || aroundOne >3)){
int[] point = new int[]{
i,j};
oneToZero.add(point);
}
// 满足规则4 加入到队列中 等待变更
else if(current == 0 && aroundOne == 3){
int[] point = new int[]{
i,j};
zeroToOne.add(point);
}
}
}
// 变换数字
for(int i = 0;i<oneToZero.size();i++){
int[] point = oneToZero.get(i);
board[point[0]][point[1]] = 0;
}
for(int i = 0;i<zeroToOne.size();i++){
int[] point = zeroToOne.get(i);
board[point[0]][point[1]] = 1;
}
}
给定一种规律 pattern 和一个字符串 str ,判断 str 是否遵循相同的规律。
这里的 遵循 指完全匹配,例如, pattern 里的每个字母和字符串 str 中的每个非空单词之间存在着双向连接的对应规律。
public boolean wordPattern(String pattern, String str) {
HashMap<String,String> map = new HashMap<>();
HashMap<String,String> map2 = new HashMap<>();
String[] strs = str.split(" ");
if(pattern.length() != strs.length)return false;
for(int i = 0;i<pattern.length();i++){
String p = String.valueOf(pattern.charAt(i));
if(map.containsKey(p) && !map.get(p).equals(strs[i]) || map2.containsKey(strs[i]) && !map2.get(strs[i]).equals(p))
return false;
else {
map.put(p,strs[i]);
map2.put(strs[i],p);
}
}
return true;
}
//方法二
public boolean wordPattern(String pattern, String str) {
HashMap<String,String> map = new HashMap<>();
String[] strs = str.split(" ");
if(pattern.length() != strs.length)return false;
for(int i = 0;i<pattern.length();i++){
String p = String.valueOf(pattern.charAt(i));
if(map.containsKey(p)){
if(!map.get(p).equals(strs[i]))return false;
}else{
if(map.containsValue(strs[i]))return false;
else map.put(p,strs[i]);
}
}
return true;
}
分析
提交结果
你和你的朋友,两个人一起玩 Nim 游戏:桌子上有一堆石头,每次你们轮流拿掉 1 - 3 块石头。 拿掉最后一块石头的人就是获胜者。你作为先手。
你们是聪明人,每一步都是最优解。 编写一个函数,来判断你是否可以在给定石头数量的情况下赢得游戏。
public boolean canWinNim(int n) {
if(n < 3)return true;
if(n % 4 == 0)return false;
return true;
}
中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5
设计一个支持以下两种操作的数据结构:
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。
解答
方法一
class MedianFinder {
ArrayList<Integer> list;
/** initialize your data structure here. */
public MedianFinder() {
list = new ArrayList<>();
}
public void addNum(int num) {
int left = 0;
int right = list.size()-1;
while(left<=right){
int mid = (left+ right)/2;
if(list.get(mid) < num)
left = mid + 1;
else if(list.get(mid) > num)
right = mid - 1;
else {
list.add(mid,num);return;}
}
list.add(left,num);
}
public double findMedian() {
int len = list.size();
if(len % 2 == 0){
return (Double.valueOf(list.get(len/2)) + Double.valueOf(list.get(len/2-1)))/2;
}else{
return Double.valueOf(list.get(len/2));
}
}
}
//方法二
class MedianFinder {
PriorityQueue<Integer> maxQueue;
PriorityQueue<Integer> minQueue;
public MedianFinder() {
maxQueue = new PriorityQueue<>((o1, o2) -> o2 - o1);
minQueue = new PriorityQueue<>();
}
public void addNum(int num) {
maxQueue.add(num);
minQueue.add(maxQueue.remove());
if(minQueue.size() > maxQueue.size())
maxQueue.add(minQueue.remove());
}
public double findMedian() {
if (maxQueue.size() == minQueue.size()) return (double)(maxQueue.peek() + minQueue.peek()) / 2;
else return maxQueue.peek();
}
}
分析
序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。
请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。
提示: 这与 LeetCode 目前使用的方式一致,详情请参阅 LeetCode 序列化二叉树的格式。你并非必须采取这种方式,你也可以采用其他的方法解决这个问题。
说明: 不要使用类的成员 / 全局 / 静态变量来存储状态,你的序列化和反序列化算法应该是无状态的。
public String rserialize(TreeNode root, StringBuilder stringBuilder) {
if (root == null) {
stringBuilder.append("None,");
} else {
stringBuilder.append(root.val).append(",");
stringBuilder.append(rserialize(root.left, new StringBuilder()));
stringBuilder.append(rserialize(root.right, new StringBuilder()));
}
return stringBuilder.toString();
}
public String serialize(TreeNode root) {
return rserialize(root, new StringBuilder());
}
public TreeNode rdeserialize(List<String> l) {
if (l.get(0).equals("None")) {
l.remove(0);
return null;
}
TreeNode root = new TreeNode(Integer.valueOf(l.get(0)));
l.remove(0);
root.left = rdeserialize(l);
root.right = rdeserialize(l);
return root;
}
public TreeNode deserialize(String data) {
String[] data_array = data.split(",");
List<String> data_list = new LinkedList<String>(Arrays.asList(data_array));
return rdeserialize(data_list);
}
你在和朋友一起玩 猜数字(Bulls and Cows)游戏,该游戏规则如下:
你写出一个秘密数字,并请朋友猜这个数字是多少。
朋友每猜测一次,你就会给他一个提示,告诉他的猜测数字中有多少位属于数字和确切位置都猜对了(称为“Bulls”, 公牛),有多少位属于数字猜对了但是位置不对(称为“Cows”, 奶牛)。
朋友根据提示继续猜,直到猜出秘密数字。
请写出一个根据秘密数字和朋友的猜测数返回提示的函数,返回字符串的格式为 xAyB ,x 和 y 都是数字,A 表示公牛,用 B 表示奶牛。
xA 表示有 x 位数字出现在秘密数字中,且位置都与秘密数字一致。
yB 表示有 y 位数字出现在秘密数字中,但位置与秘密数字不一致。
请注意秘密数字和朋友的猜测数都可能含有重复数字,每位数字只能统计一次。
public String getHint(String secret, String guess) {
StringBuilder stringBuilder = new StringBuilder();
int A = 0;
int B = 0;
int[] visited = new int[secret.length()];
int[] numbers = new int[10];
for(int i = 0;i<secret.length();i++){
if(secret.charAt(i) == guess.charAt(i)){
A++;
visited[i] = 1;
}else numbers[secret.charAt(i) - '0']++;
}
for(int i = 0;i<guess.length();i++){
char ch = guess.charAt(i);
if(numbers[ch-'0'] > 0 && visited[i] != 1){
B++;
numbers[ch-'0']--;
}
}
return stringBuilder.append(A).append("A").append(B).append("B").toString();
}
分析
给定一个无序的整数数组,找到其中最长上升子序列的长度。
//方法一
int res = 1;
int temp = 0;
public int lengthOfLIS(int[] nums) {
if (nums.length == 0) return 0;
for (int i = 0; i < nums.length - 1; i++) {
temp = nums[i];
findmaxLength(nums, i + 1, 1);
}
return res;
}
public void findmaxLength(int[] nums, int index, int number) {
for (int i = index; i < nums.length; i++) {
if (nums.length - i + number < res) break;
if (nums[i] < temp) {
continue;
}
if (nums[i] > temp) {
res = Math.max(res, number + 1);
int a = temp;
temp = nums[i];
findmaxLength(nums, i + 1, number + 1);
temp = a;
}
}
}
//方法二
public int lengthOfLIS(int[] nums) {
if(nums.length ==0)return 0;
int[] dp = new int[nums.length];
dp[0] = 1;
for (int i = 1; i < nums.length; i++) {
dp[i] = 1;
for (int j = 0; j < i; j++) {
if (nums[i] > nums[j]) dp[i] = Math.max(dp[j] + 1, dp[i]);
}
}
int res = 0;
for (int i = 0; i < dp.length; i++) {
res = Math.max(res, dp[i]);
}
return res;
}
//方法三
public int lengthOfLIS(int[] nums) {
int len = nums.length;
if (len <= 1) {
return len;
}
int[] tail = new int[len];//用于记录最长的上升子序列
tail[0] = nums[0];
int end = 0;
for (int i = 1; i < len; i++) {
if (nums[i] > tail[end]) {
//若当前的值大于序列的最后一位,则直接加入,长度+1
end++;
tail[end] = nums[i];
} else {
//否则从中tail中找到第一个比nums[i]大的数字,替换它。
int left = 0;
int right = end;
while (left < right) {
//二分查找
int mid = left + ((right - left) >>> 1);
if (tail[mid] < nums[i]) {
left = mid + 1;
} else {
right = mid;
}
}
tail[left] = nums[i];
}
}
end++;
return end;
}
分析
提交结果