1、旋转数组的最小数字
class Solution {
public int minArray(int[] numbers) {
if(numbers.length==1){
return numbers[0];
}
int left=0;
int right=numbers.length-1;
while(left<right){
int mid=(left+right)/2;
if(numbers[mid]<numbers[right]){
right=mid;
}else if(numbers[mid]>numbers[right]){
left=mid+1;
}else{
right--;
}
}
return numbers[left];
}
}
2、剪绳子
class Solution {
public int cuttingRope(int n) {
if(n<2){
return 0;
}
int[] dp=new int[n+1];
dp[1]=1;
for(int i=2;i<=n;i++){
for(int j=1;j<i;j++){
dp[i] = Math.max(dp[i], (Math.max(j, dp[j])) * (Math.max(i - j, dp[i - j])));
}
}
return dp[n];
}
}
3、剪绳子2
class Solution {
public int cuttingRope(int n) {
if(n <= 3) return n - 1;
long res=1L;
int p=1000000007;
//贪心算法,优先切三,其次切二
while(n>4){
res=res*3%p;
n-=3;
}
//出来循环只有三种情况,分别是n=2、3、4
return (int)(res*n%p);
}
}
4、二进制中1的个数
方法一:
5、数值的整除次方
class Solution {
public double myPow(double x, int n) {
//x为0,直接返回0
if(x == 0) return 0;
if(n>=0){
return power(x,n);
}else{
//n<0时,x^n即求(1/x)^(-n)
return power(1/x,-n);
}
}
//递归方式的快速幂
public double power(double x,int n){
if(n==0){
return 1;
}
double tem=power(x,n/2);
//n&1判断n最低为是否为1,为1则n为奇数,为0则n为偶数(其实就是n%2)
//指数n为奇数,则需要多乘一个底数。因为上行代码计算n/2时,若n为奇数,精度丢失向下取整(相当于少乘一个x)
if((n&1)==1){
return tem*tem*x;
}else{
return tem*tem;
}
}
}
6、链表中倒数第k个节点
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
//使用双指针,前指针先走k步,然后两个指针一个走,这样两个指针的间距一直为k
public ListNode getKthFromEnd(ListNode head, int k) {
if(head==null){
return head;
}
if(head.next==null){
return head;
}
ListNode low=head;
ListNode pre=low;
ListNode fast=head;
for(int i=0;i<k-1;i++){
if(fast==null){
return null;
}
fast=fast.next;
}
while(fast.next!=null){
fast=fast.next;
pre=low;
low=low.next;
}
if(low==head){
return head;
}else{
pre.next=null;
return low;
}
}
}
7、反转链表
class Solution {
public ListNode reverseList(ListNode head) {
return reverse(head);
}
//递归实现
public ListNode reverseRe(ListNode head){
if(head==null||head.next==null){
return head;
}
ListNode tem=reverseRe(head.next);
head.next.next=head;
head.next=null;
return tem;
}
//非递归实现
public ListNode reverse(ListNode head){
if(head==null||head.next==null){
return head;
}
ListNode pre=null;
ListNode now=head;
while(now!=null){
ListNode tem=now.next;
now.next=pre;
pre=now;
now=tem;
}
return pre;
}
}
8、合并两个有序链表
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dump=new ListNode(-1);
ListNode head=dump;
while(l1!=null&&l2!=null){
if(l1.val<=l2.val){
head.next=new ListNode(l1.val);
head=head.next;
l1=l1.next;
}else{
head.next=new ListNode(l2.val);
head=head.next;
l2=l2.next;
}
}
while(l1!=null){
head.next=new ListNode(l1.val);
head=head.next;
l1=l1.next;
}
while(l2!=null){
head.next=new ListNode(l2.val);
head=head.next;
l2=l2.next;
}
return dump.next;
}
}
9、树的子结构
class Solution {
public boolean isSubStructure(TreeNode A, TreeNode B) {
if(A==null||B==null){
return false;
}
//先从根节点判断B是不是A的子结构,如果不是在分别从左右两个子树判断,
//只要有一个为true,就说明B是A的子结构
return judge(A,B)||isSubStructure(A.left,B)||isSubStructure(A.right,B);
}
public boolean judge(TreeNode t1,TreeNode t2){
//这里如果t2为空,说明t2已经访问完了,确定是t1的子结构
if(t2==null){
return true;
}
//如果B不为空A为空,或者这两个节点值不同,说明B树不是
//A的子结构,直接返回false
if(t1==null||t1.val!=t2.val){
return false;
}
//当前节点比较完之后还要继续判断左右子节点
return judge(t1.left,t2.left)&&judge(t1.right,t2.right);
}
}
10、二叉树的镜像
class Solution {
public TreeNode mirrorTree(TreeNode root) {
if(root==null){
return root;
}
if(root.left==null&&root.right==null){
return root;
}
TreeNode tem=root.left;
root.left=mirrorTree(root.right);
root.right=mirrorTree(tem);
return root;
}
}
11、对称的二叉树
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null){
return true;
}
return judge(root.left,root.right);
}
public boolean judge(TreeNode t1,TreeNode t2){
if(t1==null&&t2==null){
return true;
}
if(t1==null||t2==null||t1.val!=t2.val){
return false;
}
return judge(t1.left,t2.right)&&judge(t1.right,t2.left);
}
}
12、顺时针打印数组
class Solution {
public int[] spiralOrder(int[][] matrix) {
if(matrix.length==0||matrix[0].length==0){
return new int[0];
}
int m=matrix.length;
int n=matrix[0].length;
int[] arr=new int[m*n];
int left=0;
int right=n-1;
int top=0;
int buttom=m-1;
int num=0;
while(true){
for(int i=left;i<=right;i++){
arr[num++]=matrix[top][i];
}
if(++top>buttom)break;
for(int i=top;i<=buttom;i++){
arr[num++]=matrix[i][right];
}
if(--right<left)break;
for(int i=right;i>=left;i--){
arr[num++]=matrix[buttom][i];
}
if(--buttom<top)break;
for(int i=buttom;i>=top;i--){
arr[num++]=matrix[i][left];
}
if(++left>right)break;
}
return arr;
}
}
13、包含min函数的栈
class MinStack {
//解题思路:使用双栈,第一个栈用于存储元素,第二个站使其存入的元素单调递减
/** initialize your data structure here. */
private Stack<Integer> st1;
private Stack<Integer> st2;
public MinStack() {
st1=new Stack<>();
st2=new Stack<>();
}
public void push(int x) {
st1.push(x);
if(st2.isEmpty()||st2.peek()>=x){
st2.push(x);
}
}
public void pop() {
if(st1.pop().equals(st2.peek()))
st2.pop();
}
public int top() {
return st1.peek();
}
public int min() {
return st2.peek();
}
}
14、栈的压入、弹出序列
解题思路:使用栈模拟出入栈。
class Solution {
public boolean validateStackSequences(int[] pushed, int[] popped) {
if(pushed.length!=popped.length){
return false;
}
Stack<Integer> stack=new Stack<>();
int index=0;
for(int i=0;i<pushed.length;i++){
stack.push(pushed[i]);
while(index<popped.length&&!stack.isEmpty()&&stack.peek()==popped[index]){
stack.pop();
index++;
}
}
if(stack.isEmpty()){
return true;
}else{
return false;
}
}
}
15、二叉树的层序遍历
class Solution {
public int[] levelOrder(TreeNode root) {
if(root==null){
return new int[0];
}
if(root.left==null&&root.right==null){
return new int[]{root.val};
}
List<Integer> list=new ArrayList<>();
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while(!queue.isEmpty()){
int len=queue.size();
for(int i=0;i<len;i++){
TreeNode tem=queue.poll();
list.add(tem.val);
if(tem.left!=null){
queue.offer(tem.left);
}
if(tem.right!=null){
queue.offer(tem.right);
}
}
}
int[] arr=new int[list.size()];
for(int i=0;i<list.size();i++){
arr[i]=list.get(i);
}
return arr;
}
}
16、从上到下打印二叉树3
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> list=new ArrayList<>();
if(root==null){
return list;
}
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
int count=0;
while(!queue.isEmpty()){
List<Integer> list1=new ArrayList<>();
Stack<TreeNode> stack=new Stack<>();
int len=queue.size();
if((count&1)==1){
for(int i=len-1;i>=0;i--){
TreeNode tem=queue.poll();
stack.push(tem);
if(tem.left!=null){
queue.offer(tem.left);
}
if(tem.right!=null){
queue.offer(tem.right);
}
}
while(!stack.isEmpty()){
list1.add(stack.pop().val);
}
}else{
for(int i=len-1;i>=0;i--){
TreeNode tem=queue.poll();
list1.add(tem.val);
if(tem.left!=null){
queue.offer(tem.left);
}
if(tem.right!=null){
queue.offer(tem.right);
}
}
}
count++;
list.add(list1);
}
return list;
}
}
17、二叉搜索树的后序遍历序列
解题思路:解题思路:我们知道后续遍历最后一个节点为根节点,从前完后遍历数组找到第一个比根节点大的值,此时当前节点的左边为根节点的左子树,判断左子树的节点值是否全部小于根节点判断当前节点到根节点之前的元素是否全部大于根节点,若全部满足,则判断左子树和右子树是否满足条件。
class Solution {
//解题思路:我们知道后续遍历最后一个节点为根节点,从前完后遍历数组
//找到第一个比根节点大的值,此时当前节点的左边为根节点的左子树,判断左子树的节点值是否全部小于根节点
//判断当前节点到根节点之前的元素是否全部大于根节点,若全部满足,则判断左子树和右子树是否满足条件
public boolean verifyPostorder(int[] postorder) {
if(postorder.length<2){
return true;
}
int root=postorder[postorder.length-1];
int i=0;
for(;i<postorder.length-1;i++){
if(postorder[i]>root){
break;
}
}
for(int j=0;j<i;j++){
if(postorder[j]>=root){
return false;
}
}
for(int j=i;j<postorder.length-1;j++){
if(postorder[j]<=root){
return false;
}
}
return verifyPostorder(Arrays.copyOfRange(postorder,0,i))&&
verifyPostorder(Arrays.copyOfRange(postorder,i,postorder.length-1));
}
}
18、二叉树中和为某一值的路径
class Solution {
private List<List<Integer>> list=new LinkedList<>();
public List<List<Integer>> pathSum(TreeNode root, int sum) {
if(root==null){
return list;
}
backtrace(root,sum,new LinkedList<>());
return list;
}
public void backtrace(TreeNode root,int sum,List<Integer> list1){
if(root==null){
return;
}
list1.add(root.val);
sum-=root.val;
if(sum==0&&root.left==null&&root.right==null){
list.add(new LinkedList<>(list1));
}
backtrace(root.left,sum,list1);
backtrace(root.right,sum,list1);
list1.remove(list1.size()-1);
}
}
19、二叉搜索树与双向链表(重点)
方法一:
class Solution {
Node head, pre;
public Node treeToDoublyList(Node root) {
if(root==null) return null;
dfs(root);
pre.right = head;
head.left =pre;//进行头节点和尾节点的相互指向,这两句的顺序也是可以颠倒的
return head;
}
public void dfs(Node cur){
if(cur==null) return;
dfs(cur.left);
//pre用于记录双向链表中位于cur左侧的节点,即上一次迭代中的cur,
//当pre==null时,cur左侧没有节点,即此时cur为双向链表中的头节
if(pre==null) head = cur;
//反之,pre!=null时,cur左侧存在节点pre,需要进行pre.right=cur的操作。
else pre.right = cur;
cur.left = pre;//pre是否为null对这句没有影响,且这句放在上面两句if else之前也是可以的。
pre = cur;//pre指向当前的cur
dfs(cur.right);//全部迭代完成后,pre指向双向链表中的尾节点
}
}
方法二:
首先使用中序遍历记录节点,然后再构造双向链表
class Solution {
public Node treeToDoublyList(Node root) {
if(root==null){
return null;
}
if(root.left==null&&root.right==null){
//注意返回循环的双向链表,当只有根节点时,也要形成循环
root.left=root;
root.right=root;
return root;
}
Queue<Node> queue=new LinkedList<>();
midOrder(root,queue);
root=queue.poll();
Node pre=root;
Node cur=null;
while(!queue.isEmpty()){
cur=queue.poll();
pre.right=cur;
cur.left=pre;
pre=cur;
}
if(cur!=null){
cur.right=root;
root.left=cur;
}
return root;
}
public void midOrder(Node root,Queue<Node> queue){
Stack<Node> stack=new Stack<>();
while(root!=null||!stack.isEmpty()){
while(root!=null){
stack.push(root);
root=root.left;
}
if(!stack.isEmpty()){
queue.add(stack.peek());
root=stack.pop().right;
}
}
}
}
20、1-n整数中1出现的次数
结题思路:
所以,当n的开头为1时,
f(n)=f(pow-1)+last+1+f(last)
不是1开头的时候,
f(n)=pow+high*f(pow-1)+f(last)
class Solution {
public int countDigitOne(int n) {
return re(n);
}
public int re(int n){
if(n<=0){
return 0;
}
String s=String.valueOf(n);
int high=s.charAt(0)-'0';
int power=(int)Math.pow(10,s.length()-1);
int last=n-high*power;
if(high==1){
return re(power-1)+last+1+re(last);
}else{
return power+high*re(power-1)+re(last);
}
}
}
20、把数组排成最小的数
class Solution {
public String minNumber(int[] nums) {
String[] tem=new String[nums.length];
for(int i=0;i<nums.length;i++){
tem[i]=String.valueOf(nums[i]);
}
Arrays.sort(tem,(o1,o2)->(o1+o2).compareTo(o2+o1));
StringBuilder str=new StringBuilder();
for(String s:tem){
str.append(s);
}
return str.toString();
}
}
21、把数字翻译成字符串
class Solution {
public int translateNum(int num) {
char[] arr=String.valueOf(num).toCharArray();
int[] dp=new int[arr.length+1];
dp[0]=dp[1]=1;
for(int i=2;i<=arr.length;i++){
if(arr[i-1]>'5'){
if(arr[i-2]>'1'||arr[i-2]=='0'){
dp[i]=dp[i-1];
}else if(arr[i-2]=='1'){
dp[i]=dp[i-1]+dp[i-2];
}
}else if(arr[i-2]=='1'||arr[i-2]=='2'){
dp[i]=dp[i-1]+dp[i-2];
}else{
dp[i]=dp[i-1];
}
}
return dp[arr.length];
}
}
22、礼物的最大价值
class Solution {
public int maxValue(int[][] grid) {
int[][] dp=new int[grid.length][grid[0].length];
dp[0][0]=grid[0][0];
for(int i=1;i<grid[0].length;i++){
dp[0][i]=dp[0][i-1]+grid[0][i];
}
for(int i=1;i<grid.length;i++){
dp[i][0]=dp[i-1][0]+grid[i][0];
}
for(int i=1;i<grid.length;i++){
for(int j=1;j<grid[0].length;j++){
dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1])+grid[i][j];
}
}
return dp[grid.length-1][grid[0].length-1];
}
}
23、最长不含重复字符的子字符串
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s==null||s.length()<1){
return 0;
}
HashMap<Character,Integer> map=new HashMap<>();
int max=1;
//窗口的起点
int left=0;
char[] arr=s.toCharArray();
for(int i=0;i<arr.length;i++){
if(map.containsKey(arr[i])){
left=Math.max(left,map.get(arr[i])+1);
}
map.put(arr[i],i);
max=Math.max(max,i-left+1);
}
return max;
}
}
24、丑数(重点)
// 一个十分巧妙的动态规划问题
// 1.我们将前面求得的丑数记录下来,后面的丑数就是前面的丑数2,3,5
// 2.但是问题来了,我怎么确定已知前面k-1个丑数,我怎么确定第k个丑数呢
// 3.采取用三个指针的方法,p2,p3,p5
// 4.index2指向的数字下一次永远2,p3指向的数字下一次永远3,p5指向的数字永远5
// 5.我们从2p2 3p3 5p5选取最小的一个数字,作为第k个丑数
// 6.如果第K个丑数==2p2,也就是说前面0-p2个丑数*2不可能产生比第K个丑数更大的丑数了,所以p2++
// 7.p3,p5同理
// 8.返回第n个丑数
class Solution {
// 一个十分巧妙的动态规划问题
// 1.我们将前面求得的丑数记录下来,后面的丑数就是前面的丑数*2,*3,*5
// 2.但是问题来了,我怎么确定已知前面k-1个丑数,我怎么确定第k个丑数呢
// 3.采取用三个指针的方法,p2,p3,p5
// 4.index2指向的数字下一次永远*2,p3指向的数字下一次永远*3,p5指向的数字永远*5
// 5.我们从2*p2 3*p3 5*p5选取最小的一个数字,作为第k个丑数
// 6.如果第K个丑数==2*p2,也就是说前面0-p2个丑数*2不可能产生比第K个丑数更大的丑数了,所以p2++
// 7.p3,p5同理
// 8.返回第n个丑数
public int nthUglyNumber(int n) {
int a=0,b=0,c=0;
int[] dp=new int[n];
dp[0]=1;
for(int i=1;i<n;i++){
dp[i]=Math.min(Math.min(dp[a]*2,dp[b]*3),dp[c]*5);
if(dp[i]==dp[a]*2)a++;
if(dp[i]==dp[b]*3)b++;
if(dp[i]==dp[c]*5)c++;
}
return dp[n-1];
}
}
25、第一个只出现一次的字符
class Solution {
public char firstUniqChar(String s) {
if(s==null||s.length()<1)return ' ';
HashMap<Character,Boolean> map=new LinkedHashMap<>();
for(char c:s.toCharArray()){
map.put(c,!map.containsKey(c));
}
for(Map.Entry<Character,Boolean> m:map.entrySet()){
if(m.getValue())return m.getKey();
}
return ' ';
}
}
26、两个链表的第一个公共节点
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode a=headA;
ListNode b=headB;
while(a!=b){
a=a==null?headB:a.next;
b=b==null?headA:b.next;
}
return a;
}
}
27、在排序数组中查找数字1
解题思路:二分法
class Solution {
public int search(int[] nums, int target) {
int count=0;
int left=0;
int right=nums.length-1;
while(left<=right){
int mid=(left+right)/2;
if(nums[mid]==target){
count++;
int i=1;
while((mid+i)<nums.length&&nums[mid+i]==target){
count++;
i++;
}
i=1;
while((mid-i)>=0&&nums[mid-i]==target){
count++;
i++;
}
break;
}else if(nums[mid]<target){
left=mid+1;
}else{
right=mid-1;
}
}
return count;
}
}
28、0~n-1中缺失的数字
class Solution {
public int missingNumber(int[] nums) {
int left=0;
int right=nums.length-1;
while(left<=right){
int mid=(left+right)/2;
if(nums[mid]==mid){
left=mid+1;
}else{
right=mid-1;
}
}
return left;
}
}
29、二叉搜索树的第k大节点
class Solution {
int k,result;
public int kthLargest(TreeNode root, int k) {
this.k=k;
rightRe(root);
return result;
}
public void rightRe(TreeNode root){
if(root==null){
return;
}else{
rightRe(root.right);
if(k==0)return;
if(--k==0)result=root.val;
rightRe(root.left);
}
}
}
30、二叉树的深度
class Solution {
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}
if(root.left==null&&root.right==null){
return 1;
}
return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
}
}
31、平衡二叉树
class Solution {
public boolean isBalanced(TreeNode root) {
if(root==null){
return true;
}
if(Math.abs(deepTree(root.left)-deepTree(root.right))<=1){
return isBalanced(root.left)&&isBalanced(root.right);
}else{
return false;
}
}
public int deepTree(TreeNode root){
if(root==null){
return 0;
}else{
return Math.max(deepTree(root.left),deepTree(root.right))+1;
}
}
}
32、左旋转字符串
class Solution {
public String reverseLeftWords(String s, int n) {
if(s==null||s.length()<1||n==0||n>=s.length()){
return s;
}
return (s+s).substring(n,n+s.length());
}
}
33、二叉搜索树的最近公共祖先
解题思路:
若根节点为p和q的最近公共祖先,需要满足(root.val>=p.val&&root.val<=q.val)||(root.val>=q.val&&root.val<=p.val)
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if((root.val>=p.val&&root.val<=q.val)||(root.val>=q.val&&root.val<=p.val)){
return root;
}
if(root.val>p.val){
return lowestCommonAncestor(root.left,p,q);
}else{
return lowestCommonAncestor(root.right,p,q);
}
}
}
34、二叉树的最近公共祖先
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null||root==p||root==q){
return root;
}
TreeNode left=lowestCommonAncestor(root.left,p,q);
TreeNode right=lowestCommonAncestor(root.right,p,q);
if(left==null)return right;
if(right==null)return left;
return root;
}
}