1.
我想到动态规划去了,但有点问题。看到大佬的递归解法很好,值得学习。
class Solution {
public int countDigitOne(int n) {
return f(n);
}
private int f(int n ) {
if(n <= 0) return 0;
String s = String.valueOf(n);//转成字符串
int high = s.charAt(0) - '0';//返回指定索引的字符,参数就是索引值
int pow = (int)Math.pow(10,s.length()-1);//Math聚合函数返回的是double类型的值,所以必须强制类型转换
int last = n-high*pow;
if(high == 1){
return f(pow-1)+f(last)+1+last;
}
return high*f(pow-1)+pow+f(last);
}
}
2.
这道题有一定难度,属于动态规划题,大佬使用的是动态规划+双指针
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Character,Integer> map = new HashMap<>();//记录每个字符上一次出现的索引值
int tmp = 0;
int res = 0;
for(int j=0;j<s.length();j++){
//i取值:如果s字符串第j位已经在map中有,重复了,i的值就是上次出现的索引值,如果没有出现过,就给i复制-1,
int i = map.containsKey(s.charAt(j)) ? map.get(s.charAt(j)) : -1;
map.put(s.charAt(j),j);
if(tmp < j-i){
tmp += 1;
}else{
tmp = j-i;
}
res = Math.max(res,tmp);
}
return res;
}
}
3.
大佬使用深度优先搜索解这道题,dfs基本都是使用递归来实现,思路如下:
这里面要弄明白为什么它要把board[i][j]的值赋值‘/’,然后又将它复原。
class Solution {
public boolean exist(char[][] board, String word) {
char[] words = word.toCharArray();//将word转化为字符数组
for(int i=0;i<board.length;i++){
for(int j=0;j<board[0].length;j++){
//其实遍历就是在board数组里找到word第一个字符
if(dfs(board,words,i,j,0)) return true;
}
}
//没有找到第一个字符
return false;
}
public boolean dfs(char[][] board,char[] words,int i,int j,int k){
if(i<0 || i>board.length-1 || j<0 || j>board[0].length-1 || board[i][j] != words[k]) return false;//判定就是如果行列超出界限或者board当前值不和word当前值相等,代表路走不下了
if(k == words.length-1) return true;//代表走完了
char tmp = board[i][j];
board[i][j] = '/';//用这个来标记,防止重复走过
boolean res = dfs(board,words,i+1,j,k+1) || dfs(board,words,i-1,j,k+1)|| dfs(board,words,i,j+1,k+1) ||dfs(board,words,i,j-1,k+1);
board[i][j] = tmp;//复原
return res;
}
}
4.
这道题我也发现其中规律,如下:
但是在编程过程中,没有大佬的巧妙,比如这里start end digit count 这些关系等等。
大佬后面的求解剩下的数所对应的的哪个数字和这个数字的哪一位,设计的思路非常巧妙
最后实现的代码如下:
class Solution {
public int findNthDigit(int n) {
if(n<=9) return n;
int dight = 1;
long start = 1;
long count = 9;
int res = 0;
while(n > count){
n -= count;
dight += 1;
start *= 10;
count = 9*start*dight;
}
long num = start+(n-1)/dight;//这个数的值
int wei = (n-1) % dight;
res = Long.toString(num).charAt(wei) - '0';
return res;
}
}
5.
思路理解:快速幂法
代码实现如下:
class Solution {
public double myPow(double x, int n) {
if(x == 0 || x == 1) return x;
if(n == 0) return 1;
long b = n;
double res = 1.0;
if(b < 0){
x = 1/x;
b = -b;
}
while(b > 0){
if(b % 2 == 1){//奇数
res *= x;
}
x *= x;
b /= 2;
}
return res;
}
}
6.
思路相比于前面的剪绳子1,这里就是大数字,在就3的a次方结果时,超出了int的范围,所以得结合上题那种快速幂的方法。
class Solution {
public int cuttingRope(int n) {
if(n <= 3) return n-1;
int b = n % 3;
int p = 1000000007;
//根据二分法计算原理,至少要保证变量 x 和 rem 可以正确存储 1000000007^21000000007 因此我们选取 long 类型。
long res = 1;
long x = 3;
for(int a = n / 3 - 1;a > 0;a /= 2){//这里a的值退掉1的原因在于配合后面的余数b,如果余数b为0,那没事就是结果继续*3,如果余数b为1,可以将退掉的3和1变成2和2,余数b为2,那就3*2
if(a % 2 == 1) res = (res * x) % p;
x = (x * x) % p;
}
if(b == 0) return (int)(res * 3 % p);
if(b == 1) return (int)(res * 4 % p);
return (int)(res * 6 % p);
}
}
7.
class Solution {
public int strToInt(String str) {
//假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
str = str.trim();//去掉前后空格
char[] c = str.toCharArray();
long rem = 0;
int i = 1;//判断除了符号位,正式的位数从何开始
if(c.length == 0) return 0;
int sign = 1;//判断符号位,有三种情况,正负和无符号
if(c[0] == '-') {
sign = -1;
}else if(c[0] != '+'){
i = 0;//无符号,从第一位还是搜索
}
for(int j=i;j<c.length;j++){
//特殊情况,这一位不是数字
if(c[j]<'0' || c[j] > '9') break;//这里break的原因是这里规定好像如果出现不是有效的整数就终止了
rem = rem * 10 + (c[j] - '0');//将数组组合起来
if(rem > Integer.MAX_VALUE){
if(sign == -1){
return Integer.MIN_VALUE;
}else{
return Integer.MAX_VALUE;
}
}
}
return sign * (int)rem;
}
}
8.
我首先思路是按照以下几点来归纳是否是数值:
1.只能是数字,不能出现其他字符(除了一些特殊的)
2.小数点出现前,不能有小数点出现过
3.e出现前,不能有e出现过,且e后面不能没有数字了
4.符号+-只能出现在首位或者e的后第一位上
所以这里大佬的思路上,他用几个boolean值的变量,来充当标识符,来判断比如前面出没出现过小数点或者e。
class Solution {
public boolean isNumber(String s) {
if(s == null || s.length() == 0) return false;
boolean num = false;//判断有无出现过数字
boolean eflag = false;//判断有无出现过e
boolean dot = false;//判断有无出现过小数点
char[] c = s.trim().toCharArray();
for(int i=0;i<c.length;i++){
if(c[i] >= '0' && c[i] <= '9'){//除了一些特殊字符,必须为数字
num = true;
}else if(c[i] == '.' ){
if(eflag || dot) return false;
//num = false;//"3." 这里为true,说明允许小数点后面没有数字
dot = true;
}else if(c[i] == '+' || c[i] == '-'){
if(i != 0 && c[i-1] != 'e'&& c[i-1] != 'E') return false;
}else if(c[i] == 'e' || c[i] == 'E'){
if(eflag || !num) return false;
eflag = true;
num = false;
}
else{
return false;
}
}
return num;
}
}
9.
我的初步思路是:使用一个arraylist,不停往其中添加,然后使用sort方法进行排序,排序后按照list的size大小判断此时是偶数还是奇数,再做相应地安排。
代码如下:这样的思路简单,但是时间复杂度太高
class MedianFinder {
ArrayList<Integer> list;
/** initialize your data structure here. */
public MedianFinder() {
list = new ArrayList<>();
}
public void addNum(int num) {
list.add(num);
}
public double findMedian() {
Collections.sort(list);
int size = list.size();
double median = 0;
if(size % 2 == 1){
int index = size / 2;
//index += 1;
median = list.get(index);
}else{
int index = size / 2 ;
median = (list.get(index-1) * 1.0+list.get(index)) / 2;
}
return median;
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
大佬设计了一个思路:使用两个堆,一个最小堆用于存放这些数列里大的一半数字,一个最大堆用于存放这些数列里小的一半数字。
就是排序工作基本就在add方法里完成了。最小堆(大小为m),最大堆(大小为n),当m=n时,添加元素先往B里添加,然后将其弹出,再插入A。当m不等于n时,先往A里添加再弹出,插入B。所以A里的元素个数永远是大于或者等于B的。所以在查找中位数时,如果m=n时,那就返回A,B堆顶元素的和的一半,如果m不等于n时,即m比n大1,返回A堆顶的元素。
这里堆的实现,用优先队列,默认就是最小堆,想使用最大堆,使用这样的语句 new PriorityQueue<Integer>((x,y)->(y-x))
class MedianFinder {
Queue<Integer> A,B;
/** initialize your data structure here. */
public MedianFinder() {
A = new PriorityQueue<Integer>(); //优先队列可以直接实现最小堆
B = new PriorityQueue<Integer>((x,y)->(y-x)); //这样子的lamada表达式可以直接实现最大堆
}
public void addNum(int num) {
int m = A.size();
int n = B.size();
if(m == n){//两个相等,就是往A里添加元素。先往B里添加再弹出B的栈顶元素给A。
//思考一下为什么要这么添加?
B.add(num);
A.add(B.poll());
}else{
A.add(num);
B.add(A.poll());
}
}
public double findMedian() {
int m = A.size();
int n = B.size();
if(m == n){
return (A.peek()+B.peek()) / 2.0;
}else{
return A.peek();
}
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
10.
这是两个函数,一个是序列化,一个是反序列化
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec {
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if(root == null) return "[]";//当二叉树根节点都为Null,就直接返回一个空
StringBuilder res = new StringBuilder("[");//序列化之后的序列
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
//层次遍历
while(!queue.isEmpty()){
TreeNode node = queue.poll();
if(node != null){//非空
res.append(node.val+",");
//我们直接要判定它是否存在左右节点,但这里因为Null也会被写入到输出结果中,所以就不需要去判定
queue.add(node.left);
queue.add(node.right);
}else{
res.append("null,");
}
}
//将元素添加完毕后,别忘了右括号和删除最后一次添加的逗号
res.deleteCharAt(res.length() - 1);//删除逗号
res.append("]");
return res.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data.equals("[]")) return null;
data = data.substring(1,data.length()-1);//去括号
String[] val = data.split(",");//分割
TreeNode root = new TreeNode(Integer.parseInt(val[0]));
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
int i = 1;//指向指针
while(!queue.isEmpty()){
TreeNode node = queue.poll();
//左节点
if(!val[i].equals("null")){//判定是否为空值,空节点
node.left = new TreeNode(Integer.parseInt(val[i]));
queue.add(node.left);
}
i++;
//右节点
if(!val[i].equals("null")){//判定是否为空值,空节点
node.right = new TreeNode(Integer.parseInt(val[i]));
queue.add(node.right);
}
i++;
}
return root;
}
}
// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));
这里用到了两个我不熟悉的方法,一个是stringbuilder的deleteCharAt(参数a):删除索引a的值,另一个是parseInt方法,parseInt(String s): 返回用十进制参数表示的整数值。就是将参数string类型的转化为Integer类型。