题目描述:
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1,m<=n),每段绳子的长度记为k[1],…,k[m]。请问k[1]x…xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
输入描述:
输入一个数n,意义见题面。(2<=n<=60)
输出描述:
输出答案。
示例:
输入:8
输出:18
牛客网上大佬给出的数学解释:
问题类似于定周长求最大面积的问题(例如给定四边形周长,求最大面积),当k[0]==k[1]==k[m]时乘积最大,设k[0]=x,那么n=x*m,乘积可以用下式表示
f(x)=(x)^(n/x)
下面是f(x)的导数:
乘积函数在n/m=e的时候,取得最大值,可知,当x∈(0,e)时f(x)单调递增,当x>e时,单调递减,因此,在x=e时取得最大值,e≈2.718,是自然对数。
从函数图像上也可以看出这一点
f(x)的函数图像:
又因为x的取值只能为整数,且f(3)>f(2),所以,当n>3时,将n尽可能地分割为3的和时,乘积最大。 当n>3时,有三种情况,即n%3==0, n%3==1, n%3==2,如下所示:
上式中除法向下取整
当n≤3时,
当n==2时f(x)=1;
当n==3时f(x)=2;
完整代码:
//贪心法
public class Solution {
public int cutRope(int target) {
if(target==2){
return 1;
}else if(target==3){
return 2;
}
if(target%3==0)
return (int)Math.pow(3,target/3);
else if(target%3==1)
return 4*(int)Math.pow(3,target/3-1);
else
return 2*(int)Math.pow(3,target/3);
}
}
完整代码:
//动态规划法
public class Solution {
public int cutRope(int target) {
if(target==2)
return 1;
if(target==3)
return 2;
int[] dp=new int[target+1];
dp[1]=1;
dp[2]=2;
dp[3]=3;
int res=0;
for(int i=4;i<=target;i++)
{
for(int j=1;j<=i/2;j++)
{
if(res<dp[j]*dp[i-j])
res=dp[j]*dp[i-j];
}
dp[i]=res;
}
return dp[target];
}
}
题目描述
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
public class Solution {
int count=0;
public int movingCount(int threshold, int rows, int cols)
{
boolean[] pass = new boolean[rows*cols];
movingCount(threshold,0,0,rows,cols,pass);
return count;
}
public void movingCount(int threshold, int i, int j,int rows, int cols,boolean[] pass){
int index = i*cols+j;
if(i<0||j<0||i>=rows||j>=cols||pass[index]==true)
return ;
if(canReach(i,j)<=threshold){
count++;
pass[index]=true;
}else{
pass[index]=false;
return;
}
movingCount(threshold, i-1, j,rows, cols,pass);
movingCount(threshold, i+1, j,rows, cols,pass);
movingCount(threshold, i, j-1,rows, cols,pass);
movingCount(threshold, i, j+1,rows, cols,pass);
}
public int canReach(int i,int j){
int sum = 0;
do{
sum += i%10;
}while((i = i/10) > 0);
do{
sum += j%10;
}while((j = j/10) > 0);
return sum;
}
}
public class Solution {
public int movingCount(int k, int m, int n) {
boolean[][] visited=new boolean[m][n];
for(int i=0;i<m;i++){
for(int j=0;j<n;j++){
visited[i][j]=false;
}
}
int count=movingCount(k,0,0,m,n,visited);
return count;
}
public int movingCount(int k, int m, int n,int rows,int cols,boolean[][] visited) {
if(m<0||n<0||m>=rows||n>=cols||visited[m][n]==true||!check(m,n,k))
return 0;
int count=0;
visited[m][n]=true;
count=movingCount(k,m-1,n,rows,cols,visited)+
movingCount(k,m,n-1,rows,cols,visited)+
movingCount(k,m+1,n,rows,cols,visited)+
movingCount(k,m,n+1,rows,cols,visited)+1;
return count;
}
public boolean check(int m,int n,int k){
int count=0;
int s=0;
while(m!=0){
s=m%10;
count+=s;
m=m/10;
}
while(n!=0){
s=n%10;
count+=s;
n=n/10;
}
if(count<=k)
return true;
return false;
}
}
public class Solution {
public int movingCount(int threshold, int rows, int cols)
{
int count=0;
if(threshold<0){
return 0;
}
boolean[][] pass=new boolean[rows+1][cols+1];
pass[0][0]=true;
for(int i=1;i<=rows;i++)
{
if(pass[i-1][0]&&canReach(threshold,i,0))
pass[i][0]=true;
else
pass[i][0]=false;
}
for(int j=1;j<=cols;j++)
{
if(pass[0][j-1]&&canReach(threshold,0,j))
pass[0][j]=true;
else
pass[0][j]=false;
}
for(int i=1;i<=rows;i++)
{
for(int j=1;j<=cols;j++)
{
if((pass[i-1][j]&&canReach(threshold,i,j))||(pass[i][j-1]&&canReach(threshold,i,j)))
pass[i][j]=true;
else
pass[i][j]=false;
}
}
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
if(pass[i][j])
count++;
}
}
return count;
}
public boolean canReach(int threshold, int x, int y){
int sum = 0;
while (x > 0) {
sum += x % 10;
x /= 10;
}
while (y > 0) {
sum += y % 10;
y /= 10;
}
return sum <= threshold;
}
}
题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子。 例如 [ a b c e s f c s a d e e ] \begin{gathered} \begin{bmatrix} a & b&c&e \\ s & f & c & s \\ a & d & e & e \end{bmatrix} \end{gathered} ⎣⎡asabfdcceese⎦⎤矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param matrix string字符串
* @param rows int整型
* @param cols int整型
* @param str string字符串
* @return bool布尔型
*/
public boolean hasPath (String matrix, int rows, int cols, String str) {
char[][] board=new char[rows][cols];
int k=0;
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
board[i][j]=matrix.charAt(k++);
}
}
boolean[][] visited=new boolean[rows][cols];//默认值为false
for(int i=0; i<rows; i++){
for(int j=0; j<cols; j++){
if(dfs(board,i,j,visited,str,0))
return true;
}
}
return false;
}
private boolean dfs(char[][] board,int i,int j,boolean[][] visited,String str,int k){
if(i<0 || i>=board.length || j<0 || j>=board[0].length || visited[i][j])
return false;
if(board[i][j]!=str.charAt(k))
return false;
if(k==str.length()-1)
return true;
visited[i][j]=true;
//注意!!!最后一个参数是k+1,而不是k++。将k+1的值传入而不是讲k传入再加1
boolean res=dfs(board,i+1,j,visited,str,k+1) ||
dfs(board,i-1,j,visited,str,k+1) ||
dfs(board,i,j+1,visited,str,k+1) ||
dfs(board,i,j-1,visited,str,k+1);
//走到这,说明这一条路不通,还原,再试其他的路径
visited[i][j]=false;
return res;
}
}
题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
int max=num[0];
int flag=0;
ArrayList<Integer> list=new ArrayList<Integer>();
if(size==0)
return list;
if(num.length<size)
return list;
int end=num.length-size;
for(int i=0;i<=end;i++)
{
flag=i+size;
max=num[i];
for(int j=i;j<flag;j++)
{
if(num[j]>max)
max=num[j];
}
list.add(max);
}
return list;
}
}
import java.util.*;
/**
用一个双端队列,队列第一个位置保存当前窗口的最大值,当窗口滑动一次
1.判断当前最大值是否过期
2.新增加的值从队尾开始比较,把所有比他小的值丢掉
例如:{2,3,4,2,6,2,5,1}
(1)i=0
begin=-2
q:i=0
(2)i=1
begin=-1
q:i=1
(3)i=2
begin=0
q:i=2
res:num[2]
(4)i=3
begin=1
q:i=3,i=2
res:num[2],num[2]
(5)i=4
begin=2
q:i=4
res:num[2],num[2],num[4]
(6)i=5
begin=3
q:i=5,i=4
res:num[2],num[2],num[4],num[4]
(6)i=6
begin=4
q:i=6,i=4
res:num[2],num[2],num[4],num[4],num[4]
(7)i=7
begin=5
q:i=6
q:i=7,i=6
res:num[2],num[2],num[4],num[4],num[4],num[6]
{2,3,4,2,6,2,5,1} 4 4 6 6 6 5
*/
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> res = new ArrayList<>();
if(size == 0) return res;
int begin;
//双端队列,队列第一个位置保存当前窗口的最大值
ArrayDeque<Integer> q = new ArrayDeque<>();
for(int i = 0; i < num.length; i++){
begin = i - size + 1;//begin是用于保存当前窗口的第一个值在原始数组中的【下标】,这样写的目的是为了少写一些判断边界条件的代码。
if(q.isEmpty())
q.add(i);
//1.判断当前最大值是否过期
else if(begin > q.peekFirst())//peekFirst()返回队列第一个元素,但是不删除这个元素
q.pollFirst();
//2.新增加的值从队尾开始比较,把所有比他小的值丢掉
while((!q.isEmpty()) && num[q.peekLast()] <= num[i])
q.pollLast();
q.add(i);
if(begin >= 0)
res.add(num[q.peekFirst()]);
}
return res;
}
}
/*最大堆方法
构建一个窗口size大小的最大堆,每次从堆中取出窗口的最大值,
随着窗口往右滑动,需要将堆中不属于窗口的堆顶元素删除。*/
import java.util.*;
public class Solution {
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> res= new ArrayList<>();
if (size > num.length || size < 1) return res;
// 构建最大堆,即堆顶元素是堆的最大值。
PriorityQueue<Integer> heap = new PriorityQueue<Integer>((o1, o2) -> o2 - o1);
for (int i = 0; i < size; i++)
heap.add(num[i]);
res.add(heap.peek());
for (int i = 1; i + size - 1 < num.length; i++)
{
heap.remove(num[i - 1]);
heap.add(num[i + size - 1]);
res.add(heap.peek());
}
return res;
}
}
题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
讲解:
先用java集合PriorityQueue来设置一个小顶堆和大顶堆
主要的思想是:因为要求的是中位数,那么这两个堆,大顶堆用来存较小的数,从大到小排列;
小顶堆存较大的数,从小到大的顺序排序,显然中位数就是大顶堆的根节点与小顶堆的根节点和的平均数。
⭐保证:小顶堆中的元素都大于等于大顶堆中的元素,所以每次塞值,并不是直接塞进去,而是从另一个堆中poll出一个最大(最小)的塞值
⭐当数目为偶数的时候,将这个值插入大顶堆中,再将大顶堆中根节点(即最大值)插入到小顶堆中;
⭐当数目为奇数的时候,将这个值插入小顶堆中,再讲小顶堆中根节点(即最小值)插入到大顶堆中;
⭐取中位数的时候,如果当前个数为偶数,显然是取小顶堆和大顶堆根结点的平均值;如果当前个数为奇数,显然是取小顶堆的根节点
理解了上面所述的主体思想,下面举个例子辅助验证一下。
例如,传入的数据为:[5,2,3,4,1,6,7,0,8],那么按照要求,输出是"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "
那么整个程序的执行流程应该是(用min表示小顶堆,max表示大顶堆):
5先进入大顶堆,然后将大顶堆中最大值放入小顶堆中,此时min=[5],max=[无],avg=[5.00]
2先进入小顶堆,然后将小顶堆中最小值放入大顶堆中,此时min=[5],max=[2],avg=[(5+2)/2]=[3.50]
3先进入大顶堆,然后将大顶堆中最大值放入小顶堆中,此时min=[3,5],max=[2],avg=[3.00]
4先进入小顶堆,然后将小顶堆中最小值放入大顶堆中,此时min=[4,5],max=[3,2],avg=[(4+3)/2]=[3.50]
1先进入大顶堆,然后将大顶堆中最大值放入小顶堆中,此时min=[3,4,5],max=[2,1],avg=[3/00]
6先进入小顶堆,然后将小顶堆中最小值放入大顶堆中,此时min=[4,5,6],max=[3,2,1],avg=[(4+3)/2]=[3.50]
7先进入大顶堆,然后将大顶堆中最大值放入小顶堆中,此时min=[4,5,6,7],max=[3,2,1],avg=[4]=[4.00]
0先进入小顶堆,然后将小顶堆中最大值放入小顶堆中,此时min=[4,5,6,7],max=[3,2,1,0],avg=[(4+3)/2]=[3.50]
8先进入大顶堆,然后将大顶堆中最小值放入大顶堆中,此时min=[4,5,6,7,8],max=[3,2,1,0],avg=[4.00]
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
//PriorityQueue默认是一个小顶堆,然而可以通过传入自定义的Comparator函数来实现大顶堆。
//小顶堆
private PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
//大顶堆
private PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(15, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
//记录偶数个还是奇数个
int count = 0;
//每次插入小顶堆的是当前大顶堆中最大的数
//每次插入大顶堆的是当前小顶堆中最小的数
//这样保证小顶堆中的数永远大于等于大顶堆中的数
//中位数就可以方便地从两者的根结点中获取了
public void Insert(Integer num) {
//个数为偶数的话,则先插入到大顶堆,然后将大顶堆中最大的数插入小顶堆中
if(count % 2 == 0){
maxHeap.offer(num);
int max = maxHeap.poll();
minHeap.offer(max);
}else{
//个数为奇数的话,则先插入到小顶堆,然后将小顶堆中最小的数插入大顶堆中
minHeap.offer(num);
int min = minHeap.poll();
maxHeap.offer(min);
}
count++;
}
public Double GetMedian() {
//当前为偶数个,则取小顶堆和大顶堆的堆顶元素求平均
if(count % 2 == 0)
return new Double(minHeap.peek() + maxHeap.peek())/2;
else
//当前为奇数个,则直接从小顶堆中取元素即可
return new Double(minHeap.peek());
/*另一种写法
if(count%2==0)
return Double.valueOf((minheap.peek()+maxheap.peek())/2.0);
else
return Double.valueOf(maxheap.peek());
*/
}
}
import java.util.*;
public class Solution {
ArrayList<Integer> res = new ArrayList<>();
public void Insert(Integer num) {
res.add(num);
Collections.sort(res);
}
public Double GetMedian() {
int n = res.size();
if (n % 2 == 0) {
return Double.valueOf((res.get(n / 2) + res.get(n / 2 - 1)) / 2.0);
} else {
return Double.valueOf(res.get(n / 2));
}
}
}
题目描述
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
TreeNode KthNode(TreeNode root, int k){
if(root==null||k==0)
return null;
Stack<TreeNode> stack = new Stack<TreeNode>();
int count = 0;
TreeNode node = root;
do{
if(node!=null){
stack.push(node);
node = node.left;
}else{
node = stack.pop();
count++;
if(count==k)
return node;
node = node.right;
}
}while(node!=null||!stack.isEmpty());
return null;
}
}
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
/*思路:二叉搜索树按照中序遍历的顺序打印出来正好就是排序好的顺序。
所以,按照中序遍历顺序找到第k个结点就是结果。*/
public class Solution {
int index=0;
TreeNode KthNode(TreeNode pRoot, int k)
{
if(pRoot!=null)
{
TreeNode node=KthNode(pRoot.left,k);
if(node!=null)
return node;
index++;//从最左节点开始,count+1
if(index==k)
return pRoot;
node=KthNode(pRoot.right,k);
if(node!=null)
return node;
}
return null;
}
}
题目描述
请实现两个函数,分别用来序列化和反序列化二叉树
二叉树的序列化是指:把一棵二叉树按照某种遍历方式的结果以某种格式保存为字符串,从而使得内存中建立起来的二叉树可以持久保存。序列化可以基于先序、中序、后序、层序的二叉树遍历方式来进行修改,序列化的结果是一个字符串,序列化时通过 某种符号表示空节点(#),以 ! 表示一个结点值的结束(value!)。
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。
例如,我们可以把一个只有根节点为1的二叉树序列化为"1,",然后通过自己的函数来解析回这个二叉树
完整代码:
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
String Serialize(TreeNode root) {
if(root==null) return null;
StringBuilder sb = new StringBuilder();//可变字符序列
LinkedList<TreeNode> list = new LinkedList<>();
list.add(root);
while(!list.isEmpty()){
TreeNode temp = list.poll();
if(temp!=null){
sb.append(temp.val+"!");
list.offer(temp.left);
list.offer(temp.right);
}
else{
sb.append("#!");
}
}
return sb.toString();
}
TreeNode Deserialize(String str) {
if(str==null||str.length()==0) return null;
LinkedList<TreeNode> list = new LinkedList<>();
String[] s = str.substring(0,str.length()).split("!");
int cur=0;
TreeNode root = new TreeNode(Integer.parseInt(s[cur]));
list.add(root);
while(!list.isEmpty()){
TreeNode temp = list.poll();
if(!s[++cur].equals("#")){
temp.left=new TreeNode(Integer.parseInt(s[cur]));
list.offer(temp.left);
}
if(!s[++cur].equals("#")){
temp.right=new TreeNode(Integer.parseInt(s[cur]));
list.offer(temp.right);
}
}
return root;
}
}
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public int index = -1;//记录反序列化的索引位置
String Serialize(TreeNode root) {
StringBuffer sb = new StringBuffer();//new一个可变字符串,可以想到
if(root == null){//当前节点为空,可变字符串添加“#,"后,返回上一级
sb.append("#,");
return sb.toString();
}
sb.append(root.val + ",");//当前节点不为空,可变字符串中添加当前节点的值以及","
sb.append(Serialize(root.left));//递归调用左子树并将结果添加到可变字符串
sb.append(Serialize(root.right));//递归调用右子树并将结果添加到可变字符串
return sb.toString();//转换成字符串类型返回结果
}
TreeNode Deserialize(String str) {
index++;//准备反序列化一个节点,因此索引加1
TreeNode node = null;//准备好反序列化后的根节点
int len = str.length();
if(index >= len){//准备好递归结束条件,序列化结束,返回结果
return node;
}
String[] s = str.split(",");//准备好要反序列化的数据
if(!s[index].equals("#")){//开始反序列化,针对所有不为空的节点
node = new TreeNode(Integer.valueOf(s[index]));//new一个树的节点
node.left = Deserialize(str);//找到这个节点的左子树
node.right = Deserialize(str);//找到这个节点的右子树
}
return node;//返回根节点
}
}
题目描述
从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> result = new ArrayList<ArrayList<Integer>>();
if(pRoot == null){
return result;
}
Queue<TreeNode> layer = new LinkedList<TreeNode>();//存当前行的所有元素结点
ArrayList<Integer> layerList = new ArrayList<Integer>();//存当前行元素的值
layer.add(pRoot);
int start = 0, end = 1;
while(!layer.isEmpty()){
TreeNode cur = layer.remove();
layerList.add(cur.val);
start++;
if(cur.left!=null){
layer.add(cur.left);
}
if(cur.right!=null){
layer.add(cur.right);
}
if(start == end){
end = layer.size();
start = 0;
result.add(layerList);
layerList = new ArrayList<Integer>();
}
}
return result;
}
}
import java.util.ArrayList;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> list = new ArrayList<>();
depth(pRoot, 1, list);
return list;
}
private void depth(TreeNode root, int depth, ArrayList<ArrayList<Integer>> list) {
if(root == null) return;
if(depth > list.size())
list.add(new ArrayList<Integer>());
list.get(depth -1).add(root.val);
depth(root.left, depth + 1, list);
depth(root.right, depth + 1, list);
}
}
题目描述
请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推。
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> result = new ArrayList<>();
//使用两个栈,一个存奇数层,一个存偶数层。因为是紧贴的,所以不用根据奇偶层来判断。
Stack<TreeNode> stack1 = new Stack<>();
Stack<TreeNode> stack2 = new Stack<>();
//检查参数
if(pRoot == null){
return result;
}
ArrayList<Integer> tempList = new ArrayList<>();
stack1.push(pRoot);
//直到两个栈都为empty才停止
while(!stack1.isEmpty() || !stack2.isEmpty()){
while(!stack1.isEmpty()){
TreeNode tempNode = stack1.pop();
tempList.add(tempNode.val);
//往偶数层存,从左到右
if(tempNode.left != null){
stack2.add(tempNode.left);
}
if(tempNode.right != null){
stack2.add(tempNode.right);
}
}
if(!tempList.isEmpty()){
result.add(tempList);
tempList = new ArrayList<>();
}
while(!stack2.isEmpty()){
TreeNode tempNode = stack2.pop();
tempList.add(tempNode.val);
//往奇数层存,从右到左
if(tempNode.right != null){
stack1.add(tempNode.right);
}
if(tempNode.left != null){
stack1.add(tempNode.left);
}
}
if(!tempList.isEmpty()){
result.add(tempList);
tempList = new ArrayList<>();
}
}
return result;
}
}
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList<>();
if (pRoot == null) {
return res;
}
ArrayList<Integer> al = new ArrayList<>();
LinkedList<TreeNode> ll = new LinkedList<>();
ll.addLast(null);//层分隔符
ll.addLast(pRoot);
boolean LeftToRight = true;
while (ll.size() != 1) {
TreeNode node = ll.removeFirst();
if (node == null) {//到达层分隔符
Iterator<TreeNode> iter = null;//iterator是为了实现对Java容器(collection)进行遍历功能的一个接口。
if (LeftToRight) {
iter = ll.iterator();//从前往后遍历
} else {
iter = ll.descendingIterator();//从后往前遍历
}
LeftToRight = !LeftToRight;
while (iter.hasNext()) {
TreeNode temp = (TreeNode)iter.next();
al.add(temp.val);
}
res.add(al);
al=new ArrayList<Integer>();
ll.addLast(null);//添加层分隔符
continue;//一定要continue
}
if (node.left != null) {
ll.addLast(node.left);
}
if (node.right != null) {
ll.addLast(node.right);
}
}
return res;
}
}
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
boolean isSymmetrical(TreeNode pRoot)
{
if(pRoot==null)
return true;
return judge(pRoot.left,pRoot.right);
}
private boolean judge(TreeNode left,TreeNode right)
{
if(left==null&&right==null)
return true;
if(left==null||right==null)
return false;
if(left.val!=right.val)
return false;
return judge(left.left,right.right)&&judge(left.right,right.left);
}
}
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
boolean isSymmetrical(TreeNode pRoot)
{
if(pRoot == null) return true;
Stack<TreeNode> s = new Stack<>();
s.push(pRoot.left);
s.push(pRoot.right);
while(!s.empty()) {
TreeNode right = s.pop();//成对取出
TreeNode left = s.pop();
if(left == null && right == null) continue;
if(left == null || right == null) return false;
if(left.val != right.val) return false;
//成对插入
s.push(left.left);
s.push(right.right);
s.push(left.right);
s.push(right.left);
}
return true;
}
}
import java.util.*;
/*
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
boolean isSymmetrical(TreeNode pRoot)
{
if(pRoot == null) return true;
Queue<TreeNode> s = new LinkedList<>();
s.offer(pRoot.left);
s.offer(pRoot.right);
while(!s.isEmpty()) {
TreeNode left= s.poll();//成对取出
TreeNode right= s.poll();
if(left == null && right == null) continue;
if(left == null || right == null) return false;
if(left.val != right.val) return false;
//成对插入
s.offer(left.left);
s.offer(right.right);
s.offer(left.right);
s.offer(right.left);
}
return true;
}
}
题目描述
给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。
解题思路
分析二叉树的下一个节点,一共有以下情况:
1.二叉树为空,则返回空;
2.节点右孩子存在,则设置一个指针从该节点的右孩子出发,一直沿着指向左子结点的指针找到的叶子节点即为下一个节点;
3.节点不是根节点。如果该节点是其父节点的左孩子,则返回父节点;如果该节点是其父节点的右孩子,继续向上遍历其父节点的父节点,重复之前的判断,返回结果。
如下图所示:
完整代码
/*
public class TreeLinkNode {
int val;
TreeLinkNode left = null;
TreeLinkNode right = null;
TreeLinkNode next = null;
TreeLinkNode(int val) {
this.val = val;
}
}
*/
public class Solution {
TreeLinkNode GetNext(TreeLinkNode node)
{
if(node==null) return null;
if(node.right!=null){ //如果有右子树,则找右子树的最左节点
node = node.right;
while(node.left!=null) node = node.left;
return node;
}
while(node.next!=null){ //没右子树,则找第一个当前节点是父节点左孩子的父节点
if(node.next.left==node) return node.next;
node = node.next;
}
return null; //退到了根节点仍没找到,则返回null
}
}
题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
ListNode node = pHead;
HashMap<Integer,Integer> map = new HashMap<>();
ArrayList<ListNode> list = new ArrayList<>();
while(node != null){
list.add(node);
if(map.containsKey(node.val)){
map.put(node.val,map.get(node.val)+1);
}else{
map.put(node.val,1);
}
node = node.next;
}
ListNode newNode = new ListNode(0);
ListNode newHead = newNode;
for(ListNode tempNode : list){
if(map.get(tempNode.val) == 1){
newNode.next = tempNode;
newNode = newNode.next;
}
}
newNode.next = null;//这里一定要将最后一个满足if的tempNode后面的链表断开
return newHead.next;
}
}
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead)
{
if(pHead==null || pHead.next==null){return pHead;}
ListNode Head = new ListNode(0);
Head.next = pHead;
ListNode pre = Head;
ListNode last = Head.next;
while (last!=null){
if(last.next!=null && last.val == last.next.val){
// 找到最后的一个相同节点
while (last.next!=null && last.val == last.next.val){
last = last.next;
}
pre.next = last.next;
last = last.next;
}else{
pre = pre.next;
last = last.next;
}
}
return Head.next;
}
}
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
if (pHead == null || pHead.next == null) { // 只有0个或1个结点,则返回
return pHead;
}
if (pHead.val == pHead.next.val) { // 当前结点是重复结点
ListNode pNode = pHead.next;
while (pNode != null && pNode.val == pHead.val) {
// 跳过值与当前结点相同的全部结点,找到第一个与当前结点不同的结点
pNode = pNode.next;
}
return deleteDuplication(pNode); // 从第一个与当前结点不同的结点开始递归
} else { // 当前结点不是重复结点
pHead.next = deleteDuplication(pHead.next); // 保留当前结点,从下一个结点开始递归
return pHead;
}
}
}
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
思路:
设置快慢指针,都从链表头出发,快指针每次走两步,慢指针一次走一步,假如有环,一定相遇于环中某点(结论1)。接着让两个指针分别从相遇点和链表头出发,两者都改为每次走一步,最终相遇于环入口(结论2)。以下是两个结论证明:
两个结论:
1、设置快慢指针,假如有环,他们最后一定相遇。
2、两个指针分别从链表头和相遇点继续出发,每次走一步,最后一定相遇与环入口。
证明结论1:设置快慢指针fast和low,fast每次走两步,low每次走一步。假如有环,两者一定会相遇(因为low一旦进环,可看作fast在后面追赶low的过程,每次两者都接近一步,最后一定能追上)。
证明结论2:
设:
链表头到环入口长度为–a
环入口到相遇点长度为–b
相遇点到环入口长度为–c
则:相遇时
快指针路程=a+(b+c)k+b ,k>=1 其中b+c为环的长度,k为绕环的圈数(k>=1,即最少一圈,不能是0圈,不然和慢指针走的一样长,矛盾)。
慢指针路程=a+b
快指针走的路程是慢指针的两倍,所以:
(a+b)*2=a+(b+c)k+b
化简可得:
a=(k-1)(b+c)+c 这个式子的意思是: 链表头到环入口的距离=相遇点到环入口的距离+(k-1)圈环长度。其中k>=1,所以k-1>=0圈。所以两个指针分别从链表头和相遇点出发,最后一定相遇于环入口。
完整代码:
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
ListNode node1=pHead;
ListNode node2=pHead;
if(pHead==null||pHead.next==null){
return null;
}
/*若测试用例有无环链表,这个循环node1、node2可能为空
while(node1!=node2){
node1=node1.next;
node2=node2.next.next;
}*/
while(node1!=null&&node2!=null){
node1=node1.next;
node2=node2.next.next;
if(node1==node2)
break;
}
node1=pHead;
while(node1!=node2){
node1=node1.next;
node2=node2.next;
}
return node1;
}
}
题目描述
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
输出描述:
如果当前字符流没有存在出现一次的字符,返回#字符。
思路:
对这个题目思考,可以发现,出现的字符和它的出现的次数是一种对应关系,自然联想到哈希表的 key-value 这种对应,或者应用关联容器 map,可以很方便的解决这个问题。map 容器中,它的一个元素就是一组(key,value)对应的数据。
完整代码:
import java.util.*;
public class Solution {
HashMap<Character,Integer> map=new HashMap();
ArrayList<Character> list=new ArrayList<Character>();
//Insert one char from stringstream
public void Insert(char ch)
{
if(map.containsKey(ch))
map.put(ch,map.get(ch)+1);
else
map.put(ch,1);
list.add(ch);//按顺序将每个字符加入集合
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
char ch='#';
for(int i = 0; i < list.size(); i++)//按顺序遍历list集合
{
char key = list.get(i);
if(map.get(key)==1)
{
ch=key;
break;
}
}
return ch;
}
}
题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
import java.util.*;
public class Solution {
public boolean isNumeric (String str) {
HashMap<Character,Integer> map = new HashMap<>();
if(str.length() == 1){
if(str.charAt(0) == '+' || str.charAt(0) == '-' ||
str.charAt(0) == '.' || str.charAt(0) == 'e' ||
str.charAt(0) == 'E' || str.charAt(0) == ' ')
return false;
}
for(int i = 0; i < str.length(); i++){
if(str.charAt(i) == 'e' || str.charAt(i) == 'E'){
if(i == 0 || i == str.length() - 1){
return false;
}
}
if(str.charAt(i) == '.'){
if((map.containsKey('e') || map.containsKey('E')) && (map.get('e') < i || map.get('E') < i)){
return false;
}
if(map.containsKey('.') ){
return false;
}
}
if(str.charAt(i) == '+' || str.charAt(i) == '-'){
if(i != 0 || (i > 0 && str.charAt(i-1) != 'e')){
return false;
}
}
map.put(str.charAt(i),i);
}
return true;
}
}
public class Solution {
public boolean isNumeric(String str) {
//HasE表示含有符号e,HasSign表示是否有正负号,HasPoint表示是否有小数点
Boolean HasE=false,HasPoint=false;
for(int i=0;i<str.length();i++)
{
if(str.charAt(i)=='e'||str.charAt(i)=='E'){
//e后面必须有数字,所以若e是最后一位不通过
if(i==str.length()-1)
return false;
//不能有两个e
if(HasE)
return false;
HasE=true;
}else if(str.charAt(i)=='+'||str.charAt(i)=='-'){
//如果+-不是第一个字符就只能在e后面,否则不通过
if(i>0&&str.charAt(i-1)!='e'&&str.charAt(i-1)!='E')
return false;
}else if(str.charAt(i)=='.'){
//如果存在e并且e后面为小数则不通过
if(HasE)
return false;
//不能有两个小数点
if(HasPoint)
return false;
HasPoint=true;
}else if(str.charAt(i)<'0'||str.charAt(i)>'9'){
//不是e也不是+-符号或小数点,那么只能是数字,不是数字就是非法的字符
return false;
}
}
return true;
}
}
题目描述
请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’‘表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab * ac * a" 匹配,但是与"aa.a"和"ab*a"均不匹配
思路
匹配第一个字符,这里有两种可能:匹配成功或匹配失败。但考虑到pattern下一个字符可能是’ * ',这里我们分两种情况讨论:pattern下一个字符为‘ * ’或不为‘ * ’:
1>pattern下一个字符不为‘ * ’:这种情况比较简单,直接匹配当前字符。如果匹配成功,继续匹配下一个;如果匹配失败,直接返回false。注意这里的“匹配成功”,除了两个字符相同的情况外,还有一种情况,就是pattern的当前字符为‘ . ’,同时str的当前字符不为‘\0’。
2>pattern下一个字符为‘ * ’时,稍微复杂一些,因为‘ * ’可以代表0个或多个。这里把这些情况都考虑到:
a>当‘ * ’匹配0个字符时,str当前字符不变,pattern当前字符后移两位,跳过这个‘ * ’符号;
b>当‘ * ’匹配1个或多个时,str当前字符移向下一个,pattern当前字符不变。(这里匹配1个或多个可以看成一种情况,因为:当匹配一个时,由于str移到了下一个字符,而pattern字符不变,就回到了上边的情况a;当匹配多于一个字符时,相当于从str的下一个字符继续开始匹配)
public class Solution {
public boolean match(String str, String pattern) {
char[] s=str.toCharArray();
char[] p=pattern.toCharArray();
if (pattern == null) {
return false;
}
int strIndex = 0;
int patternIndex = 0;
return matchCore(s, strIndex, p, patternIndex);
}
public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) {
//有效性检验:str到尾,pattern到尾,匹配成功
if (strIndex == str.length && patternIndex == pattern.length) {
return true;
}
//pattern先到尾,匹配失败
if (strIndex != str.length && patternIndex == pattern.length) {
return false;
}
//模式第2个是*,且字符串第1个跟模式第1个匹配,分2种匹配模式;如不匹配,模式后移2位
if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {
if ( (strIndex != str.length && pattern[patternIndex] == str[strIndex])
|| (pattern[patternIndex] == '.' && strIndex != str.length) ) {
return matchCore(str, strIndex, pattern, patternIndex + 2)//模式后移2,视为x*匹配0个字符
|| matchCore(str, strIndex + 1, pattern, patternIndex);//*匹配1个或多个,再匹配str中的下一个
} else {
return matchCore(str, strIndex, pattern, patternIndex + 2);
}
}
//模式第2个不是*,且字符串第1个跟模式第1个匹配,则都后移1位,否则直接返回false
if ((strIndex != str.length && pattern[patternIndex] == str[strIndex])
|| (pattern[patternIndex] == '.' && strIndex != str.length)) {
return matchCore(str, strIndex + 1, pattern, patternIndex + 1);
}
return false;
}
}
思路
先看下一个是否是“ * ”,再看当前是否相等
1.若下一个是" * “,分为当前相等和当前不等
1.1:当前相等dp[i][j]=dp[i][j+2] || dp[i+1][j]
1.2:当前不等dp[i][j]=dp[i][j+2]
2.若不是” * ",但是当前相等 d[i][j]= dp[i+1][j+1];
class Solution {
public boolean isMatch(String s, String p) {
boolean[][] dp=new boolean[s.length()+1][p.length()+1];
//初始化为true的含义是:若为str=aaa 和pattern=aa* 从末尾开始匹配" "和" "一定为true
dp[s.length()][p.length()]=true;
//外循环:从空串开始匹配,因为字符串为空也可能匹配成功。例" "和"a*"匹配
for(int i=s.length();i>=0;i--){
//内循环:从最后一个字符开始匹配,模式串不能为空,否则一定不匹配
for(int j=p.length()-1;j>=0;j--){
if(j<p.length()-1&&p.charAt(j+1)=='*'){
//两个字符串当前字符相等
if(i<s.length()&&(s.charAt(i)==p.charAt(j)||p.charAt(j)=='.')){
dp[i][j]=dp[i+1][j]||dp[i][j+2];
}else{//两个字符串当前字符不相等
dp[i][j]=dp[i][j+2];
}
}else{
if(i<s.length()&&(s.charAt(i)==p.charAt(j)||p.charAt(j)=='.')){
dp[i][j]=dp[i+1][j+1];
}
}
}
}
return dp[0][0];
}
}
题目描述
给定一个数组A[0,1,…,n-1],请构建一个数组B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。(注意:规定B[0] = A[1] * A[2] * … * A[n-1],B[n-1] = A[0] * A[1] * … * A[n-2];)
对于A长度为1的情况,B无意义,故而无法构建,因此该情况不会存在。
import java.util.ArrayList;
public class Solution {
public int[] multiply(int[] A) {
int[] B=new int[A.length];
if(A.length==1)
return null;
for(int i=0;i<A.length;i++)
{
B[i]=1;
}
for(int i=1;i<A.length;i++)
{
B[0]=B[0]*A[i];
}
for(int i=0;i<A.length-1;i++)
{
B[A.length-1]=B[A.length-1]*A[i];
}
for(int i=1;i<A.length-1;i++)
{
for(int j=0;j<A.length;j++)
{
if(j!=i)
B[i]=B[i]*A[j];
else
continue;
}
}
return B;
}
}
import java.util.ArrayList;
public class Solution {
int[] multiply(int[] A) {
int len = A.length;
int forword[] = new int[len];
int backword[] = new int[len];
int B[] = new int[len];
forword[0] = 1;
backword[0] = 1;
for(int i = 1;i < len; i++){
forword[i] = A[i - 1]*forword[i-1];
backword[i] = A[len - i]*backword[i - 1];
}
for(int i = 0; i < len; i++){
B[i] = forword[i] * backword[len - i -1];
}
return B;
}
}
题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
若题目中是请找出数组中第一个重复的数字,则此方法不适用。
import java.util.*;
public class Solution {
public int duplicate (int[] numbers) {
if(numbers==null||numbers.length==0)
return -1;
quick(numbers,0,numbers.length-1);
for(int i=0;i<numbers.length-1;i++){
if(numbers[i]==numbers[i+1]){
return numbers[i];
}
}
return -1;
}
public int partition(int[] array,int begin,int end){
int i=begin,j=end;
while(i<j){
while(i<j&&array[i]<=array[j]){
i++;
}
if(i<j){
int temp=array[i];
array[i]=array[j];
array[j]=temp;
j--;
}
while(i<j&&array[i]<=array[j]){
j--;
}
if(i<j){
int temp=array[i];
array[i]=array[j];
array[j]=temp;
i++;
}
}
return i;
}
public void quick(int[] array,int begin,int end){
if(begin<end){
int index=partition(array,begin,end);
quick(array,begin,index-1);
quick(array,index+1,end);
}
}
}
若题目中是请找出数组中第一个重复的数字,则此方法仍适用。
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param numbers int整型一维数组
* @return int整型
*/
public int duplicate (int[] numbers) {
int[] hashMap=new int[numbers.length];
if(numbers==null||numbers.length==0){
return -1;
}
for(int i=0;i<numbers.length;i++){
if(hashMap[numbers[i]]==1){
return numbers[i];
}
hashMap[numbers[i]]=1;
}
return -1;
}
}
public class Solution {
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
// Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
// 这里要特别注意~返回任意重复的一个,赋值duplication[0]
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
public boolean duplicate(int numbers[],int length,int [] duplication) {
if(numbers==null||length==0)
return false;
for(int i=0;i<length;i++){
if(numbers[i]<0||numbers[i]>length-1)
return false;
}
for(int i=0;i<length;i++){
if(numbers[i]!=i){
int j=numbers[i];
if(j!=numbers[j]){
int temp=numbers[i];
numbers[i]=numbers[j];
numbers[j]=temp;
}else{
duplication[0]=numbers[i];
return true;
}
}
}
return false;
}
}
题目描述
将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0
输入描述:
输入一个字符串,包括数字字母符号,可以为空
输出描述:
如果是合法的数值表达则返回该数字,否则返回0
示例1
输入
+2147483647
1a33
输出
2147483647
0
关于溢出处理:
Integer.MIN_VALUE,即-2147483648,二进制位如下: 1000 0000 0000 0000 0000 0000 0000 0000
Integer.MAX_VALUE,即 2147483647,二进制位如下:0111 1111 1111 1111 1111 1111 1111 1111
当使用Integer的最大值进行循环时,要注意:
举例:
for(int i=0;i<=Integer.MAX_VALUE;i++){
temp = i;
}
这段代码会陷入死循环,只有当i比int最大值大的时候才能结束循环,可问题i本身就是int类型的,它怎么可能比int的最大值还大!!!
for循环运行到 i = Integer.MAX_VALUE ,即 i = 2147483647,i再加1,就变成了-2147483648
因此,此题进行溢出判断时,res应为long类型而不是int。
/*
要点:1. 异常输入和0输入的区分,设置一个全局变量
2. 正负号的处理
3. 溢出处理
*/
public class Solution {
public int StrToInt(String str) {
long res = 0;
int symbol = 1;
boolean isInvalid = false; // 区分非法输入和0
char[] arr = str.toCharArray();
int n = arr.length;
if (n <= 0 || str == null) {
isInvalid = true;
return 0;
}
if (arr[0] == '-')
symbol = -1;
for (int i = (arr[0] == '-' || arr[0] == '+') ? 1 : 0; i < n; i++) {
if (!('0' <= arr[i] && arr[i] <= '9')) {
isInvalid = true;
return 0;
}
res = (res << 1) + (res << 3) + (arr[i] & 0xf); //res=res*10+arr[i]-'0'
// 溢出判断
if((symbol == 1 && res>Integer.MAX_VALUE) ||
(symbol == -1 && res*(-1) < Integer.MIN_VALUE)) {
isInvalid = true;
return 0;
}
}
return (int)(res * symbol);
}
}
题目描述:
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
思路
位运算实现加法:
1.按位或(|):如果两个相应的二进制位有一个为1,则该结果为1,否则为0。
2.按位异或(^):如果两个相应的二进制位值不同则为1,否则为0。
3.按位与(&):如果两个相应的二进制位都为1,则该位的结果值为1。否则为0。
对于二进制的竖式计算中存在两个逻辑,分别是 进位 和 合并 。
进位指对列同为 1 的执行逢二进一,合并指对列不同数值的执行为 1 。
以上逻辑将分别用二进制运算来取代,前者可以视为按位与(&)和左移一位,后者可以视为两数执行按位或。
在位运算中 ^ 和 & 分别产生以下结果:
数学意义为,模拟逢二进一的相加结果,保留未进位的结果,这是为了模拟竖式计算的加法结果。
(因为同为 1 则进位,同为 0 无结果,最终结果都是要将其变为 0 ,则可以理解为进位后的结果。)
数学意义为,标记所有进位位置,对其执行 << 1 即可得到进位的结果。
以 1011 + 1001 为例:
根据竖式运算可知:
合并(^)结果后检查是否为可进位(&),直到最终不存在可进位的数,则说明加法结束。
总结:
两个数异或:相当于每一位相加,而不考虑进位;
两个数相与,并左移一位:相当于求得进位;
直到最终不存在可进位的数,则说明加法结束。
完整代码:
public class Solution {
public int Add(int num1,int num2) {
while( num2!=0 ){
int sum = num1 ^ num2;
int carray = (num1 & num2) << 1;
num1 = sum;
num2 = carray;
}
return num1;
}
}
题目描述
求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
一,利用利用短路 && 来实现 if的功能;
二,利用递归来实现循环while的功能。
思路:
1.需利用逻辑与的短路特性实现递归终止。
2.当n==0时,(n>0)&&((sum+=Sum_Solution(n-1))>0)只执行前面的判断,为false,然后直接返回0;
3.当n>0时,执行sum+=Sum_Solution(n-1),实现递归计算Sum_Solution(n)。
完整代码:
public class Solution {
public int Sum_Solution(int n) {
int sum = n;
if(n>0)
sum+=Sum_Solution(n-1);
return sum;
}
}
背景知识
按位运算符有6个
& 按位与 | 按位或 ^ 按位异或 ~ 取反 >> 右移 << 左移
移位运算符移位运算符把位按指定的值向左或向右移动
<< 向左移动 而 >> 向右移动,超过的位将丢失,而空出的位则补0
如 0 1 0 0 0 0 1 1(十进制67) 向左移动两位67<<2将变成
0 0 0 0 1 1 0 0 (十进制12)当然如果用java代码写,由于是32位,不会溢出,结果是268
向右移动两位67>>2则是
0 0 0 1 0 0 0 0(十进制16)
下面介绍一些具体的应用
前面提到2向前移动1位变成4 利用这个特性可以做乘法运算(在不考虑溢出和符号位的情况下)
2 << 1 =4
3 << 1 = 6
4 << 1 = 8
同理 >> 则可以做除法运算
任何小数 把它 >> 0可以取整
如3.14159 >> 0 = 3;
思路
利用Math实现n(n+1),进而求出n(n+1)/2
完整代码
public class Solution {
public int Sum_Solution(int n) {
return (int) (Math.pow(n, 2) + n) >> 1;
}
}
题目描述
每年六一儿童节,牛客都会准备一些小礼物去看望孤儿院的小朋友,今年亦是如此。HF作为牛客的资深元老,自然也准备了一些小游戏。其中,有个游戏是这样的:首先,让小朋友们围成一个大圈。然后,他随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中,从他的下一个小朋友开始,继续0…m-1报数…这样下去…直到剩下最后一个小朋友,可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!_)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
如果没有小朋友,请返回-1
思路:
此题为约瑟夫问题,详细解析参考https://blog.csdn.net/u011500062/article/details/72855826
//用数组来模拟环
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if(n<1||m<1) return -1;
int[] array = new int[n];
int i = -1,step = 0, count = n;
while(count>0){ //跳出循环时将最后一个元素也设置为了-1
i++; //指向上一个被删除对象的下一个元素。
if(i>=n) i=0; //模拟环。
if(array[i] == -1) continue; //跳过被删除的对象。
step++; //记录已走过的。
if(step==m) { //找到待删除的对象。
array[i]=-1;
step = 0;
count--;
}
}
return i;//返回跳出循环时的i,即最后一个被设置为-1的元素
}
}
import java.util.LinkedList;
public class Solution {
public int LastRemaining_Solution(int n, int m) {
if (m == 0 || n == 0) {
return -1;
}
LinkedList<Integer> list = new LinkedList<Integer>();
for (int i = 0; i < n; i ++) {
list.add(i);
}
int bt = 0;
while (list.size() > 1) {
/*第一次删掉的位置是从0开始数m-1个位置,以后每次从删掉的下一个节点开始取,所以
每次要在bt的索引处加上m-1(每次删除一个元素后的环移m-1位得到下一个删除元素相当
于删除前的环移动m位的元素),因为是环,所以加了以后对链表长度取余*/
bt = (bt + m - 1) % list.size();
list.remove(bt);
}
return list.size() == 1 ? list.get(0) : -1;
}
}
public class Solution {
/*约瑟夫问题,求递推公式,每轮的序列中最后出序列的数都是同一个*/
public int LastRemaining_Solution(int n,int m) {
if(n < 1 || m < 1)
return -1;
if(n == 1){
return 0;
}
return (LastRemaining_Solution(n-1, m)+m)%n;
}
}
public class Solution {
public int LastRemaining_Solution(int n,int m) {
if(n==0) return -1;
int s=0;
for(int i=2;i<=n;i++){
s=(s+m)%i;
}
return s;
}
}
题目描述:
LL今天心情特别好,因为他去买了一副扑克牌,发现里面居然有2个大王,2个小王(一副牌原本是54张_)…他随机从中抽出了5张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心A,黑桃3,小王,大王,方片5”,“Oh My God!”不是顺子…LL不高兴了,他想了想,决定大\小 王可以看成任何数字,并且A看作1,J为11,Q为12,K为13。上面的5张牌就可以变成“1,2,3,4,5”(大小王分别看作2和4),“So Lucky!”。LL决定去买体育彩票啦。 现在,要求你使用这幅牌模拟上面的过程,然后告诉我们LL的运气如何, 如果牌能组成顺子就输出true,否则就输出false。为了方便起见,你可以认为大小王是0。
思路:
max 记录 最大值
min 记录 最小值
min ,max 都不记0
满足条件 1 max - min <5
2 除0外没有重复的数字(牌)
3 数组长度 为5
import java.util.*;
public class Solution {
public boolean isContinuous(int [] numbers) {
int[] count=new int[14];
count[0]=-5;
int len=numbers.length;
int max=-1;
int min=14;
if(len==0) return false;
for(int i=0;i<len;i++)
{
count[numbers[i]]++;
if(numbers[i]==0)
continue;
if(count[numbers[i]]>1)
return false;
if(numbers[i]>max)
max=numbers[i];
if(numbers[i]<min)
min=numbers[i];
}
if(max-min<5)
return true;
return false;
}
}
思路:
1、排序
2、计算所有相邻数字间隔总数
3、计算0的个数
4、如果2、3相等,就是顺子
5、如果出现对子,则不是顺子
import java.util.Arrays;
public class Solution {
public boolean isContinuous(int[] numbers) {
int numOfZero = 0;
int numOfInterval = 0;
int length = numbers.length;
if(length == 0){
return false;
}
Arrays.sort(numbers);
for (int i = 0; i < length - 1; i++) {
// 计算癞子数量
if (numbers[i] == 0) {
numOfZero++;
continue;
}
// 对子,直接返回
if (numbers[i] == numbers[i + 1]) {
return false;
}
numOfInterval += numbers[i + 1] - numbers[i] - 1;
}
if (numOfZero >= numOfInterval) {
return true;
}
return false;
}
}
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
完整代码:
import java.util.*;
public class Solution {
public String ReverseSentence(String str) {
if (str == null || str.length() == 0 ) {
return str;
}
char[] chars=str.toCharArray();
reverseString(chars,0,str.length()-1);
int blank=-1;
int nextblank=-1;
for(int i=0;i<str.length();i++)
{
if(chars[i]==' ')
{
nextblank=i;
reverseString(chars,blank+1,nextblank-1);
blank=nextblank;
}
}
reverseString(chars,blank+1,str.length()-1);
return new String(chars);
}
public void reverseString(char[] ch, int start, int end) {
while (start < end) {
char temp = ch[start];
ch[start] = ch[end];
ch[end] = temp;
start++;
end--;
}
}
}
题目描述
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
思路:
1.先翻转前半部分
2.再翻转后半部分
3.再对字符串整个进行翻转
考点:不使用库对字符串进行灵活的翻转
public class Solution {
public String LeftRotateString(String str,int n) {
if (str == null || str.length() == 0 || n <= 0) {
return str;
}
if (n >= str.length()) {
n = n % str.length();
}
char[] ch = str.toCharArray();
reverseString(ch, 0, n - 1);
reverseString(ch, n, str.length() - 1);
reverseString(ch, 0, str.length() - 1);
return new String(ch);
}
public void reverseString(char[] ch, int start, int end) {
while (start < end) {
char temp = ch[start];
ch[start] = ch[end];
ch[end] = temp;
start++;
end--;
}
}
}
题目描述
输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的。
输出描述:
对应每个测试案例,输出两个数,小的先输出。
import java.util.*;
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> list=new ArrayList<>();
if (array == null || array.length == 0)
return list;
int product=10000;
for(int i=0;i<array.length;i++)
{
for(int j=i+1;j<array.length;j++)
{
if(array[i]+array[j]==sum)
{
if(array[i]*array[j]<product)
{
product=array[i]*array[j];
while(!list.isEmpty())
{
list.remove(0);
}
list.add(array[i]);
list.add(array[j]);
}
}
}
}
return list;
}
}
思路:
假设ab<=(a+1)(b-1),左右夹逼法,乘积最小的肯定是第一组,只需要2个指针
1.left开头,right指向结尾
2.如果和小于sum,说明太小了,left右移寻找更大的数
3.如果和大于sum,说明太大了,right左移寻找更小的数
4.和相等,把left和right的数返回
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
int left = 0, right = array.length - 1;
ArrayList<Integer> list = new ArrayList<>();
while(left < right){
if(array[left] + array[right] < sum){
left++;
}else if(array[left] + array[right] > sum){
right--;
}else{
list.add(array[left]);
list.add(array[right]);
break;
}
}
return list;
}
}
题目描述:
小明很喜欢数学,有一天他在做数学作业时,要求计算出9~16的和,他马上就写出了正确答案是100。但是他并不满足于此,他在想究竟有多少种连续的正数序列的和为100(至少包括两个数)。没多久,他就得到另一组连续正数和为100的序列:18,19,20,21,22。现在把问题交给你,你能不能也很快的找出所有和为S的连续正数序列? Good Luck!
输出描述:
输出所有和为S的连续正数序列。序列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序
import java.util.ArrayList;
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> res=new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> list=new ArrayList<>();
for(int i=1;i<=sum/2+1;i++)
{
int Sum=0;
for(int j=i;Sum<sum;j++)
{
Sum+=j;
list.add(j);
}
if(list.size()==1)
return res;
if(Sum==sum){
res.add(list);
}
list=new ArrayList<>();
}
return res;
}
}
import java.util.ArrayList;
public class Solution {
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
//存放结果
ArrayList<ArrayList<Integer> > result = new ArrayList<>();
//两个起点,相当于动态窗口的两边,根据其窗口内的值的和来确定窗口的位置和大小
int plow = 1,phigh = 2;
while(phigh > plow){
//由于是连续的,差为1的一个序列,那么求和公式是(a0+an)*n/2
int cur = (phigh + plow) * (phigh - plow + 1) / 2;
//相等,那么就将窗口范围的所有数添加进结果集
if(cur == sum){
ArrayList<Integer> list = new ArrayList<>();
for(int i=plow;i<=phigh;i++){
list.add(i);
}
result.add(list);
plow++; // 一定不要忘记这行
//如果当前窗口内的值之和小于sum,那么右边窗口右移一下
}else if(cur < sum){
phigh++;
}else{
//如果当前窗口内的值之和大于sum,那么左边窗口右移一下
plow++;
}
}
return result;
}
}
题目描述
一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param array int整型一维数组
* @return int整型一维数组
*/
public int[] FindNumsAppearOnce (int[] array) {
int[] res = new int[2];
HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i = 0; i < array.length; i++){
if(map.containsKey(array[i])){
map.put(array[i], map.get(array[i]) + 1);
}else{
map.put(array[i], 1);
}
}
Set<Integer> set = map.keySet();
Iterator<Integer> keys = set.iterator();
int index = 0;
while(keys.hasNext()){
Integer key = keys.next();
if(map.get(key) == 1){
res[index++] = key;
}
}
return res;
}
}
import java.util.*;
//num1,num2分别为长度为1的数组。传出参数
//将num1[0],num2[0]设置为返回结果
public class Solution {
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
Arrays.sort(array);
int i=0;
int count=0;
while(i<array.length-1)
{
if(array[i]==array[i+1]){
i=i+2;
}else{
if(count==0){
num1[0]=array[i];
count++;
}else{
num2[0]=array[i];
}
i=i+1;
}
}
if(i==array.length-1)//若只出现一次的是最后一个数
{
if(count==1)
num2[0]=array[i];
}
}
}
思路:
首先:位运算中异或的性质:两个相同数字异或=0,一个数和0异或还是它本身。
当只有一个数出现一次时,我们把数组中所有的数,依次异或运算,最后剩下的就是落单的数,因为成对儿出现的都抵消了。
依照这个思路,我们来看两个数(我们假设是AB)出现一次的数组。我们首先还是先异或,剩下的数字肯定是A、B异或的结果,这个结果的二进制中的1,表现的是A和B的不同的位。我们就取第一个1所在的位数,假设是第3位,接着把原数组分成两组,分组标准是第3位是否为1。如此,相同的数肯定在一个组,因为相同数字所有位都相同,而不同的数,肯定不在一组。然后把这两个组按照最开始的思路,依次异或,剩余的两个结果就是这两个只出现一次的数字。
完整代码
public class Solution {
public void FindNumsAppearOnce(int[] array, int[] num1, int[] num2) {
int length = array.length;
if(length == 2){
num1[0] = array[0];
num2[0] = array[1];
return;
}
int bitResult = 0;
for(int i = 0; i < length; ++i){
bitResult ^= array[i];
}
int index = findFirst1(bitResult);
for(int i = 0; i < length; ++i){
if(isBit1(array[i], index)){
num1[0] ^= array[i];
}else{
num2[0] ^= array[i];
}
}
}
private int findFirst1(int bitResult){
int index = 0;
while(((bitResult & 1) == 0) && index < 32){
bitResult >>= 1;
index++;
}
return index;
}
private boolean isBit1(int target, int index){
return ((target >> index) & 1) == 1;
}
}
题目描述
输入一棵二叉树,判断该二叉树是否是平衡二叉树。
在这里,我们只需要考虑其平衡性,不需要考虑其是不是排序二叉树。
我的代码
public class Solution {
public boolean IsBalanced_Solution(TreeNode root) {
if(root == null){
return true;
}
int leftheight = height(root.left);
int rightheight = height(root.right);
if(leftheight - rightheight > 1 || rightheight - leftheight > 1){
return false;
}
return IsBalanced_Solution(root.left) && IsBalanced_Solution(root.right);
}
public int height(TreeNode root) {
if(root == null){
return 0;
}
int leftheight = height(root.left);
int rightheight = height(root.right);
return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
}
}
题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
public int TreeDepth(TreeNode root) {
Queue<TreeNode> q = new LinkedList<>();
if(root == null){
return 0;
}
q.offer(root);
int index = 0;
int depth = 1;
while(!q.isEmpty()){
TreeNode node = q.poll();
if(node.left != null){
q.offer(node.left);
index++;
}
if(node.right != null){
q.offer(node.right);
index++;
}
if(!q.isEmpty() && index == q.size()){
depth++;
index = 0;
}
}
return depth;
}
}
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public int TreeDepth(TreeNode root) {
if(root==null)
return 0;
int right=TreeDepth(root.right);
int left=TreeDepth(root.left);
return right>left ? right+1 : left+1;
}
}
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
public int TreeDepth(TreeNode root) {
ArrayList<TreeNode> q=new ArrayList<>();
q.add(root);
int index=0;
int depth=0;
if(root==null){
return 0;
}
while(!q.isEmpty()){
TreeNode node=q.remove(0);
if(node.left!=null){
q.add(node.left);
index++;
}
if(node.right!=null){
q.add(node.right);
index++;
}
if(index==q.size()){
depth++;
index=0;
}
}
return depth;
}
}
题目描述
统计一个数字在升序数组中出现的次数。
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int count=0;
int i=0;
while(i<array.length)
{
while(array[i]==k)
{
count++;
i++;
if(i==array.length)
break;
}
i++;
}
return count;
}
}
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int left = GetNumberOfK(array, k, 0, array.length-1, true);
int right = GetNumberOfK(array, k, 0, array.length-1, false);
if(left == -1 || right == -1){
return 0;
}
return right-left+1;
}
public int GetNumberOfK(int[] array, int k, int left, int right, Boolean isleft){
int index = -1;
while(left <= right){
int mid = (left + right) / 2;
if(k > array[mid]){
left = mid + 1;
}else if(k < array[mid]){
right = mid - 1;
}else{
index = mid;
if(isleft){
right = mid - 1;
}else{
left = mid + 1;
}
}
}
return index;
}
}
public class Solution {
public int GetNumberOfK(int [] array , int k) {
int length = array.length;
if(length == 0){
return 0;
}
int firstK = getFirstK(array, k, 0, length-1);
int lastK = getLastK(array, k, 0, length-1);
if(firstK != -1 && lastK != -1){
return lastK - firstK + 1;
}
return 0;
}
//递归写法
private int getFirstK(int [] array , int k, int start, int end){
if(start > end){
return -1;
}
int mid = (start + end) >> 1;
if(array[mid] > k){
return getFirstK(array, k, start, mid-1);
}else if (array[mid] < k){
return getFirstK(array, k, mid+1, end);
}else if(mid-1 >=0 && array[mid-1] == k){
return getFirstK(array, k, start, mid-1);
}else{
return mid;
}
}
private int getLastK(int [] array , int k, int start, int end){
if(start > end){
return -1;
}
int mid = (start + end) >> 1;
if(array[mid] > k){
return getLastK(array, k, start, mid-1);
}else if (array[mid] < k){
return getLastK(array, k, mid+1, end);
}else if(mid+1 < array.length && array[mid+1] == k){
return getLastK(array, k, mid+1, end);
}else{
return mid;
}
}
}
题目描述
输入两个链表,找出它们的第一个公共结点。(注意因为传入数据是链表,所以错误测试数据的提示是用其他方式显示的,保证传入数据是正确的)
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
/*思路:如果存在共同节点的话,那么从该节点,两个链表之后的元素都是相同的。
也就是说两个链表从尾部往前到某个点,节点都是一样的。
我们可以用两个栈分别来装这两条链表。一个一个比较出来的值。
找到第一个相同的节点。*/
import java.util.Stack;
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
if (pHead1 == null || pHead2 == null) {
return null;
}
Stack<ListNode> stack1 = new Stack<>();
Stack<ListNode> stack2 = new Stack<>();
while (pHead1 != null) {
stack1.push(pHead1);
pHead1 = pHead1.next;
}
while (pHead2 != null) {
stack2.push(pHead2);
pHead2 = pHead2.next;
}
ListNode commonListNode = null;
while (!stack1.isEmpty() && !stack2.isEmpty() && stack1.peek() == stack2.peek() ) {
stack1.pop();
commonListNode = stack2.pop();
}
return commonListNode;
}
}
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
import java.util.HashMap;
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode current1 = pHead1;
ListNode current2 = pHead2;
HashMap<ListNode, Integer> hashMap = new HashMap<ListNode, Integer>();
while (current1 != null) {
hashMap.put(current1, null);
current1 = current1.next;
}
while (current2 != null) {
if (hashMap.containsKey(current2))
return current2;
current2 = current2.next;
}
return null;
}
}
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode current1 = pHead1;// 链表1
ListNode current2 = pHead2;// 链表2
if (pHead1 == null || pHead2 == null)
return null;
// 两连表的长度差
int length1 = getLength(current1);
int length2 = getLength(current2);
// 如果链表1的长度大于链表2的长度
if (length1 >= length2) {
int len = length1 - length2;
// 先遍历链表1,遍历的长度就是两链表的长度差
while (len > 0) {
current1 = current1.next;
len--;
}
}
// 如果链表2的长度大于链表1的长度
else if (length1 < length2) {
int len = length2 - length1;
// 先遍历链表1,遍历的长度就是两链表的长度差
while (len > 0) {
current2 = current2.next;
len--;
}
}
//开始齐头并进,直到找到第一个公共结点
while(current1!=current2){
current1=current1.next;
current2=current2.next;
}
return current1;
}
// 求指定链表的长度
public static int getLength(ListNode pHead) {
int length = 0;
ListNode current = pHead;
while (current != null) {
length++;
current = current.next;
}
return length;
}
}
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
示例:
输入:
1,2,3,4,5,6,7,0
输出:
7
思路:
看到这个题目,我们的第一反应是顺序扫描整个数组。每扫描到一个数组的时候,逐个比较该数字和它后面的数字的大小。如果后面的数字比它小,则这两个数字就组成了一个逆序对。假设数组中含有n个数字。由于每个数字都要和O(n)这个数字比较,因此这个算法的时间复杂度为O(n^2)。
我们以数组{7,5,6,4}为例来分析统计逆序对的过程。每次扫描到一个数字的时候,我们不拿ta和后面的每一个数字作比较,否则时间复杂度就是O(n^2),因此我们可以考虑先比较两个相邻的数字。
如图5.2(a)和(b)中,我们先把数组分解成两个长度为2的子数组,再把这两个子数组分别拆成两个长度为1的子数组。接下来一边合并相邻的子数组,一边统计逆序对的数目。在第一对长度为1的子数组{7}、{5}中7大于5,因此(7,5)组成一个逆序对。同样在第二对长度为1的子数组{6}、{4}中也有逆序对(6,4)。由于我们已经统计了这两对子数组内部的逆序对,因此需要把这两对子数组 排序 如图5.2(c)所示, 以免在以后的统计过程中再重复统计。
我们先用两个指针分别指向两个子数组的末尾,并每次比较两个指针指向的数字。如果第一个子数组中的数字大于第二个数组中的数字,则构成逆序对,并且逆序对的数目等于第二个子数组中剩余数字的个数,如下图(a)和(c)所示。如果第一个数组的数字小于或等于第二个数组中的数字,则不构成逆序对,如图b所示。每一次比较的时候,我们都把较大的数字从后面往前复制到一个辅助数组中,确保 辅助数组(记为copy) 中的数字是递增排序的。在把较大的数字复制到辅助数组之后,把对应的指针向前移动一位,接下来进行下一轮比较。
过程:先把数组分割成子数组,先统计出子数组内部的逆序对的数目,然后再统计出两个相邻子数组之间的逆序对的数目。在统计逆序对的过程中,还需要对数组进行排序。如果对排序算法很熟悉,我们不难发现这个过程实际上就是归并排序。
完整代码:
public class Solution {
public int InversePairs(int [] array) {
if(array==null||array.length==0)
{
return 0;
}
int[] copy = new int[array.length];
for(int i=0;i<array.length;i++)
{
copy[i] = array[i];
}
int count = InversePairsCore(array,copy,0,array.length-1);//数值过大求余
return count;
}
private int InversePairsCore(int[] array,int[] copy,int low,int high)
{
if(low==high)
{
return 0;
}
int mid = (low+high)>>1;
/*递归调用时将copy和array两个数组颠倒,对已经统计了逆序对的数组,
我们需要对其排好序,以避免在以后的统计过程中再次重复统计,所以copy
数组就是起到这个作用,当然,这里的有序只是“局部有序”,整体来看还是
无序的。既然copy数组是“有序”的,下一次就直接在这个基础上进行统计就
可以,原始数据data用来充当原来copy数组的角色来保存“更加有序”的数组,
这样进行递归,就节省了数据来回拷贝所浪费的时间。*/
int leftCount = InversePairsCore(copy,array,low,mid)%1000000007;
int rightCount = InversePairsCore(copy,array,mid+1,high)%1000000007;
int count = 0;
int i=mid;
int j=high;
int locCopy = high;
while(i>=low&&j>mid)
{
if(array[i]>array[j])
{
count += j-mid;
copy[locCopy--] = array[i--];
if(count>=1000000007)//数值过大求余
{
count%=1000000007;
}
}
else
{
copy[locCopy--] = array[j--];
}
}
for(;i>=low;i--)
{
copy[locCopy--]=array[i];
}
for(;j>mid;j--)
{
copy[locCopy--]=array[j];
}
/*若上述递归的两行代码未将array与copy互换则需要这步拷贝过程
for(int s=low;s<=high;s++)
{
array[s] = copy[s];
}
*/
return (leftCount+rightCount+count)%1000000007;
}
}
我的方法:
public class Solution {
public int InversePairs(int [] array) {
long count = 0;
for(int i = 0; i < array.length; i++){
for(int j = i+1; j < array.length; j++){
if(array[j] < array[i]){
count++;
}
}
}
return (int)(count % 1000000007);
}
}
题目描述
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写).(从0开始计数)
思路:
主要还是hash,利用每个字母的ASCII码作hash来作为数组的index。首先用一个58长度的数组来存储每个字母出现的次数,为什么是58呢,主要是由于A-Z对应的ASCII码为65-90,a-z对应的ASCII码值为97-122,从65开始到122结束(小a到大z,算上了几个没用的字符)一共有58位,创建一个数组不可能从游标65开始吧?都是从零开始的,所以如果游标要跟字符对应上的话字符就要减65。每个字母的index=int(word)-65,比如g=103-65=38,而数组中具体记录的内容是该字母出现的次数,最终遍历一遍字符串,找出第一个数组内容为1的字母就可以了,时间复杂度为O(n)
完整代码:
public class Solution {
public int FirstNotRepeatingChar(String str) {
int[] words = new int[58];
for(int i=0;i<str.length();i++){
words[((int)str.charAt(i))-65] += 1;
}
for(int i=0;i<str.length();i++){
if(words[((int)str.charAt(i))-65]==1)
return i;
}
return -1;
}
}
我的方法
import java.util.*;
public class Solution {
public int FirstNotRepeatingChar(String str) {
HashMap<Character,Integer> map = new HashMap<>();
for(int i = 0; i < str.length(); i++){
if(map.containsKey(str.charAt(i))){
map.put(str.charAt(i), map.get(str.charAt(i)) + 1);
}else{
map.put(str.charAt(i), 1);
}
}
for(int i = 0; i <str.length(); i++){
if(map.get(str.charAt(i)) == 1){
return i;
}
}
return -1;
}
}
题目描述
把只包含质因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含质因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路:
通俗易懂的解释:
首先从丑数的定义我们知道,一个丑数的因子只有2,3,5,那么丑数p = 2 ^ x * 3 ^ y * 5 ^ z,换句话说一个丑数一定由另一个丑数乘以2或者乘以3或者乘以5得到,那么我们从1开始乘以2,3,5,就得到2,3,5三个丑数,在从这三个丑数出发乘以2,3,5就得到4,6,10,6,9,15,10,15,25九个丑数,我们发现这种方***得到重复的丑数,而且我们题目要求第N个丑数,这样的方法得到的丑数也是无序的。那么我们可以维护三个队列:
(1)丑数数组: 1
乘以2的队列:2
乘以3的队列:3
乘以5的队列:5
选择三个队列头最小的数2加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(2)丑数数组:1,2
乘以2的队列:4
乘以3的队列:3,6
乘以5的队列:5,10
选择三个队列头最小的数3加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(3)丑数数组:1,2,3
乘以2的队列:4,6
乘以3的队列:6,9
乘以5的队列:5,10,15
选择三个队列头里最小的数4加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(4)丑数数组:1,2,3,4
乘以2的队列:6,8
乘以3的队列:6,9,12
乘以5的队列:5,10,15,20
选择三个队列头里最小的数5加入丑数数组,同时将该最小的数乘以2,3,5放入三个队列;
(5)丑数数组:1,2,3,4,5
乘以2的队列:6,8,10,
乘以3的队列:6,9,12,15
乘以5的队列:10,15,20,25
选择三个队列头里最小的数6加入丑数数组,但我们发现,有两个队列头都为6,所以我们弹出两个队列头,同时将12,18,30放入三个队列;
……………………
我们没有必要维护三个队列,只需要记录三个指针显示到达哪一步;“|”表示指针,arr表示丑数数组;
(1)1
|2
|3
|5
目前指针指向0,0,0,队列头arr[0] * 2 = 2, arr[0] * 3 = 3, arr[0] * 5 = 5
(2)1 2
2 |4
|3 6
|5 10
目前指针指向1,0,0,队列头arr[1] * 2 = 4, arr[0] * 3 = 3, arr[0] * 5 = 5
(3)1 2 3
2 |4 6
3 |6 9
|5 10 15
目前指针指向1,1,0,队列头arr[1] * 2 = 4, arr[1] * 3 = 6, arr[0] * 5 = 5
………………
完整代码
import java.util.*;
public class Solution {
public int GetUglyNumber_Solution(int index) {
if(index<=0)
return 0;
ArrayList<Integer> list = new ArrayList<Integer>();
//add进第一个丑数1
list.add(1);
//三个下标用于记录丑数的位置
int i2=0,i3=0,i5=0;
while(list.size()<index){
//三个数都是可能的丑数,取最小的放进丑数数组里面
int n2=list.get(i2)*2;
int n3=list.get(i3)*3;
int n5=list.get(i5)*5;
int min = Math.min(n2,Math.min(n3,n5));
list.add(min);
if(min==n2)
i2++;
if(min==n3)
i3++;
if(min==n5)
i5++;
}
return list.get(list.size()-1);
}
}
import java.util.*;
public class Solution {
public int GetUglyNumber_Solution(int index)
{
if (index<=0) return 0;
int[] k=new int[index];
k[0]=1;
int t2=0,t3=0,t5=0;
for (int i=1;i<index;i++) {
k[i]=Math.min(k[t2]*2,Math.min(k[t3]*3,k[t5]*5));
// 每个已经确定的丑数都分别*2、*3、*5,再取最小的放到丑数数组。因为有可能大丑数*2还大于小丑数*5,因此要在丑数数组中记录*2、*3、*5的都在丑数数组中走到哪个位置了(每个数都要*2、*3、*5)
if (k[i]==k[t2]*2) t2++;
if (k[i]==k[t3]*3) t3++;
if (k[i]==k[t5]*5) t5++;
}
return k[index-1];
}
}
题目描述
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
完整代码
import java.util.*;
/*
* 解题思路:
* 先将整型数组转换成String数组,然后将String数组排序,最后将排好序的字符串数组拼接出来。关键就是制定排序规则。
* 排序规则如下:
* 若ab > ba 则 a > b,
* 若ab < ba 则 a < b,
* 若ab = ba 则 a = b;
* 解释说明:
* 比如 "3" < "31"但是 "331" > "313",所以要将二者拼接起来进行比较
*/
public class Solution {
public String PrintMinNumber(int [] numbers) {
if(numbers == null || numbers.length == 0) return "";
int len = numbers.length;
String[] str = new String[len];
StringBuilder sb = new StringBuilder();//StringBuilder是一个可变的字符序列
for(int i = 0; i < len; i++){
str[i] = String.valueOf(numbers[i]);
}
Arrays.sort(str,new Comparator<String>(){
@Override
public int compare(String s1, String s2) {
String c1 = s1 + s2;
String c2 = s2 + s1;
return c1.compareTo(c2);
}
});
for(int i = 0; i < len; i++){
sb.append(str[i]);
}
return sb.toString();
}
}
题目描述
求出1 ~ 13的整数中1出现的次数,并算出100 ~ 1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
思路:
类似这样的问题,我们可以通过归纳总结的方法
首先可以先分类:
个位:
我们知道在个位数上,1会每隔10出现一次,例如1、11、21等等,我们发现以10为一个阶梯的话,每一个完整的阶梯里面都有一个1,例如数字22,按照10为间隔来分三个阶梯,在完整阶梯0-9,10-19之中都有一个1,但是19之后有一个不完整的阶梯,我们需要去判断这个阶梯中会不会出现1,易推断知,如果最后这个露出来的部分小于1,则不可能出现1(这个归纳换做其它数字也成立)。
我们可以归纳个位上1出现的个数为:
n/10 * 1+(n%10!=0 ? 1 : 0)
十位:
现在说十位数,十位数上出现1的情况应该是10-19,依然沿用分析个位数时候的阶梯理论,我们知道10-19这组数,每隔100出现一次,这次我们的阶梯是100,例如数字317,分析有阶梯0-99,100-199,200-299三段完整阶梯,每一段阶梯里面都会出现10次1(从10-19),最后分析露出来的那段不完整的阶梯。我们考虑如果露出来的数大于19,那么直接算10个1就行了,因为10-19肯定会出现;如果小于10,那么肯定不会出现十位数的1;如果在10-19之间的,我们计算结果应该是k - 10 + 1。例如我们分析300-317,17个数字,1出现的个数应该是17-10+1=8个。
那么现在可以归纳:十位上1出现的个数为:
设k = n % 100,即为不完整阶梯段的数字
归纳式为:(n / 100) * 10 + (if(k > 19) 10 else if(k < 10) 0 else k - 10 + 1)
百位
现在说百位1,我们知道在百位,100-199都会出现百位1,一共出现100次,阶梯间隔为1000,100-199这组数,每隔1000就会出现一次。这次假设我们的数为2139。跟上述思想一致,先算阶梯数 * 完整阶梯中1在百位出现的个数,即n/1000 * 100得到前两个阶梯中1的个数,那么再算漏出来的部分139,沿用上述思想,不完整阶梯数k199,得到100个百位1,100<=k<=199则得到k - 100 + 1个百位1。
那么继续归纳百位上出现1的个数:
设k = n % 1000
归纳式为:(n / 1000) * 100 + (if(k >199) 100 else if(k < 100) 0 else k - 100 + 1)
后面的依次类推…
再次回顾个位
我们把个位数上算1的个数的式子也纳入归纳式中
k = n % 10
个位数上1的个数为:n / 10 * 1 + (if(k > 1) 1 else if(k < 1) 0 else k - 1 + 1)
完美!归纳式看起来已经很规整了。 来一个更抽象的归纳,设i为计算1所在的位数,i=1表示计算个位数的1的个数,10表示计算十位数的1的个数等等。
k = n % (i * 10)
count(i) = (n / (i * 10)) * i + (if(k > i * 2 - 1) i else if(k < i) 0 else k - i + 1)
好了,这样从10到10的n次方的归纳就完成了。
sum1 = sum(count(i)),i = Math.pow(10, j), 0<=j<=log10(n)
但是有一个地方值得我们注意的,就是代码的简洁性来看,有多个ifelse不太好,能不能进一步简化呢? 我们可以把后半段简化成这样,我们不去计算i * 2 - 1了,我们只需保证k - i + 1在[0, i]区间内就行了,最后后半段可以写成这样
min(max((n mod (i*10))−i+1,0),i)
完整代码:
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
if(n <= 0)
return 0;
int count = 0;
for(int i = 1; i <= n; i *= 10){
count+=n/(i*10)*i+Math.min( Math.max( n%(i*10)-i+1,0 ),i );
}
return count;
}
}
完成比完美重要,巧解法实在是记不住。
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int sum = 0;
for(int i = 0; i <= n; i++){
int temp = NumberOf1(i);
sum += temp;
}
return sum;
}
public int NumberOf1(int n){
int sum = 0;
while(n != 0){
if(n % 10 == 1){
sum++;
}
n = n/10;
}
return sum;
}
}
题目描述
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和,你会不会被他忽悠住?(子向量的长度至少是1)
/*
算法时间复杂度O(n)
用total记录累计值,maxSum记录和最大
基于思想:对于一个数A,若是A的左边累计数非负,那么加上A能使得值不小于A,认为累计值对
整体和是有贡献的。如果前几项累计值负数,则认为有害于总和,total记录当前值重新开始。
此时 若和大于maxSum 则用maxSum记录下来
*/
public class Solution {
public int FindGreatestSumOfSubArray(int[] array){
if(array.length==0)
return 0;
int total=array[0],maxSum=array[0];
for(int i=1;i<array.length;i++){
if(total>=0)
total+=array[i];
else
total=array[i];//若前几个连续的数的和小于0,则从当前数字作为所求连续子数组和的第一个数重新开始
if(total>maxSum)
maxSum=total;
}
return maxSum;
}
}
思路:
F(i):以array[i]为末尾元素的子数组的和的最大值,子数组的元素的相对位置不变
F(i)=max(F(i-1)+array[i] , array[i])
res:所有子数组的和的最大值
res=max(res,F(i))
如数组[6, -3, -2, 7, -15, 1, 2, 2]
初始状态:
F(0)=6
res=6
i=1:
F(1)=max(F(0)-3,-3)=max(6-3,-3)=3
res=max(F(1),res)=max(3,6)=6
i=2:
F(2)=max(F(1)-2,-2)=max(3-2,-2)=1
res=max(F(2),res)=max(1,6)=6
i=3:
F(3)=max(F(2)+7,7)=max(1+7,7)=8
res=max(F(2),res)=max(8,6)=8
i=4:
F(4)=max(F(3)-15,-15)=max(8-15,-15)=-7
res=max(F(4),res)=max(-7,8)=8
以此类推
最终res的值为8
完整代码:
public class Solution {
public int FindGreatestSumOfSubArray(int[] array) {
int res = array[0]; //记录当前所有子数组的和的最大值
int max=array[0]; //包含array[i]的连续数组最大值
for (int i = 1; i < array.length; i++) {
max=Math.max(max+array[i], array[i]);
res=Math.max(max, res);
}
return res;
}
}
题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> list=new ArrayList<>();
if(input.length==0||k>input.length)
return list;
Arrays.sort(input);
for(int i=0;i<k;i++){
list.add(input[i]);
}
return list;
}
}
/*用最大堆保存这k个数,每次只和堆顶比,如果比堆顶小,删除堆顶,新数入堆。*/
import java.util.*;
public class Solution {
public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
ArrayList<Integer> result = new ArrayList<Integer>();
int length = input.length;
if(k > length || k == 0){
return result;
}
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
for (int i = 0; i < length; i++) {
if (maxHeap.size() != k) {
maxHeap.offer(input[i]);
} else if (maxHeap.peek() > input[i]) {
maxHeap.poll();
maxHeap.offer(input[i]);
}
}
for (Integer integer : maxHeap) {
result.add(integer);
}
return result;
}
}
题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路:
首先将容器中的数字排序,则排序结束后,相同的数字都在相邻位置,直接判断数组当前位置的数字与(数组位置+数组一半长度位置)的数字是否相等,相等则输出该数字。
完整代码:
import java.util.Arrays;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array == null || array.length <= 0)
return 0;
int len = array.length / 2;
Arrays.sort(array);
for(int i = 0; i + len < array.length;i++)
if(array[i] == array[i + len])
return array[i];
return 0;
}
}
import java.util.*;
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array == null || array.length <= 0)
return 0;
HashMap <Integer,Integer> map = new HashMap <Integer,Integer>();
for(int i = 0;i < array.length;i++){
if(map.containsKey(array[i])){
map.put(array[i], map.get(array[i])+1);
}else{
map.put(array[i], 1);
}
}
/*Map中采用Entry内部类来表示一个映射项,映射项包含Key和Value。Map.Entry
里面包含getKey()和getValue()方法。Set> entrySet()该方法返
回值是这个map中各个键值对映射关系的集合。*/
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if(entry.getValue() > array.length/2)
return entry.getKey();
}
return 0;
}
}
思路:
数组中有一个数字出现的次数超过了数组长度的一半,如果把这个数组排序,那么排序之后位于数组中间的数字一定就是那个出现次数超过数组长度一般的数字。也就是说,这个数字就是统计学上的中位数。
完整代码:
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
if(array == null || array.length == 0)
return 0;
if(array.length == 1)
return array[0];
int mid = array.length >> 1;
int left = 0;
int right = array.length - 1;
//先做一次切分获取初始index,并且对数组做一次整理
int index = partition(array, 0, array.length - 1);
//如果index比mid大,说明目标应该存在于左边;反之,目标存在于右边
while(index != mid){
if(index > mid){ right = index - 1; }
else{ left = index + 1; }
index = partition(array,left,right);
}
return array[index];
}
//快速排序确定轴值
private int partition(int[] array,int first,int end){
int i=first,j=end;
while(i<j)
{
while(i<j&&array[i]<=array[j])
j--;//右侧扫描
if(i<j)
{
int temp=array[i];array[i]=array[j];array[j]=temp;
i++;
}
while(i<j&&array[i]<=array[j])
i++;
if(i<j)
{
int temp=array[i];array[i]=array[j];array[j]=temp;
j--;
}
}
return i;
}
}
思路:
我们可以考虑在遍历数组的时候保存两个值:一个是数组中的一个数字,另一个是次数。当我们遍历到下一个数字的时候,如果下一个数字和我们之前保存的数字相同,则次数加1;如果下一个数字和我们之前保存的数字不同,则次数减1。如果次数为零,那我们需要保存下一个数字,并把次数设为1。由于我们要找的数字出现的次数比其他所有数字出现的次数之和还要多,那么要找的数字肯定是最后一次把次数设为1时对应的数字。
完整代码:
public class Solution {
public int MoreThanHalfNum_Solution(int [] array) {
int vote = 1;
int mode = array[0];
for(int i = 1; i < array.length; i++){
if(vote == 0){
mode = array[i];
vote=1;
}else if(array[i] == mode){
vote++;
}else{
vote--;
}
}
//统计mode的数量是否超过了数组的一半
int amount = 0;
for(int i = 0; i < array.length; i++){
if(array[i] == mode){
amount++;
}
}
return amount > array.length / 2 ? mode : 0;
}
}
题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则按字典序打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
思路:
import java.util.*;
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> res = new ArrayList<>();
Permutation(str.toCharArray(), res, 0);
/*若不排序,会出现下述情况代码无法通过:
用例输入:abc
用例输出:["abc","acb","bac","bca","cab","cba"]
实际输出:["abc","acb","bac","bca","cba","cab"]
*/
Collections.sort(res);
return res;
}
public void Permutation(char[] str, ArrayList<String> res, int index){
//这是递归的终止条件,就是index下标已经移到char数组的末尾的时候,考虑添加这一组字符串进入结果集中
if(index == str.length - 1){
if(!res.contains(new String(str))){
res.add(new String(str));
}
}
for(int j = index; j < str.length; j++){
swap(str, index, j);
Permutation(str, res, index + 1);
swap(str, index, j);// 复位,用以恢复之前字符串顺序,达到第一位依次跟其他位交换的目的
}
}
public void swap(char[] str, int i, int j){
char temp = str[i];
str[i] = str[j];
str[j] = temp;
}
}
思路:
一个全排列可看做一个字符串,字符串可有前缀、后缀。
生成给定全排列的下一个排列.所谓一个的下一个就是这一个与下一个之间没有其他的。
这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。
[例]839647521是1–9的排列。1—9的排列最前面的是123456789,最后面的987654321,
从右向左扫描若都是增的,就到了987654321,也就没有下一个了。否则找出第一次出现下降的位置。
【例】 如何得到346987521的下一个
1.从尾部往前找第一个P(i-1) < P(i)的位置
3 4 6 <- 9 <- 8 <- 7 <- 5 <- 2 <- 1
最终找到6是第一个变小的数字,记录下6的位置i-1
2.从i位置往后找到最后一个大于6的数
3 4 6 -> 9 -> 8 -> 7 5 2 1
最终找到7的位置,记录位置为m
3.交换位置i-1和m的值
3 4 7 9 8 6 5 2 1
4.倒序i位置后的所有数据
3 4 7 1 2 5 6 8 9
则347125689为346987521的下一个排列
完整代码:
import java.util.ArrayList;
import java.util.Arrays;
public class Solution {
public ArrayList<String> Permutation(String str) {
ArrayList<String> list = new ArrayList<String>();
if(str == null || str.length()==0) {
return list;
}
char[] chars = str.toCharArray();
Arrays.sort(chars);
list.add(String.valueOf(chars));
int len = chars.length;
while(true) {
int lIndex = len-1;
//从右往左,找到每一个开始变小数字的位置
while(lIndex>0 && chars[lIndex-1]>=chars[lIndex]) {
lIndex--;
}
//从右向左扫描若都是增的,代表全排列结束
if(lIndex == 0) {
break;
}
int rIndex = lIndex;
//从开始变小数字后面开始向右遍历,找到最后一个比这个数字大的数
while(rIndex<len && chars[rIndex]>chars[lIndex-1]) {
rIndex++;
}
//交换lIndex-1和rIndex-1这两个数的位置
swap(chars,lIndex-1,rIndex-1);
//lIndex开始反转字符
reverse(chars,lIndex);
list.add(String.valueOf(chars));
}
return list;
}
private void swap(char[] chars, int i, int j) {
char temp = chars[i];
chars[i] = chars[j];
chars[j] = temp;
}
private void reverse(char[] chars,int k) {
if(chars==null || chars.length<=k) {
return;
}
int len = chars.length;
for(int i=0; i<(len-k)/2; i++) {
int m = k+i;
int n = len-1-i;
swap(chars,m,n);
}
}
}
题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
import java.util.*;
public class Solution {
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree==null)
return null;
Stack<TreeNode> s=new Stack<>();
TreeNode node=pRootOfTree;
TreeNode pre=null;
TreeNode head=null;
Boolean isFirst=true;
while(node!=null||!s.isEmpty()){
while(node!=null){
s.push(node);
node=node.left;
}
node=s.pop();
if(isFirst){
head=node;
pre=head;
isFirst=false;
}else{
pre.right=node;
node.left=pre;
pre=node;
}
node=node.right;
}
return head;
}
}
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
private TreeNode pre = null;
// private TreeNode head = null;
public TreeNode Convert(TreeNode root) {
if (root == null)
return null;
// 如果左子树为空,那么根节点root为双向链表的头节点
TreeNode head = Convert(root.left);
if (head == null)
head = root;
// 不理解这样为什么不行
// TreeNode leftnode = Convert(root.left);
// if (leftnode == null)
// head = root;
// 连接当前节点root和当前链表的尾节点pLast
root.left = pre;
if (pre != null)
pre.right = root;
pre = root;
Convert(root.right);
//返回双向链表头结点即中序遍历左子树最左结点
return head;
}
}
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针random指向一个随机节点),请对此链表进行深拷贝,并返回拷贝后的头结点。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
思路:
(1)在旧链表中创建新链表,此时不处理新链表的兄弟结点。
(2)根据旧链表的兄弟结点,初始化新链表的兄弟结点。
(3)从旧链表中拆分得到新链表
完整代码:
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
/*复制链表法:
*/
public class Solution {
public RandomListNode Clone(RandomListNode pHead) {
if(pHead==null){
return null;
}
RandomListNode node=pHead;
while(node!=null){
RandomListNode nextNode=new RandomListNode(node.label);
nextNode.next=node.next;
node.next=nextNode;
node=nextNode.next;
}
node=pHead;
while(node!=null){
if(node.random!=null){
node.next.random=node.random.next;
}
node=node.next.next;
}
node=pHead;
RandomListNode copyHead=node.next;
RandomListNode copyNode=copyHead;
while(node!=null){
node.next=node.next.next;
copyNode.next=copyNode.next==null?null:copyNode.next.next;
node=node.next;
copyNode=copyNode.next;
}
return copyHead;
}
}
/*
public class RandomListNode {
int label;
RandomListNode next = null;
RandomListNode random = null;
RandomListNode(int label) {
this.label = label;
}
}
*/
/*使用哈希表,时间复杂度O(N),额外空间复杂度O(N)*/
import java.util.HashMap;
public class Solution {
public RandomListNode Clone(RandomListNode pHead)
{
HashMap<RandomListNode, RandomListNode> map = new HashMap<RandomListNode, RandomListNode>();
RandomListNode cur = pHead;
while (cur != null) {
map.put(cur, new RandomListNode(cur.label));//key为原链表结点,value为原链表结点对应复制的链表
cur = cur.next;
}
cur = pHead;
while (cur != null) {
map.get(cur).next = map.get(cur.next);
cur = cur.next;
}
cur = pHead;
while (cur != null) {
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
RandomListNode resHead = map.get(pHead);
return resHead;
}
}
题目描述
输入一颗二叉树的根节点和一个整数,按字典序打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
private ArrayList<ArrayList<Integer>> listAll = new ArrayList<ArrayList<Integer>>();
private ArrayList<Integer> list = new ArrayList<Integer>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root == null)
return listAll;
list.add(root.val);
target -= root.val;
if(target == 0 && root.left == null && root.right == null){
// ArrayList是一个引用,记录的是指向位置。
// 如果是result.add(list);无法拷贝list里面的值
listAll.add(new ArrayList<Integer>(list));
}
FindPath(root.left, target);
FindPath(root.right, target);
/*移除最后一个元素,深度遍历完一条路径后要回退。无论当前路径和是否大于target,
必须去掉最后一个,然后返回父节点,去查找另一条路径。不去掉最后一个也会遍历左子树,
但是遍历左子树时前面遍历的右子树也加到了集合中。例如{10,5,12,4,7},22
实际输出会是[[10,5,4,7],[10,5,4,7,12]] 而不是[[10,5,7],[10,12]]*/
list.remove(list.size()-1);
return listAll;
}
}
题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。
思路:
前面有道题是“栈的压入、弹出序列”。
写这道题的例子时发现二叉树的中序序列和后序序列就满足栈的压入弹出序列关系。
即如果把中序序列当做栈的压入序列,那么后序序列是该栈的一个弹出序列。
而BST的中序是排序数组。因此将本题的序列排序作为中序序列,引用前面题的答案判断两序列是否满足上述关系即可。
完整代码:
import java.util.Stack;
import java.util.Arrays;
public class Solution {
public boolean VerifySquenceOfBST(int [] seq) {
int[] arr = seq.clone();
Arrays.sort(arr);
return IsPopOrder(arr,seq);
}
//判断第二个序列是否可能为第一个序列的弹出顺序,引用的“栈的压入、弹出序列”题目的答案
public boolean IsPopOrder(int [] pushA,int [] popA) {
if(pushA.length == 0 || popA.length == 0)
return false;
Stack<Integer> s = new Stack<Integer>();
//用于标识弹出序列的位置
int popIndex = 0;
for(int i = 0; i< pushA.length;i++){
s.push(pushA[i]);
//如果栈不为空,且栈顶元素等于弹出序列
while(!s.empty()&&s.peek() == popA[popIndex]){
//出栈
s.pop();
//弹出序列向后一位
popIndex++;
}
}
// if(popIndex==popA.length){
// return true;
// }
// return false;
return s.empty();
}
}
public class Solution {
public boolean VerifySquenceOfBST(int [] sequence){
if(sequence==null||sequence.length<=0)
return false;
return VerifySquenceOfBST(sequence,0,sequence.length-1);
}
private boolean VerifySquenceOfBST(int [] sequence, int start, int end){
if(start>=end)
return true;
int root = sequence[end];
int i=start;
while(sequence[i]<root){
i++;
}
int j=i;
while(j<end){
if(sequence[j]<root){
return false;
}
j++;
}
boolean left = VerifySquenceOfBST(sequence, start, i-1);
boolean right = VerifySquenceOfBST(sequence, i, end-1);
return left && right;
}
}
题目描述
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
我的方法(标准解法)
import java.util.ArrayList;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
/**
思路:用arraylist模拟一个队列来存储相应的TreeNode
*/
public class Solution {
public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
ArrayList<Integer> list = new ArrayList<>();
ArrayList<TreeNode> queue = new ArrayList<>();
if (root == null) {
return list;
}
queue.add(root);
while (queue.size() != 0) {
TreeNode temp = queue.remove(0);
list.add(temp.val);
if (temp.left != null){
queue.add(temp.left);
}
if (temp.right != null) {
queue.add(temp.right);
}
}
return list;
}
}
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
我的方法(标准解法)
import java.util.*;
public class Solution {
public boolean IsPopOrder(int [] pushA,int [] popA) {
Stack<Integer> s = new Stack<>();
int index = 0;
for(int i = 0;i < pushA.length;i++){
s.push(pushA[i]);
while(!s.isEmpty() && s.peek()==popA[index]){
s.pop();
index++;
}
}
if(s.isEmpty()){
return true;
}
return false;
}
}
题目描述:
定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
思路:
因为题目要求时间复杂度为O(1),所以应利用一个辅助栈来存放最小值,以空间换时间。
栈 3,4,2,5,1
辅助栈 3,3,2,2,1
每入栈一次,就与辅助栈顶比较大小,如果小就入栈,如果大就入栈当前的辅助栈顶
当出栈时,辅助栈也要出栈
这种做法可以保证辅助栈顶一定都当前栈的最小值
完整代码:
import java.util.Stack;
public class Solution {
Stack<Integer> dataStack = new Stack<Integer>();
Stack<Integer> minStack = new Stack<Integer>();
public void push(int node) {
dataStack.push(node);
if(minStack.isEmpty()||node < minStack.peek()){
minStack.push(node);
}else{
minStack.push(minStack.peek());
}
}
public void pop() {
dataStack.pop();
minStack.pop();
}
public int top() {
return dataStack.peek();
}
public int min() {
return minStack.peek();
}
}
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵:
[ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ] \begin{gathered} \begin{bmatrix} 1 & 2&3&4 \\5 & 6 & 7 & 8 \\ 9 & 10 & 11& 12 \\ 13& 14 & 15& 16 \end{bmatrix} \end{gathered} ⎣⎢⎢⎡15913261014371115481216⎦⎥⎥⎤
则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
import java.util.*;
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
ArrayList<Integer> list=new ArrayList<>();
if(matrix==null)
return list;
int[] res=new int[matrix.length*matrix[0].length];
printMatrixCode(matrix,res,0,0,matrix.length,matrix[0].length);
for(int i=0;i<res.length;i++)
list.add(res[i]);
return list;
}
int m=0;
public void printMatrixCode(int[][] matrix,int[] res,int rowstart,int colstart,int row,int col){
if(m==(matrix.length*matrix[0].length))
return ;
int i=rowstart,j=colstart-1;
while(j<col-1&&m<(matrix.length*matrix[0].length))
res[m++]=matrix[i][++j];
while(i<row-1&&m<(matrix.length*matrix[0].length))
res[m++]=matrix[++i][j];
while(j>=colstart+1&&m<(matrix.length*matrix[0].length))
res[m++]=matrix[i][--j];
while(i>rowstart+1&&m<(matrix.length*matrix[0].length))
res[m++]=matrix[--i][j];
printMatrixCode(matrix,res,rowstart+1,colstart+1,row-1,col-1);
}
}
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
输入描述:
import java.util.*;
/*
* public class TreeNode {
* int val = 0;
* TreeNode left = null;
* TreeNode right = null;
* public TreeNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pRoot TreeNode类
* @return TreeNode类
*/
public TreeNode Mirror (TreeNode pRoot) {
// write code here
if(pRoot == null){
return pRoot;
}
TreeNode node = pRoot.left;
pRoot.left = pRoot.right;
pRoot.right = node;
pRoot.left = Mirror(pRoot.left);
pRoot.right = Mirror(pRoot.right);
return pRoot;
}
}
import java.util.*;
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public void Mirror(TreeNode root) {
if(root == null){
return;
}
Stack<TreeNode> stack = new Stack<TreeNode>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode node = stack.pop();
if(node.left != null||node.right != null){
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
}
if(node.left!=null){
stack.push(node.left);
}
if(node.right!=null){
stack.push(node.right);
}
}
}
}
题目描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
/**
public class TreeNode {
int val = 0;
TreeNode left = null;
TreeNode right = null;
public TreeNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public static boolean HasSubtree(TreeNode root1, TreeNode root2) {
boolean result = false;
//当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false
if (root2 != null && root1 != null) {
//如果找到了对应Tree2的根节点的点
if(root1.val == root2.val){
//以这个根节点为为起点判断是否包含Tree2
result = doesTree1HaveTree2(root1,root2);
}
//如果找不到,那么就再去root的左儿子当作起点,去判断时候包含Tree2
if (!result) {
result = HasSubtree(root1.left,root2);
}
//如果还找不到,那么就再去root的右儿子当作起点,去判断时候包含Tree2
if (!result) {
result = HasSubtree(root1.right,root2);
}
}
//返回结果
return result;
}
public static boolean doesTree1HaveTree2(TreeNode node1, TreeNode node2) {
//如果Tree2已经遍历完了都能对应的上,返回true
if (node2 == null) {
return true;
}
//如果Tree2还没有遍历完,Tree1却遍历完了。返回false
if (node1 == null) {
return false;
}
//如果其中有一个点没有对应上,返回false
if (node1.val != node2.val) {
return false;
}
//如果根节点对应的上,那么就分别去子节点里面匹配
return doesTree1HaveTree2(node1.left,node2.left) && doesTree1HaveTree2(node1.right,node2.right);
}
}
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
完整代码
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1,ListNode list2) {
ListNode node=new ListNode(-1);
node.next=null;
ListNode head=node;
while(list1!=null&&list2!=null){
if(list1.val<list2.val){
node.next=list1;
node=list1;
list1=list1.next;
}else{
node.next=list2;
node=list2;
list2=list2.next;
}
}
if(list1!=null){
node.next=list1;
}
if(list2!=null){
node.next=list2;
}
return head.next;
}
}
完整代码
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode Merge(ListNode list1, ListNode list2) {
if(list1==null)
return list2;
if(list2==null)
return list1;
ListNode res = null;
if(list1.val<list2.val){
res = list1;
res.next = Merge(list1.next, list2);
}else{
res = list2;
res.next = Merge(list1, list2.next);
}
return res;
}
}
题目描述
输入一个链表,反转链表后,输出新链表的表头。
此方法好于递归法,主要的思想是用两个指针,其中newHead指向的是反转成功的链表的头部,currentHead指向的是还没有反转的链表的头部:
初始状态是newHead指向null,currentHead指向的是第一个元素,一直往后遍历直到newHead指向最后一个元素为止:
下面展示的是其中某个时间点的指向细节:
完整代码
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
// 1->2->3->4->5
// 设置两个指针,一个指针指向反转成功的链表的头部,另一个指针指向未反转的链表的头部
// 初始状态两个指针一个指向NULL,一个指向链表第一个元素
// 结束状态两个指针一个指向链表最后一个元素,一个指向NULL
// 1<-2<-3 4->5
ListNode head1 = null;
ListNode head2 = head;
if(head == null || head.next == null){
return head;
}
while(head2!=null){
ListNode temp = head2.next;
head2.next = head1;
head1 = head2;
head2 = temp;
}
return head1;
}
}
ReverseList(head.next)后:
head.next.next = head后:
head.next = null:
完整代码
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
if(head == null || head.next == null) {
return head;
}
ListNode preNode = ReverseList(head.next);
head.next.next = head;
head.next = null;
return preNode;
}
}
题目描述
输入一个链表,输出该链表中倒数第k个结点。
import java.util.*;
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param pHead ListNode类
* @param k int整型
* @return ListNode类
*/
public ListNode FindKthToTail (ListNode pHead, int k) {
ListNode node1 = pHead;
ListNode node2 = pHead;
while(k > 0){
if(node1 != null){
node1 = node1.next;
k--;
}else{
return null;
}
}
while(node1 != null){
node1 = node1.next;
node2 = node2.next;
}
return node2;
}
}
import java.util.*;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
if(head == null || k ==0 ){
return null;
}
//可以先把链表反转,然后找出第k个
Stack<ListNode> stack = new Stack<ListNode>();
int count = 0;
while(head != null){
stack.push(head);
head = head.next;
count++;
}
if(count < k){
return null;
}
ListNode knode = null;
for(int i = 0; i < k; i++){
knode = stack.pop();
}
return knode;
}
}
题目描述
输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
public class Solution {
public void reOrderArray(int [] array) {
int newArray[]=new int[array.length];
int i=0;
int j=array.length-1;
int lowIndex=0;
int highIndex=array.length-1;
while(i<array.length||j>=0){
if(array[i]%2==1){
newArray[lowIndex++]=array[i];
}
i++;
if(array[j]%2==0){
newArray[highIndex--]=array[j];
}
j--;
}
for(i=0;i<array.length;i++){
array[i]=newArray[i];
}
}
}
public class Solution {
public void reOrderArray(int [] array) {
//相对位置不变,稳定性
//插入排序的思想
int m = array.length;
int k = 0;//记录已经摆好位置的奇数的个数
for (int i = 0; i < m; i++) {
if (array[i] % 2 == 1) {
int j = i;
while (j > k) {//j >= k+1
int tmp = array[j];
array[j] = array[j-1];
array[j-1] = tmp;
j--;
}
k++;
}
}
}
}
/*
整体思路:
首先统计奇数的个数
然后新建一个等长数组,设置两个指针,奇数指针从0开始,偶数指针从奇数个数的末尾开始
遍历,填数
时间复杂度为O(n),空间复杂度为O(n)
*/
public class Solution {
public void reOrderArray(int [] array) {
if(array.length==0||array.length==1) return;
int oddCount=0,oddBegin=0;
int[] newArray=new int[array.length];
for(int i=0;i<array.length;i++){
if((array[i]&1)==1) oddCount++;
}
for(int i=0;i<array.length;i++){
if((array[i]&1)==1) newArray[oddBegin++]=array[i];
else newArray[oddCount++]=array[i];
}
for(int i=0;i<array.length;i++){
array[i]=newArray[i];
}
}
}
题目描述
给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
保证base和exponent不同时为0
public class Solution {
public double Power(double base, int exponent) {
int count=1;
int n;
if(exponent<0){
n=-exponent;
}else if(exponent>0){
n=exponent;
}else{
return 1;
}
double result=base;
while(count<n){
result=result*base;
count++;
}
if(exponent>0)
return result;
else
return 1/result;
}
}
public class Solution {
/**
* 1.全面考察指数的正负、底数是否为零等情况。
* 2.写出指数的二进制表达,例如13表达为二进制1101。
* 3.举例:10^1101 = 10^0001*10^0100*10^1000。
* 4.通过&1和>>1来逐位读取1101,为1时将该位代表的乘数累乘到最终结果。
*/
public double Power(double base, int n) {
double res = 1,curr = base;
int exponent;
if(n>0){
exponent = n;
}else if(n<0){
if(base==0)
throw new RuntimeException("分母不能为0");
exponent = -n;
}else{// n==0
return 1;// 0的0次方
}
while(exponent!=0){
if((exponent&1)==1)
res*=curr;
curr*=curr;// 翻倍
exponent>>=1;// 右移一位
}
return n>=0?res:(1/res);
}
}
/*剑指书中细节:
*1.当底数为0且指数<0时
*会出现对0求倒数的情况,需进行错误处理,设置一个全局变量;
*2.判断底数是否等于0
*由于base为double型,不能直接用==判断
*3.优化求幂函数
*当n为偶数,a^n =(a^n/2)*(a^n/2)
*当n为奇数,a^n = a^[(n-1)/2] * a^[(n-1)/2] * a
*时间复杂度O(logn)
*/
public class Solution {
public double Power(double base, int exponent) {
int n;
double res=1;
double pre;
if(exponent<0){
n=-exponent;
}else if(exponent>0){
n=exponent;
}else{
return 1;
}
pre=Power(base,n>>1);
res=pre*pre;
if((exponent&1)==1){
res=res*base;
}
return exponent>0?res:1/res;
}
}
题目描述
输入一个整数,输出该数32位二进制表示中1的个数。其中负数用补码表示。
思路:
为了避免死循环,我们可以不右移输入的数字n。首先把n和1做与运算,判断n的最低位是不是为1。接着把1左移一位得到2,再和n做与运算,就能判断n的低位是不是1…这样反复左移,每次都能判断n的其中一位是不是1。
public class Solution {
public int NumberOf1(int n) {
int count=0;
int flag=1;
while(flag!=0){
if((n&flag)!=0)
count++;
flag=flag<<1;
}
return count;
}
}
思路:
如果一个整数不为0,那么这个整数至少有一位是1。如果我们把这个整数减1,那么原来处在整数最右边的1就会变为0,原来在1后面的所有的0都会变成1(如果最右边的1后面还有0的话)。其余所有位将不会受到影响。
举个例子:一个二进制数1100,从右边数起第三位是处于最右边的一个1。减去1后,第三位变成0,它后面的两位0变成了1,而前面的1保持不变,因此得到的结果是1011.我们发现减1的结果是把最右边的一个1开始的所有位都取反了。这个时候如果我们再把原来的整数和减去1之后的结果做与运算,从原来整数最右边一个1那一位开始所有位都会变成0。如1100&1011=1000.也就是说,把一个整数减去1,再和原整数做与运算,会把该整数最右边一个1变成0.那么一个整数的二进制有多少个1,就可以进行多少次这样的操作。
public class Solution {
public int NumberOf1(int n) {
int count = 0;
while (n != 0) {
++count;
n = (n - 1) & n;
}
return count;
}
}
题目描述
我们可以用2 * 1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2 * 1的小矩形无重叠地覆盖一个2 * n的大矩形,总共有多少种方法?
比如n=3时,2 * 3的矩形块有3种覆盖方法:
思路:
假设:n块矩形有f(n)种覆盖方法。进行逆向分析,要完成最后的搭建有两种可能。
第一种情况等价于情形1中阴影部分的n-1块矩形有多少种覆盖方法,为f(n-1);
第二种情况等价于情形2中阴影部分的n-2块矩形有多少种覆盖方法,为f(n-2);
故f(n) = f(n-1) + f(n-2),是一个斐波那契数列,且f(1) = 1, f(2) = 2。
public class Solution {
public int RectCover(int target) {
if(target <= 0){
return 0;
}
if(target == 1){
return 1;
}
if(target == 2){
return 2;
}
return RectCover(target-1)+RectCover(target-2);
}
}
public class Solution {
public int RectCover(int target) {
if(target <= 0){
return 0;
}
if(target == 1){
return 1;
}
if(target == 2){
return 2;
}
int first = 1;
int second = 2;
int result = 0;
for(int i = 3; i <= target; i++){
result = first + second;
first = second;
second = result;
}
return result;
}
}
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
思路:
关于本题,前提是n个台阶会有一次n阶的跳法。分析如下:
f(1) = 1
f(2) = f(2-1) + f(2-2) //f(2-2) 表示2阶一次跳2阶的次数。
f(3) = f(3-1) + f(3-2) + f(3-3)
…
f(n) = f(n-1) + f(n-2) + f(n-3) + … + f(n-(n-1)) + f(n-n)
说明:
1)这里的f(n) 代表的是n个台阶有一次1,2,…n阶的跳法数。
2)n = 1时,只有1种跳法,f(1) = 1
3)n = 2时,会有两个跳得方式,一次1阶或者2阶,这回归到了问题(1) ,f(2) = f(2-1) + f(2-2)
4)n = 3时,会有三种跳得方式,1阶、2阶、3阶,
那么就是第一次跳出1阶后面剩下:f(3-1);第一次跳出2阶,剩下f(3-2);第一次3阶,那么剩下f(3-3)
因此结论是f(3) = f(3-1)+f(3-2)+f(3-3)
5)n = n时,会有n中跳的方式,1阶、2阶…n阶,得出结论:
f(n) = f(n-1)+f(n-2)+…+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + … + f(n-1)
6)由以上已经是一种结论,但是为了简单,我们可以继续简化:
f(n-1) = f(0) + f(1)+f(2)+f(3) + … + f((n-1)-1) = f(0) + f(1) + f(2) + f(3) + … + f(n-2)
f(n) = f(0) + f(1) + f(2) + f(3) + … + f(n-2) + f(n-1) = f(n-1) + f(n-1)
可以得出:
f(n) = 2*f(n-1)
7)得出最终结论,在n阶台阶,一次有1、2、…n阶的跳的方式时,总得跳法为:
f(n) = -1 (n=0)
f(n) = 1 (n=1)
f(n) = 2*f(n-1) (n>=2)
完整代码:
public class Solution {
public int JumpFloorII(int target) {
if (target <= 0) {
return -1;
} else if (target == 1) {
return 1;
} else {
return 2 * JumpFloorII(target - 1);
}
}
}
public class Solution {
public int JumpFloorII(int target) {
//左移1位就是乘以一个2,在1基础上左移number-1位就是1乘以2^(number-1)
return 1<<(target-1);
}
}
题目描述
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。
思路
对于本题,前提只有 一次 1阶或者2阶的跳法。
a.如果两种跳法,1阶或者2阶,那么假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法是f(n-1);
b.假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法是f(n-2)
c.由a\b假设可以得出总跳法为: f(n) = f(n-1) + f(n-2)
d.然后通过实际的情况可以得出:只有一阶的时候 f(1) = 1 ,只有两阶的时候可以有 f(2) = 2
e.可以发现最终得出的是一个斐波那契数列:
f(n) = 1 (n=1)
f(n) = 2 (n=2)
f(n) = f(n-1)+f(n-2) ,(n>2,n为整数)
完整代码:
public class Solution {
public int JumpFloor(int target) {
if (target <= 0) {
return -1;
} else if (target == 1) {
return 1;
} else if (target ==2) {
return 2;
} else {
return JumpFloor(target-1)+JumpFloor(target-2);
}
}
}
题目描述
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0,第1项是1)。
n<=39
public class Solution {
public int Fibonacci(int n) {
if(n==0)
return 0;
if(n==1)
return 1;
return Fibonacci(n-1)+Fibonacci(n-2);
}
}
public class Solution {
public int Fibonacci(int n) {
int preNum=1;
int prePreNum=0;
int result=0;
if(n==0)
return 0;
if(n==1)
return 1;
for(int i=2;i<=n;i++){
result=preNum+prePreNum;
prePreNum=preNum;
preNum=result;
}
return result;
}
}
题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组[3,4,5,1,2]为[1,2,3,4,5]的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
思路:
采用二分法解答这个问题,
mid = low + (high - low)/2
需要考虑三种情况:
(1)array[mid] > array[high]:
出现这种情况的array类似[3,4,5,6,0,1,2],此时最小数字一定在mid的右边。
low = mid + 1
(2)array[mid] == array[high]:
出现这种情况的array类似 [1,0,1,1,1] 或者[1,1,1,0,1],此时最小数字不好判断在mid左边
还是右边,这时只好一个一个试 ,
high = high - 1
(3)array[mid] < array[high]:
出现这种情况的array类似[2,2,3,4,5,6,6],此时最小数字一定就是array[mid]或者在mid的左
边。因为右边必然都是递增的。
high = mid
注意这里有个坑:如果待查询的范围最后只剩两个数,那么mid 一定会指向下标靠前的数字
比如 array = [4,6]
array[low] = 4 ;array[mid] = 4 ; array[high] = 6 ;
如果high = mid - 1,就会产生错误, 因此high = mid
但情形(1)中low = mid + 1就不会错误
完整代码:
import java.util.ArrayList;
public class Solution {
public int minNumberInRotateArray(int [] array) {
int start=0;
int end=array.length-1;
while(start<end){
int mid=(start+end)/2;
if(array[mid]>array[end]){
start=mid+1;//此时array[mid]不可能是最小元素,所以从下标为mid+1开始查找
}else if(array[mid]<array[end]){
end=mid;//此时array[mid]有可能是最小元素,所以从下标为mid开始查找
}else{
end--;
}
}
return array[start];
}
}
题目描述
用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。
import java.util.Stack;
public class Solution {
Stack<Integer> stack1 = new Stack<Integer>();
Stack<Integer> stack2 = new Stack<Integer>();
public void push(int node) {
stack1.push(node);
}
public int pop() {
if(stack2.isEmpty()){
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
思路:
因为是树的结构,一般都是用递归来实现。
用数学归纳法的思想就是,假设最后一步,就是root的左右子树都已经重建好了,那么我只要考虑将root的左右子树安上去即可。
根据前序遍历的性质,第一个元素必然就是root,那么下面的工作就是如何确定root的左右子树的范围。
根据中序遍历的性质,root元素前面都是root的左子树,后面都是root的右子树。那么我们只要找到中序遍历中root的位置,就可以确定好左右子树的范围。
正如上面所说,只需要将确定的左右子树安到root上即可。递归要注意出口,假设最后只有一个元素了,那么就要返回。
完整代码:
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
import java.util.Arrays;
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
//数组长度为0的时候要处理
if(pre.length == 0){
return null;
}
int rootVal = pre[0];
//我们先找到root所在的位置,确定好前序和中序中左子树和右子树序列的范围
TreeNode root = new TreeNode(rootVal);
int rootIndex = 0;
for(int i=0;i<in.length;i++){
if(rootVal == in[i]){
rootIndex = i;
break;
}
}
//递归,假设root的左右子树都已经构建完毕,那么只要将左右子树安到root左右即可
//这里注意Arrays.copyOfRange(int[],start,end)是[)的区间
root.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,rootIndex+1),Arrays.copyOfRange(in,0,rootIndex));
root.right = reConstructBinaryTree(Arrays.copyOfRange(pre,rootIndex+1,pre.length),Arrays.copyOfRange(in,rootIndex+1,in.length));
return root;
}
}
题目描述
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
Stack<Integer> stack=new Stack<Integer>();
while(listNode!=null){
stack.push(listNode.val);
listNode=listNode.next;
}
ArrayList<Integer> list=new ArrayList<Integer>();
while(!stack.isEmpty()){
list.add(stack.pop());
}
return list;
}
}
/**
* public class ListNode {
* int val;
* ListNode next = null;
*
* ListNode(int val) {
* this.val = val;
* }
* }
*
*/
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list=new ArrayList<>();
if(listNode!=null){
if(listNode.next!=null){
list=printListFromTailToHead(listNode.next);
}
list.add(listNode.val);
}
return list;
}
}
题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
参数为String类型(剑指offer更新):
import java.util.*;
public class Solution {
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param s string字符串
* @return string字符串
*/
public String replaceSpace (String s) {
// write code here
char[] str=s.toCharArray();
int space=0;
for(int i=0;i<str.length;i++){
if(str[i]==' '){
space++;
}
}
int copyLength=s.length()+space*2;
char[] copy=new char[copyLength];
int i=str.length-1;
int index=copyLength-1;
while(i>=0){
if(str[i]==' '){
copy[index--]='0';
copy[index--]='2';
copy[index--]='%';
i--;
}else{
copy[index--]=str[i--];
}
}
return String.valueOf(copy);
}
}
/*
问题1:替换字符串,是在原来的字符串上做替换,还是新开辟一个字符串做替换!
问题2:在当前字符串替换,怎么替换才更有效率(不考虑java里现有的replace方法)。
从前往后替换,后面的字符要不断往后移动,要多次移动,所以效率低下
从后往前,先计算需要多少空间,然后从后往前移动,则每个字符只为移动一次,这样效率更高一点。
*/
public class Solution {
public String replaceSpace(StringBuffer str) {
int spacenum=0;
for(int i=0;i<str.length();i++){
if(str.charAt(i)==' '){
spacenum++;
}
}
int oldindex=str.length()-1;
int newLength=str.length()+spacenum*2;
str.setLength(newLength);
int newindex=str.length()-1;
for(int i=oldindex;i>=0;i--){
if(str.charAt(i)==' '){
str.setCharAt(newindex--,'0');
str.setCharAt(newindex--,'2');
str.setCharAt(newindex--,'%');
}else{
str.setCharAt(newindex--,str.charAt(i));
}
}
return str.toString();
}
}
public class Solution {
public String replaceSpace(StringBuffer str) {
StringBuffer result=new StringBuffer();
for(int i = 0;i<str.length();i++){
if(str.charAt(i)==' '){
result = result.append("%20");
}else{
result = result.append(str.charAt(i));
}
}
return result.toString();
}
}
import java.util.*;
public class Solution {
public String replaceSpace(StringBuffer str) {
String res=str.toString().replaceAll(" ","%20");
return res;
}
}
题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
/*把每一行看成有序递增的数组,
利用二分查找,
通过遍历每一行得到答案,
时间复杂度是nlogn*/
public class Solution {
public boolean Find(int target, int [][] array) {
for(int i=0;i<array.length;i++){
int low=0;
int high=array[i].length-1;
while(low<=high){
int mid=(low+high)/2;
if(target>array[i][mid])
low=mid+1;
else if(target<array[i][mid])
high=mid-1;
else
return true;
}
}
return false;
}
}
/*利用二维数组由上到下,由左到右递增的规律,
那么选取右上角或者左下角的元素a[row][col]与target进行比较,
当target小于元素a[row][col]时,那么target必定在元素a所在行的左边,
即col--;
当target大于元素a[row][col]时,那么target必定在元素a所在列的下边,
即row++;*/
public class Solution {
public boolean Find(int target, int [][] array) {
int row=0;
int col=array[0].length-1;
while(row<=array.length-1&&col>=0){
if(target==array[row][col])
return true;
else if(target>array[row][col])
row++;
else
col--;
}
return false;
}
}
递归法
public static int Binary(int[] array,int start,int end,int k){
int mid=(start+end)/2;
if(start>end){
return -1;
}
if(k==array[mid]){
return mid;
}else if(k<array[mid]){
return Binary(array,start,mid-1,k);
}else{
return Binary(array,mid+1,end,k);
}
}
非递归法
public static int Binary(int[] array,int k){
int mid;
int start=0;
int end=array.length-1;
if(array.length==0){
return -1;
}
while(start<=end){
mid=(start+end)/2;
if(k==array[mid]){
return mid;
}else if(k<array[mid]){
end=mid-1;
}else{
start=mid+1;
}
}
return -1;
}
public static void sort(int []arr){
if(arr.length==0){
return ;
}
int []temp = new int[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
sort(arr,0,arr.length-1,temp);
}
public static void sort(int[] arr,int left,int right,int []temp){
if(left<right){
int mid = (left+right)/2;
sort(arr,left,mid,temp);//左边归并排序,使得左子序列有序
sort(arr,mid+1,right,temp);//右边归并排序,使得右子序列有序
MergeSort(arr,left,mid,right,temp);//将两个有序子数组合并操作
}
}
public static void MergeSort(int[] arr,int left,int mid,int right,int[] temp){
int i = left;//左序列指针
int j = mid+1;//右序列指针
int t = 0;//临时数组指针
while (i<=mid && j<=right){
if(arr[i]<=arr[j]){
temp[t++] = arr[i++];
}else {
temp[t++] = arr[j++];
}
}
while(i<=mid){//将左边剩余元素填充进temp中
temp[t++] = arr[i++];
}
while(j<=right){//将右序列剩余元素填充进temp中
temp[t++] = arr[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while(left <= right){
arr[left++] = temp[t++];
}
}
import java.util.*;
public class Solution {
public void sort(int[] numbers) {
if(numbers==null||numbers.length==0)
return -1;
quick(numbers,0,numbers.length-1);
}
public int partition(int[] array,int begin,int end){
int i=begin,j=end;
while(i<j){
while(i<j&&array[i]<=array[j]){
i++;
}
if(i<j){
int temp=array[i];
array[i]=array[j];
array[j]=temp;
j--;
}
while(i<j&&array[i]<=array[j]){
j--;
}
if(i<j){
int temp=array[i];
array[i]=array[j];
array[j]=temp;
i++;
}
}
return i;
}
public void quick(int[] array,int begin,int end){
if(begin<end){
int index=partition(array,begin,end);
quick(array,begin,index-1);
quick(array,index+1,end);
}
}
}
public static void Bubblesort(int[] array){
for(int i=1;i<array.length;i++){
for(int j=0;j<array.length-i;j++){
if(array[j]>array[j+1]){
int temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
}
}
中序:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<>();
inorderTraversal(root,res);
return res;
}
public void inorderTraversal(TreeNode root,List<Integer> list){
if(root!=null){
inorderTraversal(root.left,list);
list.add(root.val);
inorderTraversal(root.right,list);
}
}
}
先序:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res=new ArrayList<>();
preorderTraversal(root,res);
return res;
}
public void preorderTraversal(TreeNode root,List<Integer> list) {
if(root!=null){
list.add(root.val);
preorderTraversal(root.left,list);
preorderTraversal(root.right,list);
}
}
}
后序:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
List<Integer> res=new ArrayList<>();
public List<Integer> postorderTraversal(TreeNode root) {
if(root==null){
return res;
}
postorderTraversal(root.left);
postorderTraversal(root.right);
res.add(root.val);
return res;
}
}
剑指offer P231 面试题46
/**
* @program:Interview
* @description:
* @auther:滕畅
* @create:2021-03-28 15:49
**/
public class NumToStr {
static int count2=0;
public static int count(int num){
if(num<0){
return 0;
}
String str=String.valueOf(num);
int length=str.length();
if(length==0)
return 0;
if(length==1)
return 1;
String s1=str.substring(1,length);
int count1=count(Integer.valueOf(s1));
char[] c=str.substring(0,2).toCharArray();//取前两个数
int digit=(c[0]-'0')*10+c[1]-'0';
if(digit<25){
if(length>2){
String s2=str.substring(2,length);
count2=count(Integer.valueOf(s2));
}
if(length==2)
count2=1;
}
return count1+count2;
}
public static void main(String[] args){
System.out.println(count(12258));
//bccfi、bwfi、bczi、mcfi、mzi
}
}
定义一个栈一个集合
while( 栈非空 || node 非空)
{
if( node 非空)
{
}
else
{
}
}
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode node = root;
while (node != null || !stack.isEmpty()) {
if (node != null) {
stack.push(node);
node = node.left;
} else {
node = stack.pop();
list.add(node.val);
node = node.right;
}
}
return list;
}
}
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
Stack<TreeNode> s=new Stack<>();
List<Integer> res=new ArrayList<>();
TreeNode node=root;
while(node!=null||!s.isEmpty()){
if(node!=null){
s.push(node);
res.add(node.val);
node=node.left;
}else{
node=s.pop();
node=node.right;
}
}
return res;
}
}
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
TreeNode node = root,r = null;
List<Integer> list = new ArrayList();
Stack<TreeNode> s = new Stack<>();
while(!s.isEmpty() || node != null)
{
if(node != null)
{
s.push(node);
node = node.left;
}
else
{
node = s.peek();
if(node.right == null || node.right == r)
{
list.add(node.val);
r = node;
s.pop();
node = null;
}
else
node = node.right;
}
}
return list;
}
}
import java.math.BigInteger;
/**
* 题目:
* 两个大数相加
*
* 题目描述:
*
两个大数相加。
1、是整数;
2、两个数无限大,long都装不下;
3、不能用BigInteger;
4、不能用任何包装类提供的运算方法;
5、两个数都是以字符串的方式提供。
*
*/
public class BigIntegerAdd {
/**
* 思路:
* 字符串逐位相加,需要进位则进位处理,考虑两个问题:
* 1、char怎么转换为integer, 减去'0'即可
* 2、怎么处理对应位相加?反转字符串相加,正确处理进位即可,
* 这样个位对应个位,十位对应十位,剩余的直接追加
*/
public String add(String str1, String str2) {
// 一个字符串为空 直接返回另外一个
if (str1 == null || "".equals(str1)) {
return str2;
}
if (str2 == null || "".equals(str2)) {
return str1;
}
StringBuilder sb = new StringBuilder();
// 字符串都不为空时
char[] arr1 = str1.toCharArray();
char[] arr2 = str2.toCharArray();
int idx1 = arr1.length - 1;
int idx2 = arr2.length - 1;
// 缓存是否需要进位
boolean carry = false;
// 遍历两个字符串 处理元素相加
while (idx1>=0 && idx2 >=0) {
char cur1 = arr1[idx1];
char cur2 = arr2[idx2];
// 相加
int sum = cur1 - '0' + cur2 - '0';
// 上一次运算是否有进位
sum = carry ? sum+1: sum;
// 是否需要进位
carry = sum >= 10 ? true : false;
// 相加结果 取个位数->字符类型
sb.append((char)((sum%10) + '0'));
// 索引递减
idx1--;
idx2--;
}
// 处理剩余的元素
while (carry || idx1 >= 0 || idx2 >= 0) {
if (idx1 >= 0) {
// 当前元素处理
int sum = arr1[idx1] - '0' + (carry ? 1 : 0);
// 下次是否需要进位
carry = sum >= 10 ? true : false;
// 添加到结果
sb.append((char)((sum%10) + '0'));
idx1--;
} else if (idx2 >= 0) {
// 当前元素处理
int sum = arr2[idx2] - '0' + (carry ? 1 : 0);
// 下次是否需要进位
carry = sum >= 10 ? true : false;
// 添加到结果
sb.append((char)((sum%10) + '0'));
idx2--;
} else {
sb.append('1');
carry = false;
}
}
// 反转sb 后返回
return sb.reverse().toString();
}
public static void main(String[] args) {
String str1 = "111111119111111111";
String str2 = "12";
System.out.println(new BigIntegerAdd().add(str1, str2));
System.out.println(new BigInteger(str1).add(new BigInteger(str2)));
String str3 = "999928";
String str4 = "72";
System.out.println(new BigIntegerAdd().add(str3, str4));
System.out.println(new BigInteger(str3).add(new BigInteger(str4)));
}
}
```# 73.有序数组的去重

```javascript
/**
* 给定任意一个有序整数数组,计算出去除重复后的数组长度,
* 并按长度生序重新组合该数组,超出新数组成都后面的数不做要求。
* 但是要求只能在原数组进行更改。
*/
public class DeleteArray {
public static void main(String[] args) {
int[] nums1 = {1,2,2,3,3,3,5,6,6,7};
int result = removeDuplicates2(nums1);
for(int i=0;i<result;i++){
System.out.println(nums1[i]);
}
}
/**
* 双指针之快慢指针
* @param arr
* @return
*/
private static int removeDuplicates2(int[] arr) {
// 边界处理
if (arr==null || arr.length==0){
return 0;
}
int slow = 0; // 慢指针
// 快慢指针从同一个位置出发
for (int fast = 0; fast < arr.length; fast++) {
if (arr[slow]!=arr[fast]){
slow++;
arr[slow] = arr[fast];
}
}
return slow+1;
}
}
boolean checkBST(Node root) {
return checkBST(root, Integer.MIN_VALUE, Integer.MAX_VALUE);
}
boolean checkBST(Node root, int min, int max) {
if (root == null) return true;
if (root.data <= min || root.data >= max)
return false;
if (!checkBST(root.left, min, root.data) || !checkBST(root.right, root.data, max))
return false;
return true;
}
第三轮该从31开始