16、请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径,路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串”bcced”的路径,但是矩阵中不包含”abcb”路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
package cn.ctgu.offer;
/*
* 题目:
* 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。
* 路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。
* 如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。
* 例如 a b c e s f c s a d e e 这样的3 X 4 矩阵中包含一条字符串"bcced"的路径
* 但是矩阵中不包含"abcb"路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。
*
* 思路:
* 1、采用回溯法
* 2、将matrix字符串模拟映射为一个字符矩阵(但并不实际创建一个矩阵)
* 3、取一个boolean[matrix.length]标记某个字符是否已经被访问过
* 3、如果没找到结果,需要将对应的boolean标记值置回false,返回上一层进行其他分路的查找
*
* */
public class HashStrPath {
public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
{
boolean[] visited=new boolean[matrix.length];
for(int i=0;ifor(int j=0;jif(searchFromHere(matrix,rows,cols,i,j,0,str,visited))
return true;
}
}
return false;
}
private boolean searchFromHere(char[] matrix, int rows, int cols, int r, int c, int index, char[] str,
boolean[] visited) {
//matrix[r*cols+c]!=str[index]这是判断位置(c,r)是否为字符串所在位置
if(r<0 || r>=rows || c<0 ||c>=cols || matrix[r*cols+c]!=str[index] || visited[r * cols + c])
return false;
if(index==str.length-1)
return true;
visited[r*cols+c]=true;
if (searchFromHere(matrix,rows,cols,r - 1,c,index + 1,str,visited) ||
searchFromHere(matrix,rows,cols,r,c -1,index + 1,str,visited) ||
searchFromHere(matrix,rows,cols,r + 1,c,index + 1,str,visited) ||
searchFromHere(matrix,rows,cols,r,c + 1,index + 1,str,visited))
return true;
visited[r*cols+c]=false;
return false;
}
}
17、给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
package cn.ctgu.offer;
/*
* 给定一个double类型的浮点数base和int类型的整数exponent。
* 求base的exponent次方。
*
*
* */
public class IntegerPower {
public double Power(double base, int exponent) {
return Math.pow(base, exponent);
}
/*手写
*
* 1.全面考察指数的正负、底数是否为零等情况。
* 2.写出指数的二进制表达,例如13表达为二进制1101。
* 3.举例:10^1101 = 10^0001*10^0100*10^1000。
* 4.通过&1和>>1来逐位读取1101,为1时将该位代表的乘数累乘到最终结果。
*
* */
public double Power2(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);
}
public static void main(String[]args) {
IntegerPower num=new IntegerPower();
System.out.println(num.Power2(12.34d, -4));
}
}
18、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的运气如何, 如果牌能组成顺子就输出true,否则就输出false。
package cn.ctgu.offer;
import java.util.Arrays;
/*
* 题目:
* 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的运气如何, 如果牌能组成顺子就输出true,否则就输出false。
*
*
* 思路:
* 1、当牌不等于5张的时候返回false
* 2、对排进行排序(方便统计王(0)的数量以及比较是否有相同的牌,前后比较即可)
* 3、判断第一张非0的牌和最后一张非0的牌的差是否超过4,如果超过4则不可能是顺子
*
* */
public class IsContinu {
public boolean isContinuous(int [] numbers) {
if(numbers.length!=5) {
return false;
}
//排序
Arrays.sort(numbers);
//统计王的个数
int res=0;
for(int i=0;iif(numbers[i]==0) {
res=res+1;
continue;
}
//当王的数量小于等于3张的时候才需要判断,等于4张的时候一定是顺子
//判断第一张非0的牌和最后一张非0的牌的差值吃否超过4
//判断前后两张牌是否相等,相等则不可能是顺子
//此时的numbers[i]为第一个不为0的数,因为前面将为0的数据都遍历完了(continue的作用是跳出本轮循环,继续下一轮,所以0没有遍历完之前不会执行下面的语句)
if(res!=4&&(numbers[numbers.length-1]-numbers[i]>4||numbers[i]==numbers[i+1])) {
return false;
}else {
return true;
}
}
return true;
}
}
19、请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串”+100”,”5e2”,”-123”,”3.1416”和”-1E-16”都表示数值。但是”12e”,”1a3.14”,”1.2.3”,”+-5”和”12e+4.3”都不是。
package cn.ctgu.offer;
/*
* 题目:
* 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。
* 例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。
* 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
*
*
* 思路:
* 1、先扫描字符是否是以+、-开头,如果是则判断下一个字符是否为数字,只有数字才能通过,否则该字符串不表示数值;
* 如果不是+、-,则判断它是否是数字
* 2、如果是数字则进行下一个字符判断,判断它是否为数字,如果不是则进行"."判断和"E、e"判断,反正第二个字符只能为数字或者"E、e"(第一个字符如果为'+、-'则第二个字符只能为数字)
* 如果第一个字符为数字,则第二个字符可以为('-、E、e、.')
*
*
* */
public class IsNumber {
private int index=0;
public boolean isNumberic(char[] str) {
if(str.length<1)
return false;
boolean flag=scanInteger(str);
if(index<str.length && str[index]=='.') {
index++;
flag=scanUnsignedInteger(str)||flag;//字符如果不为数字则返回false
}
if(index<str.length && (str[index]=='E'||str[index]=='e')) {
index++;
flag=flag && scanInteger(str);
}
return flag && index==str.length;
}
//判断字符是否是以+或-号开头
private boolean scanInteger(char[] str) {
if(index<str.length &&(str[index]=='+'||str[index]=='-'))
index++;
//如果不是+、-就判断是否为数字
return scanUnsignedInteger(str);
}
private boolean scanUnsignedInteger(char[] str) {
int start=index;
while(index<str.length && str[index]>='0'&& str[index]<='9')
index++;
return start<index;//是否存在整数
}
}
20、输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。说明:二叉搜索树的根节点大于左子树,右节点大于根节点
package cn.ctgu.offer;
/*
* 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。
* 假设输入的数组的任意两个数字都互不相同。
* 说明:二叉搜索树的根节点大于左子树,右节点大于根节点
*
* 思路:
* 1、采用递归
* 2、去掉根节点,也就是数组的最后一个元素,小于最后一个节点的为左子树,大于根节点的为右子树
* 3、如果左子树和右子树都满足左节点小于根节点、右节点大于根节点则为true,否则为true
*
*
* */
public class JudgeBST {
public boolean VerifySquenceOfBST(int [] sequence) {
if(sequence.length==0) {
return false;
}
return IsBst(sequence,0,sequence.length-1);
}
public boolean IsBst(int[]sequence,int start,int end ) {
if(end<=start) {//当只有一个元素时,则为真
return true;
}
int i=start;
for(;iif(sequence[i]>sequence[end]) {//当找到某个节点大于根节点,则从该元素开始直到end-1(倒数第二个元素),都为左子树
break;
}
}
for(int j=i;j//如果右子树中存在小于根节点的元素,则返回false,即该数组不是二叉搜索树的后序遍历
if(sequence[j]return false;
}
}
return IsBst(sequence,start,i-1) && IsBst(sequence,i,end-1);
}
}
21、一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法
package cn.ctgu.offer;
/*
* 一只青蛙一次可以跳上1级台阶,也可以跳上2级。
* 求该青蛙跳上一个n级的台阶总共有多少种跳法
*
* 思路:当前台阶的跳法总数=当前台阶后退一阶的台阶的跳法总数+当前台阶后退二阶的台阶的跳法总数
*
*
* 使用动态规划来求解该问题,动态规划适合求解最优解问题。
* 1、先构造出最优子结构
* 2、建立递归关系
* 3、计算最优值
* 4、求解最优解
*
* */
public class JumpFloor {
public int JumpFloors(int target) {
if(target==1) {
return 1;
}
if(target==2) {
return 2;
}
int one=1;//当前台阶后退二阶的台阶的跳法总数(初始值当前台阶是第3阶)
int two=2;//当前台阶后退一阶的台阶的跳法总数(初始值当前台阶是第3阶)
int result=0;// 当前台阶的跳法总数
for(int i=2;i//后退一阶在下一次迭代变为后退两阶
two=result;//当前台阶在下一次迭代变为后退一阶
}
return result;
}
public static void main(String[]args) {
JumpFloor dy=new JumpFloor();
System.out.println(dy.JumpFloors(3));
}
}
22、一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
package cn.ctgu.offer;
/*
*一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。
*求该青蛙跳上一个n级的台阶总共有多少种跳法。
*
* 思路:设跳上第n阶的跳法为f(n)
* 即 f(n)=f(n-1)+f(n-2)+f(n-3)+...+f(1)+1
*
* 1就是直接跳上第n阶楼梯的跳法
*
* */
public class JumpFloor2 {
public int JumpFloor(int target) {
int[]ways=new int[target+1];
ways[0]=1;
ways[1]=1;//跳上一级阶梯有一种跳法
if(target==0) {
return 0;
}
else {
for(int i=2;i<=target;i++) {
ways[i]=0;//相当于那个1,也就是直接跳上第n阶楼梯的跳法
for(int j=0;jreturn ways[target];
}
}
public static void main(String[]args) {
JumpFloor2 dy=new JumpFloor2();
System.out.println(dy.JumpFloor(5));
}
}
23、随机指定一个数m,让编号为0的小朋友开始报数。每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中。从他的下一个小朋友开始,继续0…m-1报数….这样下去….直到剩下最后一个小朋友。可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
package cn.ctgu.offer;
import java.util.LinkedList;
/*
* 题目:
* 随机指定一个数m,让编号为0的小朋友开始报数。
* 每次喊到m-1的那个小朋友要出列唱首歌,然后可以在礼品箱中任意的挑选礼物,并且不再回到圈中。
* 从他的下一个小朋友开始,继续0...m-1报数....这样下去....直到剩下最后一个小朋友。
* 可以不用表演,并且拿到牛客名贵的“名侦探柯南”典藏版(名额有限哦!!^_^)。
* 请你试着想下,哪个小朋友会得到这份礼品呢?(注:小朋友的编号是从0到n-1)
*
*
* 思路:
* 1、将所有的数封装成一个LinkedList
* 2、每次删除的都是第m-1个元素,它的索引可以通过公式:index=(index+m-1)%(环的长度)得到
* 3、index是指开始那个元素的索引,比如,因为每次报数都是从0开始,m为2的话删除的必定为1(m=2,2-1=1)
* 4、将原始索引为1的删除了的话,下一个开始的元素报0的数为原始索引为2的元素(由于是链表,则删除了索引为1的元素
* 后面的元素自然前移,即原来索引为2的元素自然为1,也就是被删除的原索引本身)
*
* */
public class LastRemain {
public int LastRemaining_Solution(int n, int m) {
LinkedListlist=new LinkedList();
//将所有的数封装到LinkedList中
for(int i=0;ilist.add(i);
}
//删除元素
int index=0;
while(list.size()>1) {
index=(index+m-1)%list.size();
list.remove(index);
}
return list.size()==1?list.get(0):-1;
}
}
24、例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。
package cn.ctgu.offer;
/*
* 题目:
* 例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。
*
* 思路:
* 1、将字符串转成字符串数组
* 2、左移k位就把前k位取出来接在原数组的后面
*
*
* */
public class LeftRotateStr {
public String LeftRotateString(String str,int n) {
int len=str.length();
if(len==0) {
return "";
}
if(n>len) {
n=n-len;
}
char[]strArray=str.toCharArray();
char[]newArray=new char[len];
for(int i=n;ifor(int j=0;jreturn String.valueOf(newArray);
}
public static void main(String[]args) {
LeftRotateStr solution=new LeftRotateStr();
String s="abcXYZdef";
String str=solution.LeftRotateString(s, 3);
System.out.println(str);
}
}
25、在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
/*两种思路
一种是:
把每一行看成有序递增的数组,
利用二分查找,
通过遍历每一行得到答案,
时间复杂度是nlogn
*/
public class Solution {
public boolean Find(int [][] array,int target) {
for(int i=0;iint 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(target1;
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 [][] array,int target) {
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;
}
}
26、给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
package cn.ctgu.offer;
import java.util.ArrayList;
/*
* 题目:
* 给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。
* 例如,如果输入数组{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]}。
*
* 思路:
* 1、每隔一个窗口长度找出最大的值
* 2、将最大的值添加进列表
* */
public class MaxInWindow {
public ArrayList maxInWindows(int [] num, int size)
{
ArrayListlist=new ArrayList();
if(size>num.length||size==0)
return list;
for(int i=0;i<=num.length-size;i++) {
int max=num[i];
for(int j=i+1;jif(num[j]>max) {
max=num[j];
}
}
list.add(max);
}
return list;
}
}
27、归并排序算法
package cn.ctgu.offer;
import java.util.Arrays;
/*
* 归并排序
*
* */
public class MergeSort {
public static void sort(int []arr) {
//排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
int[] temp=new int[arr.length];
sort(arr,0,arr.length-1,temp);
}
private static void sort(int[] arr, int left, int right, int[] temp) {
if(leftint mid=(left+right)/2;
//左边归并排序,使得左子序列有序
sort(arr,left,mid,temp);
//右边归并排序,使得右子序列有序
sort(arr,mid+1,right,temp);
//将两个有序子数组合并操作
merge(arr,left,mid,right,temp);
}
}
private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
//左序列指针
int i=left;
//右序列指针
int j=mid+1;
//临时数组指针
int t=0;
while(i<=mid && jif(arr[i]<=arr[j]) {
temp[t]=arr[i];
t++;
i++;
}else{
temp[t]=arr[j];
t++;
j++;
}
}
while(i<=mid) {//将左边剩余元素填充进temp中
temp[t]=arr[i];
t++;
i++;
}
while(j<=right) {//将右序列剩余元素填充进temp中
temp[t]=arr[j];
t++;
j++;
}
t=0;
//将temp中的元素全部拷贝到原数组中
while(left<=right) {
arr[left]=temp[t];
left++;
t++;
}
}
public static void main(String []args){
int []arr = {9,8,7,6,5,4,3,2,1};
sort(arr);
System.out.println(Arrays.toString(arr));
}
}
28、输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
package cn.ctgu.offer;
/*
* 题目:
* 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
* 例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
*
* 思路:
* 两两比较,自定义一个比较大小的函数
* 比较两个字符串s1, s2大小的时候,先将它们拼接起来
* 比较s1+s2,和s2+s1那个大,如果s1+s2大,那说明s2应该放前面
* 所以按这个规则,s2就应该排在s1前面。
*
* */
public class MinArrayNumber {
public String PrintMinNumber(int [] numbers) {
String str="";
for(int i=0;ifor(int j=i+1;jint a=Integer.valueOf(numbers[i]+""+numbers[j]);
int b=Integer.valueOf(numbers[j]+""+numbers[i]);
if(a>b) {
int t=numbers[i];
numbers[i]=numbers[j];
numbers[j]=t;
}
}
}
for(int i=0;ireturn str;
}
public static void main(String[]args) {
MinArrayNumber solution=new MinArrayNumber();
int[] num= {3,32,321};
String str=solution.PrintMinNumber(num);
System.out.println(str);
}
}
29、定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
package cn.ctgu.offer;
import java.util.Iterator;
import java.util.Stack;
/*
* 定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
*
*
*
*
* */
public class MinStack {
Stack<Integer> stack=new Stack<Integer>();
public void push(int node) {
stack.push(node);
}
public void pop() {
stack.pop();
}
public int top() {
return stack.peek(); //栈顶元素(也就是最后一个元素)
}
public int min() {
int min = stack.peek();
int tmp=0;
Iterator<Integer> iterator = stack.iterator();
while (iterator.hasNext()){
tmp = iterator.next();
if (min>tmp){
min = tmp;
}
}
return min;
}
}
30、地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
package cn.ctgu.offer;
/*
* 题目:
* 地上有一个m行和n列的方格。
* 一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格
* 但是不能进入行坐标和列坐标的数位之和大于k的格子。
* 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。
* 但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
*
* 思路:
* 1、回溯法
* 2、从(0,0)开始走,每成功走一步标记当前位置为true,然后从当前位置往四个方向探索
* 返回1+4个方向的探索值之和
* 3、探索时,判断当前结点是否可达的标准为:
* 1)当前结点在矩阵内
* 2)当前结点未被访问过
* 3)当前结点满足limit限制
*
* */
public class MoveCount {
public int movingCount(int threshold, int rows, int cols) {
boolean[][]visited=new boolean[rows][cols];
return countingSteps(threshold,rows,cols,0,0,visited);
}
private int countingSteps(int limit, int rows, int cols, int r, int c, boolean[][] visited) {
if(r<0||r>=rows||c<0||c>=cols||visited[r][c]||bitSum(r)+bitSum(c)>limit)
return 0;
visited[r][c]=true;
return countingSteps(limit,rows,cols,r - 1,c,visited)
+ countingSteps(limit,rows,cols,r,c - 1,visited)
+ countingSteps(limit,rows,cols,r + 1,c,visited)
+ countingSteps(limit,rows,cols,r,c + 1,visited)
+ 1;
}
private int bitSum(int t) {
int count=0;
while(t!=0) {
count=count+t%10;
t=t/10;
}
return count;
}
}