剑指offer题解15
60 二叉搜索树的第k个节点
题目描述
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8) 中,按结点数值大小顺序第三小结点的值为4。
分析:根据性质,中序遍历即可
//中序遍历
public class Solution {
int count=0;
TreeNode KthNode(TreeNode pRoot, int k)
{
if(pRoot==null){
return null;
}
TreeNode node=KthNode(pRoot.left,k);
//如果没有达到目标,一定返回null,根据返回是否是null来判断是否成功获取到了值
if(node!=null){
return node;
}
count++;
if(count==k){
return pRoot;
}
node=KthNode(pRoot.right,k);
if(node!=null){
return node;
}
return node;
}
}
//非递归
public class Solution {
int count=0;
TreeNode KthNode(TreeNode pRoot, int k)
{
if(pRoot==null||k==0){
return null;
}
Stack<TreeNode> stack=new Stack<>();
while(pRoot!=null||!stack.isEmpty()){
if(pRoot!=null){
stack.push(pRoot);
pRoot=pRoot.left;
}else{
pRoot=stack.pop();
count++;
if(count==k){
return pRoot;
}
pRoot=pRoot.right;
}
}
return null;
}
}
61 数据流中的中位数
题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
分析:维护两个堆,一个大顶堆,一个小顶堆,并且大小堆的大小相差不能超过1,大顶堆中的顶小于等于小顶堆的顶,那么中位数就是大顶堆堆顶或者小顶堆堆顶或者他们的中位数.
public class Solution {
//大小顶堆
private PriorityQueue<Integer> maxHeap=new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
private PriorityQueue<Integer> minHeap=new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
});
//判断应该插入哪一个,保证相差不能超过一,并且可以确定奇数情况下是小顶堆数目多一
private boolean isMinHeap=false;
//从最开始,每一次,小顶堆将最小的值给大顶堆,大顶堆将最大值给小顶堆,保证了大顶堆中的顶小于等于小顶堆的顶
public void Insert(Integer num) {
if (isMinHeap){
minHeap.offer(num);
int smaller=minHeap.poll();
maxHeap.offer(smaller);
}else {
maxHeap.offer(num);
int bigger=maxHeap.poll();
minHeap.offer(bigger);
}
isMinHeap=!isMinHeap;
}
public Double GetMedian() {
if (minHeap.isEmpty()){
return null;
}
if(isMinHeap){
return Double.valueOf(minHeap.peek());
}
return (double) (maxHeap.peek() + minHeap.peek()) /2;
}
}
62 滑动窗口的最大值
题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
分析:直接使用滑动窗口即可
public class Solution {
public ArrayList<Integer> maxInWindows(int[] num, int size) {
//大顶
PriorityQueue<Integer> queue=new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
ArrayList<Integer> res=new ArrayList<>();
if (num==null||num.length<size||size==0){
return res;
}
int index=0;
//每一次取出最大值,删除过期值
for (int i = 0; i < num.length; i++) {
if (i>=size){
res.add(queue.peek());
queue.remove(Integer.valueOf(num[index++]));
}
queue.offer(num[i]);
}
//处理剩余的值:一共是length-size+1个结果
res.add(queue.peek());
return res;
}
}
63 矩阵中的路径
题目描述
请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
分析:经典的dfs,处理好以为数组和二维数组之间的映射关系即可i*col+j=index
public class Solution {
public boolean hasPath(char[] matrix, int rows, int cols, char[] str) {
for (int i = 0; i <rows ; i++) {
for (int j = 0; j < cols; j++) {
if (help(matrix,rows,cols,i,j,str,0,new boolean[matrix.length])){
return true;
}
}
}
return false;
}
private boolean help(char[] matrix, int row,int col,int i,int j,char[] str,int indexOfStr,boolean[] visited){
//到头了,正确
if (indexOfStr==str.length){
return true;
}
//判断是否i,j是否合法,此位置是否访问过,此位置是否和str相应位置值相同
if (i<0||j<0||i>=row||j>=col||visited[i*col+j]||matrix[i*col+j]!=str[indexOfStr]){
return false;
}
visited[i*col+j]=true;
indexOfStr+=1;
//上下左右移动
boolean res=help(matrix,row,col,i-1,j,str,indexOfStr,visited)
||help(matrix,row,col,i,j-1,str,indexOfStr,visited)
||help(matrix,row,col,i+1,j,str,indexOfStr,visited)
||help(matrix,row,col,i,j+1,str,indexOfStr,visited);
//visited归位
visited[i*col+j]=false;
return res;
}
}
64 机器人的运动范围
题目描述
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
分析:深搜即可
public class Solution {
public int movingCount(int threshold, int rows, int cols) {
boolean[] visited=new boolean[rows*cols];
return moving(threshold,rows,cols,0,0,visited);
}
private int moving(int threshold, int rows, int cols, int i, int j, boolean[] visited) {
if (i<0||j<0||i>=rows||j>=cols||visited[i*cols+j]||!checkSum(threshold,i,j)){
return 0;
}
visited[i*cols+j]=true;
return 1+moving(threshold,rows,cols,i+1,j,visited)
+moving(threshold,rows,cols,i,j+1,visited);
//+moving(threshold,rows,cols,i-1,j,visited)
//+moving(threshold,rows,cols,i,j-1,visited);
}
private boolean checkSum(int threshold, int i, int j) {
int res=0;
while (i>0){
res+=(i%10);
i/=10;
}
while (j>0){
res+=(j%10);
j/=10;
}
return res<=threshold;
}
}
65 整数中1出现的次数(从1到n整数中1出现的次数)
题目描述
求出113的整数中1出现的次数,并算出1001300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
分析:假如现在有一个数各位是abcdefg,现在求每个为是1的个数那么假如现在这个位是千位
这个位是0,那么此位出现1的情况仅由高位决定,比如230874,出现1的:1000-1999,11000-11999,21000-21999…221000-221999,出现的个数是:1000高位;
这个位是1,那么此位出现1的情况由高位和低位决定,比如231874,出现1的:1000-1874,11000-231874,…231000-231874,出现的个数是:高位1000+低位+1
这个为是2-9,那么此位出现1的情况仅由高位决定,比如233874,出现1的:1000-1999,11000-11999,…231000-231999,出现1的个数是:(高位+1)*1000
因此:可以按照个十百千万的次序按位来计算每一位出现1的次数
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int res=0,i=1;
while (n/i!=0){
int curr=n/i%10;
int low=n-n/i*i;
int high=n/(i*10);
//当前位为0,该位出现1的次数由高位决定
if(curr==0){
res+=high*i;
//此位为1,由高位和低位共同决定
}else if(curr==1){
res+=high*i+low+1;
//当前位是2-9由高位决定
}else {
res+=(high+1)*i;
}
i*=10;
}
return res;
}
}