* 面试题59:队列的最大值
* 题目一:滑动窗口的最大值
* 给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。
* 例如:如果输入数组{2,3,4,2,6,2,5}及滑动窗口的大小为3,那么一共存在6个滑动窗口,他们的最大值分别是{4,4,6,6,6,5}
*
* * 例如:
* 如果输入数组{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]}。
*
*
*
* 思路:运用双端队列,存储最大的下标+次大的下标,队头就是所要求的最大下标
* 时间复杂度O(n)
* 若当前值大于双端队列中存储的,则将其弹出 存储当前下标i
* 然后再判断是否已经滑动窗口内的数量已经到达size,
* 若表示队首元素是最大元素,但是要将队首元素出滑动窗口了,
* 需要将其从队列中的队头出弹出,因为此最大值下标已经过期了呢
* 然后再判断是否应该滑动窗口了,若i>size-1则表示需要滑动窗口,
* 也就是自从第三个位置(也就是索引值为2的地方)开始就需要每一次都滑动窗口
package Test;
import java.util.ArrayList;
import java.util.LinkedList;
public class No59First_MaxInWindows {
/*
* 面试题59:队列的最大值
* 题目一:滑动窗口的最大值
* 给定一个数组和滑动窗口的大小,请找出所有滑动窗口里的最大值。
* 例如:如果输入数组{2,3,4,2,6,2,5}及滑动窗口的大小为3,那么一共存在6个滑动窗口,他们的最大值分别是{4,4,6,6,6,5}
*
* * 例如:
* 如果输入数组{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]}。
*
*
*
* 思路:运用双端队列,存储最大的下标+次大的下标,队头就是所要求的最大下标
* 时间复杂度O(n)
* 若当前值大于双端队列中存储的,则将其弹出 存储当前下标i
* 然后再判断是否已经滑动窗口内的数量已经到达size,
* 若表示队首元素是最大元素,但是要将队首元素出滑动窗口了,
* 需要将其从队列中的队头出弹出,因为此最大值下标已经过期了呢
* 然后再判断是否应该滑动窗口了,若i>size-1则表示需要滑动窗口,
* 也就是自从第三个位置(也就是索引值为2的地方)开始就需要每一次都滑动窗口
*
* */
public static void main(String[] args) {
// TODO Auto-generated method stub
No59First_MaxInWindows m = new No59First_MaxInWindows();
int[] array = {2,3,4,2,6,2,5};
int size = 3;
ArrayList<Integer> res = new ArrayList<Integer>();
res = m.MaxInWindows(array,size);
System.out.println("滑动窗口的最大值序列是:"+res);
}
private ArrayList<Integer> MaxInWindows(int[] array, int size) {
// TODO Auto-generated method stub
ArrayList<Integer> res = new ArrayList<Integer>();
if(array == null || array.length == 0|| array.length < size || size == 0)
return res;
LinkedList<Integer> qMax = new LinkedList<Integer>();
//双端队列 存储最大值+次大值
for(int i = 0; i < array.length; i++) {
int cur = array[i];
//当队列不为空 且 队列中队尾存储的最大值 小于等于 当前值时 则将队尾值弹出
while(!qMax.isEmpty() && array[qMax.peekLast()] <= cur)
qMax.pollLast();
qMax.add(i); //将队尾下标存储到队列中
//当获取队首元素时,队首元素下标 = i - size---> 表示i - 队首元素下标 = size
//也就是该最大值已经过期 是即将划过去的下标 需要将其弹出队列 则将队首元素弹出
if(qMax.peekFirst() == i - size)
qMax.pollFirst();
//只要大于等于2 则将窗口右移 滑动到下一个窗口
if(i >= size - 1)
//并且获取到当前窗口的最大值
res.add(array[qMax.peekFirst()]);
}
return res;
}
}
* 面试题60:n个骰子的点数
* 题目:把n个骰子扔到地上,所有骰子朝上一面的点数之和为s。
* 输入n,打印出s的所有可能的值出现的概率
*
* 需要先统计出每个点数出现的次数,然后再每个点出现的次数除以6的n次方
* (n个骰子所有点数的排列数),就是每个点数出现的概率
* 思路:运用循环的思想,时间性能好O(n)
* 首先构建两个数组存储骰子点数的每个总数s出现的次数
* 第一次循环:第一个数组中的第n个数字表示:骰子和为n出现的次数
* 第二次~第n次循环:因为分析发现f(n)和为n的骰子出现的次数
* =上一轮循环中骰子和为n-1,n-2,n-3,n-4,n-5,n-6的次数的总和
* 所以从第二次开始每一次都要加上前一个数组的n-1到n-6的值
* 其中使用flag对两个数组进行切换 且要保证每次进行切换时数据都经过了清零操作
*
* 程序中使用maxValue这一变量来存储骰子面数6,是为了当其他厂家使用其他点数的骰子时,
* 只需要更改maxValue的值即可,对程序的扩展性极好
package Test;
public class No60PrintProbability {
/*
* 面试题60:n个骰子的点数
* 题目:把n个骰子扔到地上,所有骰子朝上一面的点数之和为s。
* 输入n,打印出s的所有可能的值出现的概率
*
* 需要先统计出每个点数出现的次数,然后再每个点出现的次数除以6的n次方
* (n个骰子所有点数的排列数),就是每个点数出现的概率
* 思路:运用循环的思想,时间性能好O(n)
* 首先构建两个数组存储骰子点数的每个总数s出现的次数
* 第一次循环:第一个数组中的第n个数字表示:骰子和为n出现的次数
* 第二次~第n次循环:因为分析发现f(n)和为n的骰子出现的次数
* =上一轮循环中骰子和为n-1,n-2,n-3,n-4,n-5,n-6的次数的总和
* 所以从第二次开始每一次都要加上前一个数组的n-1到n-6的值
* 其中使用flag对两个数组进行切换 且要保证每次进行切换时数据都经过了清零操作
*
* 程序中使用maxValue这一变量来存储骰子面数6,是为了当其他厂家使用其他点数的骰子时,
* 只需要更改maxValue的值即可,对程序的扩展性极好
* */
public static void main(String[] args) {
// TODO Auto-generated method stub
No60PrintProbability p = new No60PrintProbability();
int n = 6;//骰子个数
System.out.println("打印出所有的骰子点数的概率(骰子点数及其出现的概率)");
p.PrintProbability(n);
}
int maxValue = 6;
private void PrintProbability(int n) {
// TODO Auto-generated method stub
if(n < 1)
return;
int[][] pProbability = new int[2][maxValue*n+1];
for(int i = 0; i < maxValue*n+1;i++) {
pProbability[0][i] = 0;
pProbability[1][i] = 0;
}
int flag = 0;
//第一次循环
for(int i = 1;i < maxValue;i++)
pProbability[0][i] = 1;
//第2-n次循环
for(int k = 2;k < n;k++) {
for(int i = 0;i < k;i++)
pProbability[1-flag][i] = 0;
for(int i = k; i <= maxValue*k;i++) {
pProbability[1-flag][i] = 0;
for(int j = 1;j <= i && j <= maxValue;++j)
//也就是【i】 = 【j】 + 【i-j】
pProbability[1-flag][i] += pProbability[flag][i-j];
}
flag = 1 - flag;
}
//算出概率并打印
//Java中使用Math类的pow方法:
//public static double pow(double a,double b),
//返回第一个参数的第二个参数次幂的值
double total = Math.pow((double)maxValue,n);
for(int i = n;i <= maxValue*n;i++) {
System.out.println(total);
double ratio = (double)pProbability[flag][i]/total;
System.out.println(i+":"+ratio);
}
}
}
* 面试题61:扑克牌中的顺子
* 题目:从扑克牌中随机抽抽出5张牌,判断是不是一个顺子,
* 即这5张牌是不是连续的。
* 2~10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字
*
* 思路:
* 1>首先对数组进行排序
* 2>计算出0的个数
* 3>计算出数与数之间的间隔数 其中若两个值相等 ,即含有对子,则一定是不连续的
* 判断间隔数是否大于o的个数 若大于 则不连续 否则连续
*
* 这里排序算法使用的是java自带的Arrays.sort函数,不必考虑时间复杂度的问题
* 这是因为:通常认为不同级别的时间复杂度只有当n足够大的时候才是有意义的
* 本题中数组的长度固定,只有五张牌吧,所以时间复杂度上不会有太大的区别呢,
* 所以使用简洁易懂的方法来实现算法就可以
package Test;
import java.util.Arrays;
public class No61IsContinuous {
/*
* 面试题61:扑克牌中的顺子
* 题目:从扑克牌中随机抽抽出5张牌,判断是不是一个顺子,
* 即这5张牌是不是连续的。
* 2~10为数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字
*
* 思路:
* 1>首先对数组进行排序
* 2>计算出0的个数
* 3>计算出数与数之间的间隔数 其中若两个值相等 ,即含有对子,则一定是不连续的
* 判断间隔数是否大于o的个数 若大于 则不连续 否则连续
*
* 这里排序算法使用的是java自带的Arrays.sort函数,不必考虑时间复杂度的问题
* 这是因为:通常认为不同级别的时间复杂度只有当n足够大的时候才是有意义的
* 本题中数组的长度固定,只有五张牌吧,所以时间复杂度上不会有太大的区别呢,
* 所以使用简洁易懂的方法来实现算法就可以
*
*
* */
public static void main(String[] args) {
// TODO Auto-generated method stub
No61IsContinuous c = new No61IsContinuous();
int[] numbers = {1,3,4,0,5};
if(c.IsContinuous(numbers))
System.out.println("随机抽取的这五张扑克牌是顺子!");
else
System.out.println("随机抽取的这五张扑克牌不是顺子!!");
}
private boolean IsContinuous(int[] numbers) {
// TODO Auto-generated method stub
if(numbers == null || numbers.length== 0)
return false;
//对数组进行排序
Arrays.sort(numbers);
int numberOf0 = 0;
int numberOfGap = 0;
//计算出0的个数 对已经排序的数组进行统计 0全部在前面呢
for(int i = 0;i < numbers.length&&numbers[i] == 0;i++)
numberOf0++;
//计算出间隔数
int small = numberOf0;//第一个非0的数
int big = small+1;
while(big < numbers.length) {
if(numbers[small] == numbers[big])
//相等表示有对子 则不连续
return false;
numberOfGap += numbers[big]-numbers[small]-1;
small ++;
big ++;
}
return (numberOfGap > numberOf0)?false:true;
}
}
* 面试题62:圆圈中最后剩下的数字
* 题目:0,1,2,......,n-1这n个数字排成一个圆圈,从数字0开始,
* 每次从这个圆圈里删除第m个数字。 * 求出这个圆圈里最后剩下的最后一个数字。
* 例如:0,1,2,3,4这5个数字组成一个源泉,从数字0开始每次删除3个数字,
* 则删除的前4个数字依次是2,0,4,1,因此最后剩下的数字是3
*
*
* 思路:每次被删除的数字都有一定的规律 就可以直接计算出最后剩下的数字
* 关系是:f(n) = 0 当n = 0时
* f(n) = [f(n-1,m)+m]%n 当n > 0时
*
* 也就是要想得到n个数字的序列中最后剩下的数字,
* 就要得到n-1个数字的序列中最后剩下的数字,以此类推
* 可使用循环或递归 循环更加简单易懂
package Test;
public class No62LastRemaining {
/*
* 面试题62:圆圈中最后剩下的数字
* 题目:0,1,2,......,n-1这n个数字排成一个圆圈,从数字0开始,
* 每次从这个圆圈里删除第m个数字。 * 求出这个圆圈里最后剩下的最后一个数字。
* 例如:0,1,2,3,4这5个数字组成一个源泉,从数字0开始每次删除3个数字,
* 则删除的前4个数字依次是2,0,4,1,因此最后剩下的数字是3
*
*
* 思路:每次被删除的数字都有一定的规律 就可以直接计算出最后剩下的数字
* 关系是:f(n) = 0 当n = 0时
* f(n) = [f(n-1,m)+m]%n 当n > 0时
*
* 也就是要想得到n个数字的序列中最后剩下的数字,
* 就要得到n-1个数字的序列中最后剩下的数字,以此类推
* 可使用循环或递归 循环更加简单易懂
* */
public static void main(String[] args) {
No62LastRemaining n = new No62LastRemaining();
System.out.println(n.LastRemaining(5, 3));
}
public int LastRemaining(int n, int m) {
if (n < 1 || m < 1) {
return -1;
}
//i个数字的序列中最后剩下的数字
int last = 0; //当n = 0时
//当n!=0时 则对其进行+m 再对i(当前在含有i个数字的序列)取余
//循环执行到最后一个即可 last就是所求
for (int i = 2; i <= n; i++)
last = (last + m) % i;
return last;
}
}