文章目录
- 1.二维数组找指定元素:
- 2.在原数组的基础上空格换%20
- 3.从后往前输出链表
- 4.前序中序构造二叉树:
- 5.两个栈实现队列:
- 6.从前往后算斐波那契数列f(n)
- 7.跳台阶:
- 8.依次可以从1步到n步:
- 9.旋转数组的最小数字:
- 10.机器人的运动范围:
- 11.剪绳子f(n)的最大分段长度乘积:
- 12.位运算,一个数减1与它求与运算,就相当于把这个数最右边的一个1变成0,统计二进制中有多少个1,就是求可以进行多少次这样的运算
- 13.数值的整数次方
- 14.打印从1到最大的n位数:
- 15. 删除链表中重复的结点
- 16.正则表达式匹配:
- 17表示数值的字符串
- 18.调整数组顺序,使得奇数在偶数前面
- 19.倒数第k个节点
- 20.链表的入口节点:
- 21.反转链表
- 22.合并两个排序的列表,每次到下一个任务与前一个相同,递归
- 23.树的子结构:
- 24.树的镜像:
- 25.对称的二叉树:
- 26.顺时针打印矩阵:
- 27.包含min函数的栈,dataList存放数据栈,minList存放最小值的栈,min变量记录最小值,push时最小栈每次都push当前最小的值,pop时,最小栈pop之后栈顶的元素仍然是数据栈中最小的元素
- 28.栈的压入,弹出序列:
- 29.从上到下打印二叉树:
- 30.二叉搜索树的后序遍历序列:
- 31.二叉树中和为某一值的路径:
- 32.复杂链表的复制:
- 33.二叉搜索树与双向链表:
- 33.序列化和反序列化二叉树:
- 34.字符串的排列:
- 35.数组中出现次数超过一半的数字:
- 36.最小的k个数:
- 37.数据流中的中位数:
- 38.连续子数组的最大和
- 39.整数中1出现的次数:
- 40.把数组排成最小的数(整型数组的数拆起来再组合最小的数)
- 41.第n个丑数(只包含质因子2,3,5):
- 42.第一个只出现一次的字符:
- 43.数组中的逆序对:
- 44.两个链表的第一个公共节点:
- 45.数字在排序数组中出现的次数:
- 46.二叉树的深度
- 47.判断二叉树是否是平衡的:
- 48.数组中只出现一次的数字:
- 49.和为S的连续正数序列:
- 50.和为S的两个数字,输出从小到大的两个数=在返回的list中小的在前,大的在后。
- 51.左旋转字符串:
- 52.翻转单词顺序列:
- 53.扑克牌顺子:
- 54.圆圈中最后剩下的数:
- 55.求1+2+3+...+n,不能利用公式和循环:
- 56.不用加减乘除做加法:
- 57.把字符串转换成整数:
- 58.数组中重复的数字:
- 59.构建乘积数组:
- 60.字符流中第一个不重复的字符:
- 61.二叉树的下一个节点:
- 62.矩形覆盖:
- 63.之字形打印二叉树:
- 64.把二叉树打印成多行:
- 65.二叉搜索树的第K个节点:
- 66.滑动窗口的最大值:
- 67.矩阵中的路径:
1.二维数组找指定元素:
从右上角开始找,比目标大,删除整列,比目标小,删除整行
public class Solution {
public boolean Find(int target, int [][] array) {
int rows=array.length;//表示行数
int cols=array[0].length;//第一行元素个数,即列数
int row=0;
int col=cols-1;
while(row<rows&&col>=0){
if(array[row][col]==target){
return true;
}else if(array[row][col]>target){
col--;
}else row++;
}
return false;//如果没有在循环中经历return退出,则表示没找到target
}
}
2.在原数组的基础上空格换%20
双指针,从后往前,ptr2指向新生成数组最后的元素,ptr1指向原数组的末尾。str中空格也算长度。
stringBuffer的方法setCharAt(int,char)
public class Solution {
public String replaceSpace(StringBuffer str) {
int blanknum=0;
for(int i=0;i<str.length();i++){
if(str.charAt(i)==' ')
blanknum++;
}
int originalStrLen=str.length();
int newStrLen=originalStrLen+blanknum*2;
str.setLength(newStrLen);
int ptr1=originalStrLen-1;
int ptr2=newStrLen-1;
while(ptr1>=0&&ptr2>ptr1){
if(str.charAt(ptr1)==' '){
str.setCharAt(ptr2--,'0');
str.setCharAt(ptr2--,'2');
str.setCharAt(ptr2--,'%');
}else{
str.setCharAt(ptr2--,str.charAt(ptr1));
}
ptr1--;
}
return str.toString();
}
}
3.从后往前输出链表
用栈实现先进后出
import java.util.ArrayList;
import java.util.Stack;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
Stack<Integer> st=new Stack<Integer>();
ArrayList<Integer> result=new ArrayList<Integer>();
while(listNode!=null){
st.push(listNode.val);//自动装箱
listNode=listNode.next;
}
while(!st.empty()){
result.add(st.peek());//不取出
st.pop();//弹出栈
}
return result;
}
}
4.前序中序构造二叉树:
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
int length=pre.length;
return buildTree(pre,in,0,length-1,0,length-1);
}
public TreeNode buildTree(int [] pre,int [] in,int preleft,int preright,int inleft,int inright){
if(preleft>preright) return null;
int mid=inleft;//对右子树的中序左边界变化了
while(mid<inright&&in[mid]!=pre[preleft]) mid++;//通过while找到中序中
TreeNode root=new TreeNode(in[mid]);
root.left=buildTree(pre,in,preleft+1,preleft+mid-inleft,inleft,mid-1);
root.right=buildTree(pre,in,preleft+mid-inleft+1,preright,mid+1,inright);
return root;
}
}
5.两个栈实现队列:
栈中后进来的元素弹出到另一个栈后,变成了底部的元素,也就是后来的元素88
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.size()==0){//只有在栈2为空,栈1不为空时,才能把栈1顶部的元素压入栈2中,否则则先弹出栈2的元素
while(stack1.size()>0){
stack2.push(stack1.pop());
}
}
return stack2.pop();
}
}
6.从前往后算斐波那契数列f(n)
public class Solution {
public int Fibonacci(int n) {
if(n<2){
return n;
}
int frear=1;
int ffront=0;
int fn=0;
for(int i=2;i<=n;i++){//共n-1次求和
fn=frear+ffront;//f(1)+f(0)依次类推
ffront=frear;
frear=fn;
}
return fn;
}
}
7.跳台阶:
起始值变化了,f(1)=1,f(2)=2
public class Solution {
public int JumpFloor(int target) {
if(target<3){
return target;
}
int frear=2;
int ffront=1;
int fn=0;
for(int i=3;i<=target;i++){
fn=frear+ffront;
ffront=frear;
frear=fn;
}
return fn;
}
}
8.依次可以从1步到n步:
f(n)=2^( n-1),根据 f(n)=f(n-1)+f(n-2)+……f(1) + 1,n>=2,做f(n-1)相减,得到f(n)为等比数列
public class Solution {
public int JumpFloorII(int target) {
int res=1;
for(int i=1;i<target;i++){
res=res*2;
}
return res;
}
}
9.旋转数组的最小数字:
分两个指针,index1指向头,index2指向尾
public class Solution {
public int minNumberInRotateArray(int [] array) {
int index1=0;
int index2=array.length-1;
int indexmid=index1;//处理旋转0个元素,已经排序的情况
while(array[index1]>=array[index2]){
if(index2-index1==1){//表示已经找到,index2的位置即最小的数字
indexmid=index2;
break;
}
indexmid=(index1+index2)/2;
if(array[index1]==array[index2]&&array[indexmid]==array[index1]){
return mininorder(array,index1,index2);
}
if(array[indexmid]>=array[index1]){//分清楚mid在前后哪个区间内
index1=indexmid;
}else if(array[indexmid]<=array[index2]){
index2=indexmid;
}
}
return array[indexmid];//找到最小下标后,统一返回
}
//处理两侧和中间相等的情况,顺序查找
public int mininorder(int [] array,int index1,int index2){
int min=array[index1];
for(int i=index1+1;i<=index2;i++){
if(array[i]<min){
min=array[i];
}
}
return min;
}
}
10.机器人的运动范围:
回溯法格式:
方法名:
{递归终止条件
访问到,设置该位被访问过
递归方法(到达下一个位置,该位置的情况等价于前一个位置)
}
public int movingCount(int threshold, int rows, int cols)
{
boolean[][] visited=new boolean[rows][cols];//默认为false
return countingsteps(threshold,rows,cols,0,0,visited);
}
public int countingsteps(int threshold,int rows,int cols,int row,int col,boolean [][] visited){
if (row < 0 || row >= rows || col < 0 || col >= cols
|| visited[row][col] || digitsum(row,col) > threshold) return 0;//不能走到,所以该行,列的步数为0
visited[row][col]=true;//能访问到
return 1+countingsteps(threshold,rows,cols,row+1,col,visited)
+countingsteps(threshold,rows,cols,row-1,col,visited)
+countingsteps(threshold,rows,cols,row,col-1,visited)
+countingsteps(threshold,rows,cols,row,col+1,visited);
}
public int digitsum(int row,int col){
int sum=0;
while(row>0){
sum=sum+row%10;
row=row/10;
}
while(col>0){
sum=sum+col%10;
col=col/10;
}
return sum;
}
11.剪绳子f(n)的最大分段长度乘积:
动态规划,求最大值可以分解为子问题的最大值,各子问题的解有重复,自下而上求解,自上而下分析
public class Solution {
public int cutRope(int target) {
if(target==2){
return 1;
}
if(target==3){
return 2;
}//对特殊情况处理
int [] products=new int[target+1];
products[0]=0;//数组元素从0开始
products[1]=1;
products[2]=2;
products[3]=3;//对4以上只切一刀,1-3的长度不切
for(int i=4;i<=target;i++){
int max=0;
for(int j=1;j<=i/2;j++){
int temp=products[j]*products[i-j];//分解为子问题
if(max<temp){
max=temp;
}
}
products[i]=max;
}
return products[target];
}
}
贪心算法:n>5,尽可能分成3为一块的,n=4,分成2*2
if(to2==0){
return to3*3;
}else if(to3==0){
return to2*2;
}else
{return to3*3*to2*2;}等价于 return (int)(Math.pow(3,to3))*(int)(Math.pow(2,to2));
完整:
public int cutRope(int target) {
if(target==2){
return 1;
}
if(target==3){
return 2;
}
int to3=target/3;
if(target-to3*3==1){//处理最后为剩余长度为4的情况
to3-=1;
}
int to2=(target-to3*3)/2;
return (int)(Math.pow(3,to3))*(int)(Math.pow(2,to2));
}
12.位运算,一个数减1与它求与运算,就相当于把这个数最右边的一个1变成0,统计二进制中有多少个1,就是求可以进行多少次这样的运算
public int NumberOf1(int n) {
int count=0;
while(!n==0)){
count++;
n=n&(n-1);
}
return count;
}
13.数值的整数次方
public double Power(double base, int exponent) {
if((int)base==0&&exponent<0) return 0.0;//对底为0,指数为负的错误情况处理
int absExponent=exponent;
if(absExponent<0) absExponent=(-absExponent);//对负数的指数取绝对值
double result=getResult(base,absExponent);//得到次方结果
if(exponent<0) result=1.0/result;//负数次方取倒数
return result;
}
public double getResult(double base,int exponent){//对指数开方,递归实现
if(exponent==0) return 1;
if(exponent==1) return base;
double result=getResult(base,exponent>>1);//指数除以2
result*=result;
if((exponent&0x1)==1){//判断是否是奇数
result=result*base;
}
return result;
}
14.打印从1到最大的n位数:
全排列输出所有整数,递归对第一位从0到9,然后0的时候到第二位从0到9,依次到下一位,递归终止条件数组长度为n,输出字符型数组
public void print1toMax(int n){
if(n<=0) return;
char[] num=new char[n];
for(int i=0;i<10;i++){
num[0]=i+'0';//数字变字符
recur(num,n,0);
}
}
public void recur(char[] num,int length,int index){
if(index=length-1){//终止条件
printNum(num);
return;
}
for(int i=0;i<10;i++){
num[index+1]=i+'0';//对每一位下位递归
recur(num,length,index+1);
}
}
public void printNum(char[] num){
boolean beginis0=true;//以0开头
int nlength=strlen(num);
for(int i=0;i<nlength;i++){
if(beginis0&&num[i]!=0){//第一个不为0开头,后面的数都输出,该步判断为false
beginis0=false;
}
if(!beginis0){//第一个不为0的数以后都直接从此输出
system.out.print(num[i]);
}
}
}
15. 删除链表中重复的结点
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.next.val==last.val){
while(last.next!=null&&last.next.val==last.val){
last=last.next;
}
pre.next=last.next;
last=last.next;
}else{
pre=last;
last=last.next;
}
}
return Head.next;//pHead???
}
16.正则表达式匹配:
当数组【下标】==非空字符时,需要先判断下标是否越界
public boolean match(char[] str, char[] pattern)
{
if(str==null||pattern==null) return false;
int stringindex=0;//用下标来表示数组元素起始的位置
int patternindex=0;
return corematch(str,pattern,stringindex,patternindex);
}
public boolean corematch(char[] str,char[] pattern,int stringindex,int patternindex){
if(stringindex==str.length&&patternindex==pattern.length){
return true;//都到达末尾匹配成功
}
if(stringindex!=str.length&&patternindex==pattern.length){
return false;//模式先结束,匹配失败
}
if(patternindex+1<pattern.length&&pattern[patternindex+1]=='*'){//判断数组是否越界,对第二个字符是*进行分类处理
if((stringindex!=str.length&&pattern[patternindex]==str[stringindex])||(pattern[patternindex]=='.'&&stringindex!=str.length)){
return corematch(str,pattern,stringindex+1,patternindex+2)//使用一次*前的字符
||corematch(str,pattern,stringindex+1,patternindex)//再次使用*前的字符
||corematch(str,pattern,stringindex,patternindex+2);//使用0次*前的字符
}else{
return corematch(str,pattern,stringindex,patternindex+2);//*前的字符与字符串的第一个字符不匹配,到*后的字符
}
}
if(stringindex!=str.length&&pattern[patternindex]==str[stringindex]||pattern[patternindex]=='.'&&stringindex!=str.length){//对str的下标进行判断,因为已经排除了pattern已经末尾的情况,不需要再对patternindex再判断
return corematch(str,pattern,stringindex+1,patternindex+1);
}
return false;
}
17表示数值的字符串
public int index=0;//两个方法之外的全局变量
public boolean isNumeric(char[] str) {
if(str.length==0) return false;
boolean flag=scanInteger(str);//先对.左边的整数扫描
if(index<str.length&&str[index]=='.'){//.右边的数时无符号整数,为了使index增加,使scan表达式写在或的前面,.左右有无数字都可以
index++;
flag=scanUnsignedInteger(str)||flag;
}
if(index<str.length&&(str[index]=='e'||str[index]=='E')){
index++;
flag=flag&&scanInteger(str);//e后面必须是整数,e前面必须有数字
}
return flag&&index==str.length;//字符必须到字符串的我为
}
public boolean scanInteger(char[] str){
if(index<str.length&&(str[index]=='+'||str[index]=='-')){
index++;
}
return scanUnsignedInteger(str);
}
public boolean scanUnsignedInteger(char[] str){
int start=index;
while(index<str.length&&str[index]>='0'&&str[index]<='9'){
index++;
}
return index>start;//有数字,返回true5
}
18.调整数组顺序,使得奇数在偶数前面
public void reOrderArray(int [] array) {
if(array.length==0) return;
int begin=0;
int end=array.length-1;
while(begin<end){
while(begin<end&&begin<array.length&&((array[begin]&0x1)!=0)){找偶数
begin++;
}
while(begin<end&&end<array.length&&((array[end]&0x1)==0)){找奇数
end++;
}
if(begin<end&&begin<array.length&&end<array.length){
int temp=array[begin];
array[begin]=array[end];
array[end]=temp;
}
}
}
public boolean isEven(int x){可扩展判断函数
return (x&1)==0;
}
方法二:类似冒泡排序,每次都保证第一个是奇数,
public void reOrderArray(int [] array) {
if(array.length==0) return;
for(int i=0;i<array.length;i++){
for(int j=array.length-1;j>i;j--){
if((array[j]&0x1)==1&&(array[j-1]&0x1)==0){
int temp=array[j-1];
array[j-1]=array[j];
array[j]=temp;
}
}
}
}
19.倒数第k个节点
public ListNode FindKthToTail(ListNode head,int k) {
if(head==null||k==0) return null;//倒数0个返回null
ListNode ahead=head;
ListNode behind=null;
for(int i=0;i<k-1;i++){//第一个先走k-1步
if(ahead.next!=null){
ahead=ahead.next;
}else{
return null;
}
}
behind=head;
while(ahead.next!=null){//从k步开始同时走,最后第二个位于n-k+1,就是倒数第k个
ahead=ahead.next;
behind=behind.next;
}
return behind;
}
20.链表的入口节点:
用过的节点尽量在一个函数中再次利用,降低创建新节点的时间,降低时间复杂度
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode meetNode=MeetNode(pHead);
if(meetNode==null) return null;
int nodeNum=1;
ListNode p1=meetNode;
while(p1.next!=meetNode){//统计环中节点数量
p1=p1.next;
nodeNum++;
}
p1=pHead;
for(int i=0;i<nodeNum;i++){找到p1初始位置,头结点往后走环中节点数目的步数
p1=p1.next;
}
ListNode p2=pHead;
while(p1!=p2){
p1=p1.next;
p2=p2.next;
}
return p1;
}
public ListNode MeetNode(ListNode pHead){//慢节点位于头结点的后一个节点,快节点位于头结点的后两个节点,相遇节点在环中
if(pHead==null) return null;
ListNode slow=pHead.next;
if(slow==null) return null;
ListNode fast=slow.next;
while(slow!=null&&fast!=null){
if(slow==fast) return slow;
slow=slow.next;
fast=fast.next;
if(fast!=null){
fast=fast.next;
}
}
return null;
}
21.反转链表
public ListNode ReverseList(ListNode head) {
ListNode reversedHead=null;
ListNode pNode=head;
ListNode pPre=null;
while(pNode!=null){//涉及到一个节点的下一个节点时,首先判断这个节点是不是null
ListNode pnext=pNode.next;//先保存该节点的下一个节点
if(pnext==null){
reversedHead=pNode;
}
pNode.next=pPre;//反转链表方向
pPre=pNode;//下一个节点的的pre要改变
pNode=pnext;//指向下一个节点
}
return reversedHead;
}
22.合并两个排序的列表,每次到下一个任务与前一个相同,递归
public ListNode Merge(ListNode list1,ListNode list2) {
if(list1==null){//任意一个列表为空,返回另外一个list
return list2;
}else if(list2==null){
return list1;
}
ListNode mergeNode=null;
if(list1.val<list2.val){
mergeNode=list1;
mergeNode.next=Merge(list1.next,list2);
}else{
mergeNode=list2;
mergeNode.next=Merge(list1,list2.next);
}
return mergeNode;
}
23.树的子结构:
1.找树A中与树B根节点相同的节点,2判断左右子树是否相等
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
boolean result=false;
if(root1!=null&&root2!=null){
if(equal(root1.val,root2.val)){
result=doesTree1hasTree2(root1,root2);
}
if(!result)
result=HasSubtree(root1.left,root2);//树B的根节点不动,对树A的左右子节点分别去查找其是否有和B的根节点相同的点
if(!result)
result=HasSubtree(root1.right,root2);
}
return result;
}
public boolean doesTree1hasTree2(TreeNode root1,TreeNode root2){
if(root2==null)//B遍历完,找到了
return true;
if(root1==null)//B没有遍历完,A先结束,没找到
return false;
if(!equal(root1.val,root2.val))//当值不相等,此子树的结构在A中以一个根节点来找则没找到,以上为终止条件
return false;
return doesTree1hasTree2(root1.left,root2.left)&&
doesTree1hasTree2(root1.right,root2.right);//其左右节点是否是A中根节点左右子节点相同
}
public boolean equal(double val1,double val2){//对double类型判断相等的处理
if((val1-val2)>-0.0000001&&(val1-val2)<0.0000001){
return true;
}else{
return false;
}
}
24.树的镜像:
对根节点的左右节点进行替换,再对其左右子节点的分别左右子节点进行替换
public void Mirror(TreeNode root) {
if(root==null) return;
if(root.left==null&&root.right==null) return;//到达叶节点时,终止
TreeNode temp=root.left;
root.left=root.right;
root.right=temp;
if(root.left!=null){
Mirror(root.left);
}if(root.right!=null){
Mirror(root.right);
}
}
25.对称的二叉树:
根左右和根右左两种遍历相同时就为对称二叉树
boolean isSymmetrical(TreeNode pRoot)
{
if(pRoot==null) return true;//处理空树
return isSymmetrical(pRoot.left,pRoot.right);
}
boolean isSymmetrical(TreeNode root1,TreeNode root2){
if(root1==null&&root2==null) return true;
if(root1==null||root2==null) return false;
if(root1.val!=root2.val) return false;//两种遍历值不等是,false
return isSymmetrical(root1.left,root2.right)&&//根左右和根右左,左和右对应
isSymmetrical(root1.right,root2.left);
}
26.顺时针打印矩阵:
每次打印一圈,起始点从(0,0)到(1,1)依次往后
public class Solution {
public ArrayList<Integer> printMatrix(int [][] matrix) {
ArrayList<Integer> result=new ArrayList<>();
int rows=matrix.length;
int cols=matrix[0].length;
int start=0;
while(rows>start*2&&cols>start*2){//每次顺时针打印一圈的条件
printInCircle(matrix,result,rows,cols,start);
start++;
}
return result;
}
public void printInCircle(int [][] matrix,ArrayList<Integer> result,int rows,int cols,int start){
int endX=cols-start-1;//终止列下标
int endY=rows-start-1;//终止行下标
for(int i=start;i<=endX;i++){//从左到右打印一行
result.add(matrix[start][i]);
}
if(endY>start){//从上到下打印一列全局
for(int i=start+1;i<=endY;i++){
result.add(matrix[i][endX]);
}
}
if(endX>start&&endY>start){//从右往左打印一行
for(int i=endX-1;i>=start;i--){
result.add(matrix[endY][i]);
}
}
if(endX>start&&start<endY-1){//从下到上打印一列
for(int i=endY-1;i>start;i--){
result.add(matrix[i][start]);
}
}
}
27.包含min函数的栈,dataList存放数据栈,minList存放最小值的栈,min变量记录最小值,push时最小栈每次都push当前最小的值,pop时,最小栈pop之后栈顶的元素仍然是数据栈中最小的元素
private ArrayList<Integer> dataList=new ArrayList<>();
private ArrayList<Integer> minList=new ArrayList<>();
private Integer min=Integer.MAX_VALUE;
public void push(int node) {
dataList.add(node);
if(min>node){
minList.add(node);
min=node;
}else{
minList.add(min);
}
}
public int getSize(){
return dataList.size();
}
public void pop() {
int end=getSize()-1;
dataList.remove(end);
minList.remove(end);
min=minList.get(end-1);
}
public int min() {
return min;
}
28.栈的压入,弹出序列:
每次把入栈序列插入栈时,比较栈顶元素是否和出栈序列的头元素相等,相等则弹出元素,弹出序列的下一个若和入栈序列的顶部又相等则继续弹出。否则则继续按入栈序列压入,直到栈顶元素等于出栈序列当前index的元素
public boolean IsPopOrder(int [] pushA,int [] popA) {
if(pushA.length==0||popA.length==0){
return false;
}
Stack<Integer> s=new Stack<>();
int popIndex=0;
for(int i=0;i<pushA.length;i++){
s.push(pushA[i]);
while(!s.isEmpty()&&s.peek()==popA[popIndex]){
s.pop();
popIndex++;
}
}
return s.isEmpty();//当栈不为空,说明出栈的元素与栈顶的元素不等,即在栈顶之前,所以不是一个出栈序列
}
29.从上到下打印二叉树:
广度优先搜索,用ArrayList模拟队列,remove(index),add在末尾
ArrayList<Integer> result=new ArrayList<>();
ArrayList<TreeNode> queue=new ArrayList<>();
if(root==null){//处理空指针
return result;
}
queue.add(root);
while(queue.size()!=0){
TreeNode temp=queue.remove(0);
if(temp.left!=null){
queue.add(temp.left);
}
if(temp.right!=null){
queue.add(temp.right);
}
result.add(temp.val);
}
return result;
}
30.二叉搜索树的后序遍历序列:
左右根,数组不变,变下标
public boolean VerifySquenceOfBST(int [] sequence) {
int len=sequence.length;
boolean result=Verify(sequence,0,len-1);
return result;
}
public boolean Verify(int [] sequence,int start,int end){
if(sequence==null||sequence.length==0){
return false;
}
int root=sequence[end];
//找出左子树的下标i-1
int i=0;
for(;i<end;i++){
if(sequence[i]>root)
break;
}
//对右子树的值进行判断
int j=i;
for(;j<end;j++){
if(sequence[j]<root)
return false;
}
//对左子树进行递归判断
boolean left=true;
if(i>0){
left=Verify(sequence,0,i-1);
}
//对右子树进行判断
boolean right=true;
if(i<end){
right=Verify(sequence,i,end-1);
}
return (left&&right);
}
31.二叉树中和为某一值的路径:
递归,首先是终止条件,然后判断结果,符合就加入,每次当前节点递归完,回到其父节点节点,list中去掉当前节点
private ArrayList<ArrayList<Integer>> result=new ArrayList<>();
private ArrayList<Integer> list=new ArrayList<>();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
if(root==null) return result;
list.add(root.val);
target-=root.val;
if(target==0&&root.left==null&&root.right==null){
result.add(new ArrayList<Integer>(list));//list是引用类型,不创建新的,在回退过程中会使得list中的数值一个个被remove掉,最后数组里元素为空
}
FindPath(root.left,target);
FindPath(root.right,target);
list.remove(list.size()-1);
return result;
}
32.复杂链表的复制:
分三步,在x.next时,要判断x是否为空
public RandomListNode Clone(RandomListNode pHead)
{
if(pHead==null) return null;
//第一步:复制每个节点
RandomListNode pNode=pHead;
while(pNode!=null){
RandomListNode pCloned=new RandomListNode(pNode.label);
pCloned.next=pNode.next;
pNode.next=pCloned;
pNode=pCloned.next;
}
//指定复制节点的随机指针
pNode=pHead;
while(pNode!=null){
pNode.next.random=pNode.random==null? null:pNode.random.next;
pNode=pNode.next.next;
}
//拆分链表,将偶数位置的节点重新组合在一起,返回
pNode=pHead;
RandomListNode pCloned=pNode.next;
while(pNode!=null){
RandomListNode ClonedNode=pNode.next;//用ClonedNode来当做复制链表的操作指针
pNode.next=ClonedNode.next;
ClonedNode.next=pNode.next==null? null:pNode.next.next;
pNode=pNode.next;
}
return pCloned;//复制链表的需要返回的头指针不改变
}
33.二叉搜索树与双向链表:
中序遍历,左根右
TreeNode tail=null;//当前链表的最后节点
TreeNode listHead=null;//双向链表的头结点
public TreeNode Convert(TreeNode pRootOfTree){
convertSub(pRootOfTree);
return listHead;
}
public void convertSub(TreeNode pRootOfTree){
if(pRootOfTree==null) return;
convertSub(pRootOfTree.left);
if(tail==null){//作初始赋值,都复制为二叉树中最小的节点
tail=pRootOfTree;
listHead=pRootOfTree;
}else{
tail.right=pRootOfTree;//对两个节点连接操作
pRootOfTree.left=tail;
tail=pRootOfTree;//将尾节点移至当前最大的节点
}
convertSub(pRootOfTree.right);
}
33.序列化和反序列化二叉树:
都是前序遍历
public int index=-1;
String Serialize(TreeNode root) {
StringBuffer sb=new StringBuffer();//对新的左右子树添加新的sb,要么是“#,”,要么是“val,”
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++;
String[] strr=str.split(",");
TreeNode node=null;//用于index到“#”的情况,即空节点
if(!strr[index].equals("#")){
node=new TreeNode(Integer.valueOf(strr[index]));
node.left=Deserialize(str);
node.right=Deserialize(str);
}
return node;//当index到子字符串位置为#时,父节点的左右子节点为if前为null的node,最后作为根节点返回
}
34.字符串的排列:
string(char 【】 ch)将字符数组转换为字符串
public ArrayList<String> Permutation(String str) {
List<String> resultList=new ArrayList<>();
if(str.length()==0){
return (ArrayList)resultList;
}
fun(str.toCharArray(),resultList,0);
Collections.sort(resultList);//对字符串List按字典表顺序排列
return (ArrayList)resultList;//强制转换
}
public void fun(char[] ch,List<String> list,int i){
if(i==ch.length-1){//当到ch的长度时,结果串添加
if(!list.contains(new String(ch))){//判断结果中是否有重复
list.add(new String(ch));
return;
}
}else{//回溯法
for(int j=i;j<ch.length;j++){
swap(ch,i,j);
fun(ch,list,i+1);//0-0,1-1,被收录,回到0-0(2),1-2,被收录,回到0-0(2),再回到0-0
swap(ch,i,j);//回到交换前的状态
}
}
}
public void swap(char[] ch,int i,int j){
if(i!=j){
char t=ch[i];
ch[i]=ch[j];
ch[j]=t;
}
}
35.数组中出现次数超过一半的数字:
因为数组超过一半的数字比其他所有数字之和要多,times记录为当前数字超过其他数字之和的个数,只有当超过为0的时候,才设置为下一个数,并把次数设置为1,次数大于0的情况下不改变result,所以是最后设为1的数字。但是有可能最后设为1的数字没有超过数组长度的一半,所以需要检验。
public int MoreThanHalfNum_Solution(int [] array) {
if(CheckInvalidArray(array))//判断数组是否为空
return 0;
int result=array[0];
int len=array.length;
int times=1;
for(int i=1;i<len;i++){
if(times==0){//当次数为0,设置result为下一个
result=array[i];
times=1;
}else if(array[i]==result){
times++;
}else{
times--;
}
}
if(!CheckMoreThanHalf(array,len,result))
result=0;
return result;
}
public boolean CheckInvalidArray(int [] array){
if(array==null||array.length==0){
return true;
}
return false;
}
public boolean CheckMoreThanHalf(int [] array,int len,int result){
int times=0;
for(int i=0;i<len;i++){
if(array[i]==result){
times++;
}
}
if(times*2<=len){//判断是否超过数组长度的一半
return false;
}
return true;
}
36.最小的k个数:
用最大堆存储当前最小的k个数,当最大堆不满k个,就把遍历到的元素加入最大堆,当已经满k个,就把遍历到的数和最大堆的顶(最大的数)来比较
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
ArrayList<Integer> result=new ArrayList<>();
if(input==null||k>input.length||k==0){
return result;
}
PriorityQueue<Integer> maxHeap=new PriorityQueue<Integer>(k,new Comparator<Integer>(){
@Override
public int compare(Integer o1,Integer o2){//入堆的数按此规则(Comparator重写的方法)排序,最大的值在最大堆的顶部
return o2.compareTo(o1);//o2比较o1,最大堆
}
});
for(int i=0;i<input.length;i++){
if(maxHeap.size()!=k){
maxHeap.offer(input[i]);
}else if(maxHeap.peek()>input[i]){
Integer temp=maxHeap.poll();
temp=null;
maxHeap.offer(input[i]);
}
}
for(Integer integer : maxHeap){
result.add(integer);
}
return result;
}
37.数据流中的中位数:
最大堆的值小于最小堆的值,使得中位数要么出现在最大堆顶和最小堆顶的平均值,要么就是最小堆的堆顶
//最小堆
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.compareTo(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++;//数据流采入的数据个数+1
}
public Double GetMedian() {
if(count%2==0){
return (maxHeap.peek()+minHeap.peek())/2.0;
}else{//奇数时,出现在最小堆的堆顶
return minHeap.peek()/1.0;
}
}
38.连续子数组的最大和
public int FindGreatestSumOfSubArray(int[] array) {
if(array==null||(array.length==0)){
return 0;
}
int curSum=0;
int maxSum=0x80000000;//最小的值
for(int i=0;i<array.length;i++){
if(curSum<=0){//当前和为负,最大和肯定不要当前的负数
curSum=array[i];
}else{//当前和为正,把当前的数加上
curSum+=array[i];
}
if(curSum>maxSum){
maxSum=curSum;
}
}
return maxSum;
}
39.整数中1出现的次数:
i是置位点,把整数拆成两部分
若i为百位:
百位为>=2,百位上为1的个数有((a/10)+1)*100
若百位为1,百位上为1的个数有(a/10)*100+b+1
若百位为0,百位上为1的个数有(a/10)*100
这三种情况下((a/10)+1)||(a/10)=(a+8)/10
public int NumberOf1Between1AndN_Solution(int n) {
int count=0;
int i=1;
for(;i<=n;i*=10){
int a=n/i;
int b=n%i;
if(a%10==1){
count=count+((a+8)/10)*i+(b+1);
}else{
count=count+((a+8)/10)*i;
}
}
return count;
}
40.把数组排成最小的数(整型数组的数拆起来再组合最小的数)
public String PrintMinNumber(int [] numbers) {
int len=numbers.length;
String result="";
ArrayList<Integer> list=new ArrayList<>();
for(int i=0;i<len;i++){
list.add(numbers[i]);
}
Collections.sort(list,new Comparator<Integer>(){//内部快排,全部排
@Override
public int compare(Integer str1,Integer str2){//修改参数是Integer
String s1=str1+""+str2;
String s2=str2+""+str1;
return s1.compareTo(s2);//按拼起来数字小的,按此顺序排
}
});
for(int j:list){
result+=j;//Integer+string组成string
}
return result;
}
41.第n个丑数(只包含质因子2,3,5):
丑数乘以2,3,5仍然是丑数,每次都加入最小的丑数后,该下标++,防止丑数重复
public int GetUglyNumber_Solution(int index) {
if(index<=0){
return 0;
}
int i2=0,i3=0,i5=0;
ArrayList<Integer> list=new ArrayList<>();
list.add(1);
while(list.size()<index){
int m2=list.get(i2)*2;
int m3=list.get(i3)*3;
int m5=list.get(i5)*5;
int min=Math.min(m2,Math.min(m3,m5));
list.add(min);
if(min==m2) i2++;
if(min==m3) i3++;
if(min==m5) i5++;
}
return list.get(list.size()-1);
}
42.第一个只出现一次的字符:
利用字母的ASCII码作hash作为数组的index,A-Z对应65-90,a-z对应97-112,中间6个不做处理
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;
}
43.数组中的逆序对:
归并排序的过程中来计算逆序对对数
int count;
public int InversePairs(int [] array) {
count=0;
if(array!=null){
mergeSortUp2Down(array,0,array.length-1);
}
return count;
}
public void mergeSortUp2Down(int [] a,int start,int end){//自上而下的归并排序
if(start>=end)
return;
int mid=(start+end)>>1;//向左取中间值
mergeSortUp2Down(a,start,mid);
mergeSortUp2Down(a,mid+1,end);
merge(a,start,mid,end);
}
public void merge(int [] a,int start,int mid,int end){//将相邻的两组合并
int[] temp=new int[end-start+1];
int i=start,j=mid+1,k=0;
while(i<=mid&&j<=end){
if(a[i]<=a[j]){
temp[k++]=a[i++];
}else{找到逆序对,i到mid的的数都可以和j组成逆序对(i到mid递增)
temp[k++]=a[j++];
count+=(mid+1-i);
count%=1000000007;
}
}
for(;i<=mid;i++){//将剩余的值复制到temp
temp[k++]=a[i];
}
for(;j<=end;j++){
temp[k++]=a[j];
}
for(int s=0;s<temp.length;s++){//将排序好的数组复制给原数组
a[start+s]=temp[s];
}
}
44.两个链表的第一个公共节点:
将第一个链表节点依次存入hashmap中,containsKey是否包含键
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
ListNode current1=pHead1;
ListNode current2=pHead2;
HashMap<ListNode,Integer> map=new HashMap<>();
while(current1!=null){
map.put(current1,null);
current1=current1.next;
}
while(current2!=null){
if(map.containsKey(current2)){
return current2;
}else{
current2=current2.next;
}
}
return null;
}
45.数字在排序数组中出现的次数:
排序数组-》二分查找
public int GetNumberOfK(int [] array , int k) {
int len=array.length;
if(len==0){
return 0;
}
int firstK=getFirstK(array,k,0,len-1);
int lastK=getLastK(array,k,0,len-1);
if(firstK!=-1&&lastK!=-1){
return lastK-firstK+1;
}
return 0;
}
//找出第一个k,递归写法
public 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){//更新end
return getFirstK(array,k,start,mid-1);
}else{
return mid;
}
}
//循环写法while(),更新start,end边界,找最后一个k,更新start
public int getLastK(int [] array,int k,int start,int end){
int mid=(start+end)>>1;
int len=array.length;
while(start<=end){
if(array[mid]>k){
end=mid-1;
}else if(array[mid]<k){
start=mid+1;
}else if(mid+1<len&&array[mid+1]==k){
start=mid+1;
}else{
return mid;
}
mid=(start+end)>>1;
}
return -1;
}
46.二叉树的深度
public int TreeDepth(TreeNode root) {
if(root==null){
return 0;
}
int left=TreeDepth(root.left);
int right=TreeDepth(root.right);
return Math.max(left,right)+1;//子树的深度加上父节点(根节点)的深度
}
47.判断二叉树是否是平衡的:
对左右子树分别递归返回其深度,若已经出现不平衡则返回-1,回到父节点的方法时,直接返回-1,否则返回左右子树的最大深度+1
public boolean IsBalanced_Solution(TreeNode root) {
return getDepth(root)!=-1;
}
public int getDepth(TreeNode root){
if(root==null) return 0;
int left=getDepth(root.left);
if(left==-1) return -1;
int right=getDepth(root.right);
if(right==-1) return -1;
return Math.abs(left-right)>1? -1:(Math.max(left,right)+1);
}
48.数组中只出现一次的数字:
数组中只有两个数字只出现了一次,其他数字都出现了两次,对所有数字进行排序
ArrayList<Integer> list=new ArrayList<>();
public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
Arrays.sort(array);
for(int i=0;i<array.length;i++){
if(i+1<array.length&&array[i]==array[i+1]){//对遇到两个相等的数字时,指针+1,for循环指针+1,相当于到跳过了当前的两个相等的数字
i++;
}else{
list.add(array[i]);
}
}
if(list.size()==2){
num1[0]=list.get(0);
num2[0]=list.get(1);
}
}
49.和为S的连续正数序列:
滑动窗口,先确定求和值,再根据和与sum的大小比较,确定边界范围,两个边界从左侧向右扩展
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> result=new ArrayList<>();
int plow=1,phigh=2;
while(plow<phigh){
int cur=(plow+phigh)*(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++;
}else if(cur<sum){
phigh++;
}else{
plow++;
}
}
return result;
}
50.和为S的两个数字,输出从小到大的两个数=在返回的list中小的在前,大的在后。
和相同的情况下越两边的数相乘越小。两边夹逼。
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> result=new ArrayList<>();
if(array==null||array.length<2){
return result;
}
int i=0,j=array.length-1;
while(i<j){
if(array[i]+array[j]==sum){
result.add(array[i]);
result.add(array[j]);
i++;
j--;
return result;//找到第一组就返回
}else if(array[i]+array[j]<sum){
i++;
}else{
j--;
}
}
return result;//若经过while循环没有返回,说明没有找到这样的两个数字,返回空list,即一开始创建的result
}
51.左旋转字符串:
左移位n位,XY转换为为YX,(YX)=(XTYT)T,三次reverse
public String LeftRotateString(String str,int n) {
char[] cha=str.toCharArray();//转换为char【】,以便翻转
if(cha.length<n) return "";
reverse(cha,0,n-1);
reverse(cha,n,cha.length-1);
reverse(cha,0,cha.length-1);
StringBuffer sb=new StringBuffer(cha.length);//sb把char拼接起来,再转换成string
for(char c:cha){
sb.append(c);
}
return sb.toString();
}
public void reverse(char[] cha,int left,int right){
char temp;
while(left<right){
temp=cha[left];
cha[left]=cha[right];
cha[right]=temp;
left++;
right--;
}
}
52.翻转单词顺序列:
用空格分离,sb拼接string【】的元素,找出加空格(“空格”)的i的规律
public String ReverseSentence(String str) {
if(str.trim().equals("")) return str;
String[] str1=str.split(" ");
int len=str1.length;
StringBuffer sb=new StringBuffer();
for(int i=0;i<len;i++){
sb.append(str1[len-i-1]);
if(i<len-1){
sb.append(" ");
}
}
return sb.toString();
}
53.扑克牌顺子:
用d数组来记录每个数字出现的个数
public boolean isContinuous(int [] numbers) {
if(numbers==null||numbers.length==0)
return false;
int[] d=new int[14];
d[0]=0;
int max=-1;
int min=14;
for(int i=0;i<numbers.length;i++){
d[numbers[i]]++;
if(numbers[i]==0){//0不计入最大值和最小值
continue;
}
if(d[numbers[i]]>1){//出现对子就不是顺子
return false;
}
if(numbers[i]>max){
max=numbers[i];
}
if(numbers[i]<min){
min=numbers[i];
}
}
if(max-min>=5){//总共5个数,最大值和最小值之间必须满足此要求
return false;
}
return true;
}
54.圆圈中最后剩下的数:
f(n,m)=0(n=1),=f(n-1,m)(n>1)
// 基于循环的实现
public int LastRemaining_Solution(int n, int m) {
if(n<1||m<1){//编号0-n-1,数数0-m-1,处理无意义的情况
return -1;
}
int last=0;
for(int i=2;i<=n;i++){
last=(last+m)%i;
}
return last;
}
55.求1+2+3+…+n,不能利用公式和循环:
采用递归实现循环
public int Sum_Solution(int n) {
int ans=n;
boolean t=(ans!=0)&&((ans+=Sum_Solution(n-1))!=0);//&&左边当ans==0时,右边的Sum(-1)就不会执行,右边!=或者==0都可以,不产生实际作用
return ans;
}
56.不用加减乘除做加法:
加法分三步:1.原来的两个数各位相加,不算进位(异或);2.原来的两个数计算进位(与+向左移1位)3.第一步和第二步得到的数作为新的两个数,重复1,2两步,直到进位为0,求和的结果就是第一步得到的数。
public int Add(int num1,int num2) {
int temp;
while(num2!=0){
temp=num1^num2;
num2=(num1&num2)<<1;
num1=temp;
}
return num1;
}
57.把字符串转换成整数:
去空后,str转换为char[],(int)(字符-‘0’)把与0的ascii码差值变成int
public int StrToInt(String str) {
if(str==null||str.length()==0||str.trim().equals(""))//对字符串为空的处理
return 0;
char[] ch=str.trim().toCharArray();
int flag=1,res=0,start=0;//flag记录正负,start记录数字开始的下标
if(ch[0]=='+')
start=1;
if(ch[0]=='-'){
start=1;
flag=-1;
}
for(int i=start;i<ch.length;i++){
if(ch[i]>'9'||ch[i]<'0'){//比较两个字符的ascll码值
return 0;
}
if(flag==1&&res*10>Integer.MAX_VALUE-(int)(ch[i]-'0')){//不能有比最大值还大的值,所以移项
return 0;
}
if(flag==-1&&res*10*flag<Integer.MIN_VALUE+(int)(ch[i]-'0')){
return 0;
}
res=res*10+(int)(ch[i]-'0');//当都满足合法数值的条件时
}
return res*flag;
}
58.数组中重复的数字:
用hashmap来存储每个数字,是否重复用containsKey
public boolean duplicate(int numbers[],int length,int [] duplication) {
HashMap<Integer,String> map=new HashMap<>();
boolean flag=true;
for(int i=0;i<length;i++){
if(map.containsKey(numbers[i])){
duplication[0]=numbers[i];
break;
}
map.put(numbers[i],null);
}
if(flag=true&&duplication[0]!=-1){
return true;
}
return false;
}
解法2:用boolean数组来表示是否访问过,第一次遍历,把数组元素置为1,有重复元素就会再次访问该位的Boolean
boolean[] k = new boolean[length];
for (int i = 0; i < k.length; i++) {
if (k[numbers[i]] == true) {
duplication[0] = numbers[i];
return true;
}
k[numbers[i]] = true;
}
return false;
}
59.构建乘积数组:
对B【0】和B【n-1】特殊处理,对中间的拆成两半,分别得出积再相乘
public int[] multiply(int[] A) {
int[] B=new int[A.length];
int n=A.length;
B[0]=1;
B[n-1]=1;
for(int i=1;i<n;i++){
B[0]*=A[i];
B[n-1]*=A[i-1];
}
for(int i=1;i<n-1;i++){
int left=1,right=1;
for(int j=0;j<i;j++){
left*=A[j];
}
for(int j=i+1;j<n;j++){
right*=A[j];
}
B[i]=left*right;
}
return B;
}
60.字符流中第一个不重复的字符:
用hashmap存放字符及其出现的次数,containsKey方法,每次插入一个字符,用ArrayList来存储字符
Character是char的包装类,就像Integer和int ,以及Long和long一样。
Character是char的包装类,注意它是一个类,提供了很多方法的
//
//Insert one char from stringstream
HashMap<Character,Integer> map=new HashMap<>();
ArrayList<Character> list=new ArrayList<>();
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()
{
for(char key:list){//加强for,对list的每一个元素
if(map.get(key)==1){//只出现一次
return key;
}
}
return '#';
}
61.二叉树的下一个节点:
求当前节点中序遍历的下一个节点
public TreeLinkNode GetNext(TreeLinkNode pNode)
{
if(pNode==null) return null;
if(pNode.right!=null){//右子树不为空,则为其右节点的左子节点(直到该节点为叶节点)
TreeLinkNode pNext=pNode.right;
while(pNext.left!=null){
pNext=pNext.left;
}
return pNext;
}
while(pNode.next!=null&&pNode.next.left!=pNode){//当右节点为空时,查找满足当前节点为其父节点的左子节点的节点
pNode=pNode.next;
}
return pNode.next;//其下一个节点为查找到的节点的父节点,并包含了无右节点的根节点的下一个节点(null)
}
62.矩形覆盖:
n>=3时时斐波拉契数列,竖着放一块,剩下f(n-1),横着放一块,下一块确定,剩下f(n-2)两者相加。
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);
}
63.之字形打印二叉树:
用两个栈来分别装入奇数层和偶数层节点,奇数层遍历的时候,将奇数层的左右依次进入偶数层的栈,这样偶数层出栈时就是右先出左后出,同理,遍历偶数层的时候,右左依次进入奇数层的栈,奇数层出栈的时候,从左往右。
注意:因为stack可以push(null),所以如果不加判空处理,在最后一层的时候,会进入下一层本来不存在的层,s1/s2有null值,而不为空,导致创建了一个空的list,把s1/s2的nullpop出来以后,会被加入res中,导致结果不对,因此要加入判空处理。(对栈:stack.empty(),对ArrayList.isEmpty())
public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
int layer=1;
Stack<TreeNode> s1=new Stack<>();
s1.push(pRoot);
Stack<TreeNode> s2=new Stack<>();
ArrayList<ArrayList<Integer>> res=new ArrayList<>();
while(!s1.empty()||!s2.empty()){
if(layer%2==1){
ArrayList<Integer> list=new ArrayList<Integer>();
while(!s1.empty()){
TreeNode node=s1.pop();
if(node!=null){
System.out.print(node.val);
list.add(node.val);
s2.push(node.left);
s2.push(node.right);
}
}
if(!list.isEmpty()){//判空
layer++;
System.out.println();
res.add(list);
}
}else{
ArrayList<Integer> list=new ArrayList<>();
while(!s2.empty()){
TreeNode node=s2.pop();//如果不判空,最后会将nullpush出来,后不执行if,跳出while循环,会加入空的list
if(node!=null){
System.out.print(node.val);
list.add(node.val);
s1.push(node.right);
s1.push(node.left);
}
}
if(!list.isEmpty()){//判空
layer++;
System.out.println();
res.add(list);
}
}
}
return res;
}
64.把二叉树打印成多行:
递归写法(深度优先),根节点的左子树,左节点的左子树,左子树的左1,左1的空空,左子树的右1,直到根节点的左子树完全遍历完,再遍历右子树,需要根据当前深度给对应层级的ArrayList添加元素,ArrayLIST下标从0开始,深度从1开始
ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> list=new ArrayList<>();
depth(pRoot,1,list);
return list;
}
public void depth(TreeNode pRoot,int depth,ArrayList<ArrayList<Integer>> list){
if(pRoot==null) return;//终止条件
if(depth>list.size()){//深度超过size,创建一个空的ArrayList
list.add(new ArrayList<Integer>());
}
list.get(depth-1).add(pRoot.val);//根据当前深度来给相应的ArrayList《Integer》增加元素
depth(pRoot.left,depth+1,list);//对左子树,深度+1
depth(pRoot.right,depth+1,list);
}
65.二叉搜索树的第K个节点:
中序遍历即可把一个二叉搜索树按从小到大遍历,将数值复制到数组中,以便找到第k小的数,同时中序遍历的时候,放入hashmap中,最后遍历hashmap,寻找符合第k小的TreeNode。
public class Solution {
int[] a=new int[10];
int i=0;
HashMap<TreeNode,Integer> map=new HashMap<>();
TreeNode KthNode(TreeNode pRoot, int k)
{
if(k<=0) return null;
mid(pRoot);
for(Map.Entry<TreeNode,Integer> mapEntry : map.entrySet()){
if(mapEntry.getValue().equals(a[k-1])){
return mapEntry.getKey();
}
}
return null;
}
public void inorder(TreeNode pRoot){
if(pRoot==null) return;
inorder(pRoot.left);
map.put(pRoot,pRoot.val);
a[i++]=pRoot.val;
inorder(pRoot.right);
}
}
66.滑动窗口的最大值:
两重循环:大循环控制滑动窗口的边界,内循环找出每个滑动窗口内的最大值
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> list=new ArrayList<>();
if(size==0) return list;
for(int i=0;i<=num.length-size;i++){
int max=0;
for(int j=i;j<i+size;j++){
if(num[j]>max){
max=num[j];
}
}
list.add(max);
}
return list;
}
67.矩阵中的路径:
回溯法,用boolean数组记录每个元素的访问状态
public class Solution {
public boolean hasPath(char[] matrix, int rows, int cols, char[] str)
{
boolean[] flag=new boolean[matrix.length];
for(int i=0;i<rows;i++){//对矩阵中的每一个元素都作为起始点,是否可能形成一条路径
for(int j=0;j<cols;j++){
if(judge(matrix,i,j,rows,cols,str,0,flag)){
return true;
}
}
}
return false;
}
public boolean judge(char[] matrix,int i,int j,int rows,int cols,char[] str,int k,boolean[] flag){
int index=i*cols+j;//计算当前行列的元素在一维数组中的位置
if(i<0||j<0||i>=rows||j>=cols||matrix[index]!=str[k]||flag[index]==true)//如果元素超出矩阵的边界,或者当前位置的元素和路径中的已经不相等或者当前已经访问过了,就返回,表示这条路走不通
return false;
if(k==str.length-1)//每一个元素都对应上且到了路径中的最后一个数,则找到了一条可行的路径
return true;
flag[index]=true;
//对当前元素的左右上下分别递归查询,只要从当前元素出发向上下左右的任意一条路径可行,就返回true
if(judge(matrix,i-1,j,rows,cols,str,k+1,flag)||
judge(matrix,i+1,j,rows,cols,str,k+1,flag)||
judge(matrix,i,j+1,rows,cols,str,k+1,flag)||
judge(matrix,i,j-1,rows,cols,str,k+1,flag)){
return true;
}
flag[index]=false;//从index出发的所有路径都不符合,则在主程序两层循环到达下一个元素作为起始元素之前,把当前元素设置未被访问,并当前元素起始的路径都不符合,返回false
return false;
}
}