-
二维数组的查找
-
描述:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
-
错误思路:第一列从上往下依次比较,找到比要寻找整数大的的上一层,进行遍历
-
错误反省:错将条件当成每一行的起始都比之前的大
-
解题思路:从左下角开始与目标元素进行对比,若目标元素大,列数加一;若目标元素小,行数减一
-
题解
public class Solution { public boolean Find(int target, int [][] array) { if(array.length==0) { return false; } int i=0;j=array.length-1; while(i<array[0].length&&j>=0) { if(target==array[i][j]) { return true; } else if(target>array[i][j]) { i++; } else { j--; } } return false; } }
-
扩展题:序号2
-
-
回文数组
-
描述:对于一个给定的正整数组成的数组 a[] ,如果将 a 倒序后数字的排列与 a 完全相同,我们称这个数组为“回文”的。
例如, [1, 2, 3, 2, 1] 的倒序是他自己,所以是一个回文的数组;而 [1, 2, 3, 1, 2] 的倒序是 [2, 1, 3, 2, 1] ,所以不是一个回文的数组。
对于任意一个正整数数组,如果我们向其中某些特定的位置插入一些正整数,那么我们总是能构造出一个回文的数组。
输入一个正整数组成的数组,要求你插入一些数字,使其变为回文的数组,且数组中所有数字的和尽可能小。输出这个插入后数组中元素的和。
-
思路:从开始的数组两边进行比较,直至两个指针相等或前面的指针大于后面的指针,指针所指向的元素如果不相等,添加较小的元素,如果较小的元素在后面,除移动以后元素外无其他操作,若较小元素在前面,较大的指针需要加一,然后两个指针同时减一,最后计算数组之和
-
解决算法:动态规划-3.1看算法导论的二叉搜索树,红黑树,b树,动态规划
-
-
删除字符串中的空格-字符串
-
描述:请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
-
思路:
- 使用java自带的toString.replace(" “,”%20")
- 从后往前遍历,遇见空格把之后的元素向后移动两个位置,然后赋值,但这个算法的时间复杂度较高
- 创建一个新的字符串
-
题解
public class Solution { public String replaceSpace(StringBuffer str) { nowlength=str.length()-1; for(int j=str.length()-1;j>=0;j--) { if(str.charAt(j)==' ') { nowlength=nowlength+2; str.setLength(nowlength+1); for(int i=nowlength;i>j+2;i--) { str.setCharAt(i,str.charAt(i+2)) } str.setCharAt(j,'%'); str.setCharAt(j+1,'2'); str.setCharAt(j+2,'0'); j=j+2; } } return str.toString(); } }
-
-
从尾到头打印链表
-
描述:输入一个链表,按链表从尾到头的顺序返回一个ArrayList
-
思路1:可以先利用链表中的元素创建数组,然后对数组实行reverse方法
-
题解1:
class Solution { public: vector<int> printListFromTailToHead(ListNode* head) { vector<int> A; while(head) { A.push_back(head->val); head=head->next; } reverse(A.begin(),A.end()); return A; } };
-
思路2:通过头插法建立新链表,然后再利用指针遍历输出
-
-
重建二叉树
-
描述:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回
-
思路:递归,前序序列的第一个元素在中序序列中找出,划分成左子树和右子树,然后分别对左右子树再使用重建二叉树的函数
-
题解:
import java.util.*; public class Solution { public TreeNode reConstructBinaryTree(int [] pre,int [] in) { if(pre.length==0||in.length==0) { return null; } TreeNode root=new TreeNode(pre[0]); for(int i=0;i<in.length;i++) { if(in[i]==pre[0]) { root.left=reConstructBinaryTree(Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(in,0,i)); root.right=reConstructBinaryTree(Arrays.copyOfRange(pre,i+1,pre.length),Arrays.copyOfRange(in,i+1,in.length)); } } return root; } }
来比较一下c++和java版本的区别,区别就在于递归的时候java直接生成了数组,c++用vector循环遍历生成新数组。
class Solution { public: TreeNode* reConstructBinaryTree(vector pre,vector vin) { int vinlen=vin.size(); if(vinlen==0) return NULL; vector pre_left, pre_right, vin_left, vin_right; //创建根节点,根节点肯定是前序遍历的第一个数 TreeNode* head = new TreeNode(pre[0]); //找到中序遍历根节点所在位置,存放于变量gen中 int gen=0; for(int i=0;i<vinlen;i++){ if(vin[i]==pre[0]){ gen=i; break; } } //对于中序遍历,根节点左边的节点位于二叉树的左边,根节点右边的节点位于二叉树的右边 // 左子树 for(int i = 0; i < gen; i++){ vin_left.push_back(vin[i]); pre_left.push_back(pre[i+1]);//先序第一个为根节点 } // 右子树 for(int i = gen + 1; i < vinlen; i++){ vin_right.push_back(vin[i]); pre_right.push_back(pre[i]); } //递归,执行上述步骤,区分子树的左、右子子树,直到叶节点 head->left = reConstructBinaryTree(pre_left, vin_left); head->right = reConstructBinaryTree(pre_right, vin_right); return head; } };
-
-
用两个栈实现队列
-
描述:用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型
-
思路:栈是先进后出,队列是先进先出,我们利用两个栈,将第一个栈的顺序从栈底到栈顶是按照压入的顺序,其pop操作原样即可
-
题解:
import java.util.Stack; public class Solution { Stack<Integer> stack1 = new Stack<Integer>(); Stack<Integer> stack2 = new Stack<Integer>(); public void push(int node) { while(!stack1.empty()) { stack2.push(stack1.pop()); } stack1.push(node); while(!stack2.empty()) { stack1.push(stack2.pop()); } } public int pop() { return stack1.pop(); } }
-
-
旋转数组的最小数字
-
描述:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。NOTE:给出的所有元素都大于0,若数组大小为0,请返回0
-
思路:既然是非递减数组的旋转,我们要利用这个特性。如果不利用这个特性的话,可以用一个变量存储最小值,遍历整个数组进行比较;或者对此数组进行排序,排序后取第一个元素,当然,这两种都没有利用到旋转数组的特性。我们可以从前往后比较,遇见比前一个元素小的,即是最小元素
-
题解:
import java.util.ArrayList; public class Solution { public int minNumberInRotateArray(int [] array) { if(array.length==0) { return 0; } int min=0; for(int i=1;i<array.length;i++) { if(array[i]<array[i-1]) { min=array[i]; } } return min; } }
-
-
费波拉契数列
-
描述:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0)
-
思路:此题考查递归于循环,将问题分解成规模较小的问题
-
题解:
public class Solution { public int Fibonacci(int n) { if(n>39) { return -1; } else if(n==0) { return 0; } else if(n==1) { return 1; } else { return Fibonacci(n-1)+Fibonacci(n-2); } } }
但是这样的算法效率并不高,我们可以用一个数组来存储计算过的值,这实际上是一维的动态规划。
-
-
后序遍历的非递归处理方法
- 描述:对于一二叉树,我们要用非递归的方式去解决其后序遍历问题
- 思路:我们用两个栈解决问题,首先把二叉树的根节点压入其中栈1中,然后弹出压入到栈2,如果无左右节点则不进行任何操作,存在左右节点的话,则需要将左右节点依次压入栈1,再弹出压入到栈2,直到栈1空,弹出栈2中所有元素即可获得遍历序列
-
青蛙跳台阶问题
-
描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)
-
思路:这题我们采用动态规划的算法,即跳n级台阶可以分为最后一步是1级加上前面跳n-1级台阶,和最后一步是两级加上前面跳n-2级台阶,这题类似于钢条切割问题,即res(n)=res(n-1)+res(n-2)
-
题解:
public class Solution { public int JumpFloor(int target) { if(target==1) { return 1; } else if(target==2) { return 2; } else { return JumpFloor(target-1)+JumpFloor(target-2); } } }
-
-
leetcode分发糖果问题-贪心算法
-
描述:老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。你需要按照以下要求,帮助老师给这些孩子分发糖果:
-
每个孩子至少分配到 1 个糖果。
-
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?
-
-
错误思路:首先从左往右遍历,第一个孩子给一个糖果,,遇见比自己分数高的,加一个糖果,如若不然重置为一,遍历完后从右向左遍历,类似操作
-
错误原因:
- 错误点:不能在从右向左遍历的过程中将糖果数重置成1
- 解决方法:只在从左向右的过程中重置为1
-
题解:
class Solution { public int candy(int[] ratings) { int[] res=new int[ratings.length]; res[0]=1; for(int i=1;i<ratings.length;i++) { if(ratings[i]>ratings[i-1]) { res[i]=res[i-1]+1; } else { res[i]=1; } } for(int k=ratings.length-2;k>=0;k--) { if(ratings[k]>ratings[k+1]) { if(res[k]>res[k+1]+1) { res[k]=res[k]; } else { res[k]=res[k+1]+1; } } } int resl=0; for(int j=0;j<res.length;j++) { resl+=res[j]; } return resl; } }
-
-
变态青蛙跳台阶
-
描述:一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法
-
思路:类似于斐波拉契数列,只是我们把情况变成了n种
-
题解:
public class Solution { public int JumpFloorII(int target) { int i=1; int res=0; while(i!=target) { res+=JumpFloorII(target-i); } res=res+1; return res; } }
但是这种算法太过繁琐,无法在规定时间内跑完,所以我们换一种思路
-
思路2:直接用一个数组存取之前的结果,然后遍历相加即可
-
题解2:
public class Solution { public int JumpFloorII(int target) { if(target<2) { return target; } else { int[] res=new int[target+1]; for(int j=0;j<target+1;j++) { res[j]=1; } res[0]=0; for(int i=0;i<target;i++) { for(int k=0;k<i;k++) { res[i]+=res[k]; } } for(int m=0;m<target;m++) { res[target]+=res[m]; } return res[target]; } } }
-
-
矩形覆盖-简单动态规划问题(已解决·,不记)
-
二进制中1的个数
-
描述:输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示
-
特殊思路:一个整数减一后,其最右边的1会变成0,而这个1右面的0会全部变成1,我们把这个数和原来的整数做与运算,则包括其最右边的1的右边全部变成0,这个整数有多少个1,就可以做多少次这样的运算
-
题解:
public class Solution { public int NumberOf1(int n) { int count=0; while(n!=0) { n=n&(n-1); count++; } return count; } }
-
一般思路:用一个标志变量flag,其初始值为1,不停的左移1位并与n做与运算,若不为0,则计数器加一,直至flag为0
-
题解:
public class Solution { public int NumberOf1(int n) { int num=0; int flag=1; while(flag!=0) { if((n&flag)!=0) num++; flag<<=1; } return num; } }
-
-
数值的整数次方
-
描述:给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方,保证base和exponent不同时为0
-
思路1:直接连续累乘,然后区分整数是负数还是正数即可
-
题解1:
public class Solution { public double Power(double base, int exponent) { int exponent2; if(exponent<0) { exponent2=-exponent; } else { exponent2=exponent; } if(exponent2==0) { return 1.0; } double res=1.0; while(exponent2!=0) { res*=base; exponent2--; } if(exponent>0) { return res; } else { return 1.0/res; } } }
-
思路2:快速幂乘法-之后我们论断的数据全是base的指数,我们可以将n用2i的累加式来表示,n的二进制表示中对应位数上的数字即为2i的系数,n每右移一位,base通过自乘运算达到平方,此算法的复杂度为O(logn)
-
题解2:
public class Solution { public double Power(double base, int exponent) { int exponent2; if(exponent<0) { exponent2=-exponent; } else { exponent2=exponent; } if(exponent2==0) { return 1.0; } double res=1.0; while(n!=0) { if(n&1) { res*=base; } base*=base; n>>=1; } if(exponent>0) { return res; } else { return 1.0/res; } } }
-
-
调整数组顺序使得奇数位于偶数前面、
-
描述:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变
-
思路1:比较暴力,用两个数组,分别用来存奇数和偶数,对原数组进行遍历,分别放置后重新对原数组赋值
-
题解1:
public class Solution { public void reOrderArray(int [] array) { int[] array1=new int[10]; int[] array2=new int[10]; int num1=0; int num2=0; for(int i=0;i<array.length;i++) { if((array[i]%2)!=0) { array1[num1]=array[i]; num1++; } else { array2[num2]=array[i]; num2++; } } for(int j=0;j<array.length;j++) { if(j>=num1) { array[j]=array2[j-num1]; } else { array[j]=array1[j]; } } } }
反省:这个算法一直报错,让我纠结了很长时间,因为按理来说这个算法是最简单直接的,但是出现了数组访问越界的错误,我怎么也找不到在哪越界的,后来发现是在计算新数组大小num1和num2的时候多加了1,导致超出数组边界,下次一定要引以为戒。
-
-
输出单链表的倒数第k个元素
-
描述:输入一个链表,输出该链表中倒数第k个节点
-
思路:用两个指针,一个是fast指针,一个是slow指针,fast指针先移动k步,然后slow指针和fast指针同时移动,至fast指针为null时返回slow指针所指的节点
-
题解:
/* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }*/ public class Solution { public ListNode FindKthToTail(ListNode head,int k) { ListNode fast=head; ListNode slow=head; while(k!=0) { if(fast==null) { return null; } fast=fast.next; k--; } while(fast!=null) { fast=fast.next; slow=slow.next; } return slow; } }
这个测试的时候报了nullpointer的错误,他的测试用例里面用了一个空的链表,所以如果不在移动k步的时候进行空节点检测,就会导致这种错误。
-
正确但是麻烦的思路:我们首先遍历一遍原始单链表,根据头插法创建原始单链表的reverse,然后找到其第k个元素
-
错误题解:
/* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }*/ public class Solution { public ListNode FindKthToTail(ListNode head,int k) { ListNode newhead=new ListNode(0); ListNode overall=head; while(overall!=null) { ListNode newoverall=overall; if(newhead.next==null) { newhead.next=newoverall; } else { newoverall.next=newhead.next; newhead.next=newoverall; } overall=overall.next; } ListNode newoverall2=newhead; while(k!=0) { if(newoverall2==null) { return null; } newoverall2=newoverall2.next; k--; } return newoverall2; } }
-
关于这题的思考:之前一直不明白为什么单链表需要一个不存任何值的头节点,此题中给出的链表不存在头节点,在用头插法建立新链表的时候十分不方便,更新一下,这题的链表是有头节点的,再次更新一下,无头节点。但是这种做法是存在缺陷的,一旦要求输出有k的元素的单链表的第k个节点,会导致输出null
-
思路3:思路2会创建一个新的链表,造成空间复杂度的上升,我们实际上可以在原有链表的基础上进行翻转,反转的思路其实就是将指向下一个节点的指针域指向前一个节点。
-
题解3:
/* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } }*/ public class Solution { public ListNode FindKthToTail(ListNode head,int k) { ListNode pre=head.next; ListNode cur=head.next; ListNode next=cur.next; cur.next=null; cur=next; while(cur!=null) { cur.next=pre; pre=cur; cur=next; } head.next=pre; ListNode res=head; while(k!=0) { if(res==null) { return null; } else { res=res.next; k--; } } return res; } }
注:关于单链表的反转和头插法操作有待加强
-
-
反转链表
-
描述:输入一个链表,反转链表后,输出新的链表的表头
-
思路:17题思路三已经很好的阐释了
-
题解:
/* 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 pre=null; ListNode cur=head; ListNode next=null; while(cur!=null) { next=cur.next; cur.next=pre; pre=cur; cur=next; } return pre; } }
-
-
合并两个单调递增的链表
-
描述:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则
-
思路:循环遍历至其中一个为空指针,将剩下的接在新链表的后面
-
题解:
/* 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 newlist=new ListNode(0); ListNode newList=newlist; while(list1!=null&&list2!=null) { if(list1.val<=list2.val) { newList.next=list1; list1=list1.next; } else { newList.next=list2; list2=list2.next; } newList=newList.next; } if(list1==null) { while(list2!=null) { newList.next=list2; list2=list2.next; newList=newList.next; } } else { newList.next=list1; list1=list1.next; } return newlist.next; } }
-
-
树的子结构
-
描述:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
-
思路:
- 首先判断B树是否为空,为空的话返回false
- 我们用先序遍历的方式去扫描两棵树,如果不用栈,我们就用递归的方式去解决
-
题解:
-
-
两数相加
-
描述:给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
-
思路:模拟小学数学的加法进位,因为是逆序存储,所以不用反转
-
题解:
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode addTwoNumbers(ListNode l1, ListNode l2) { ListNode pre=new ListNode(0); ListNode cur=pre; int carry=0; while(l1!=null||l2!=null) { int x=l1==null?0:l1.val; int y=l2==null?0:l2.val; int sum=x+y+carry; ListNode node=new ListNode(sum%10); cur.next=node; cur=node; carry=sum/10; if(l1!=null) { l1=l1.next; } if(l2!=null) { l2=l2.next; } } if(carry==1) { ListNode node=new ListNode(carry); cur.next=node; cur=node; } return pre.next; } }
-
-
删除链表的倒数第N个节点
-
描述:给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
-
思路:这题与之前的找到第倒数N个节点不同就是要删除元素
-
题解:
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode removeNthFromEnd(ListNode head, int n) { ListNode fast=head; ListNode slow=head; ListNode pre=head; while(n!=0) { if(fast==null) { return null; } fast=fast.next; n--; } while(fast!=null) { fast=fast.next; pre=slow; slow=slow.next; } if(pre==slow&&pre==head) { return head.next; } else { pre.next=slow.next; } return head; } }
-
-
合并k个有序链表
-
描述:输入链表数组,合并k个有序链表
-
思路:用类似合并两个数组的办法,多指针并起
-
自己的题解:
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode mergeKLists(ListNode[] lists) { int flag=1; ListNode res=new ListNode(0); ListNode temp=res; while(flag!=0) { int[] value=new int[lists.length]; for(int j=0;j<lists.length;j++) { value[j]=lists[j]==null?-1:lists[j].val; } int min=value[0]; int num=0; for(int i=0;i<lists.length;i++) { if(value[i]<min) { num=i; min=value[i]; } } ListNode node=new ListNode(min); temp.next=node; temp=ndoe; lists[num]=lists[num].next; for(int k=0;k<lists.length;k++) { if(lists[k]==null) { flag=0; } } } return res.next; } }
这算法解决的大部分的问题,在遇见超大数据输入的时候超出了时间限制
-
思路2:两个两个的合并,共合并n-1次
-
题解2:
/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode mergeKLists(ListNode[] lists) { if(lists.length==0) { return null; } if(lists.length==1) { return lists[0]; } else { ListNode newnode=mergetTwoLists(lists[0],lists[1]); for(int k=2;k<lists.length;k++) { newnode=mergetTwoLists(newnode,lists[k]); } return newnode; } } public ListNode mergetTwoLists(ListNode l1,ListNode l2){ ListNode res=new ListNode(0); ListNode temp=res; while(l1!=null||l2!=null) { int x=l1==null?1000:l1.val; int y=l2==null?1000:l2.val; if(x<=y) { ListNode node=new ListNode(x); temp.next=node; temp=node; if(l1!=null) { l1=l1.next; } } else { ListNode node=new ListNode(y); temp.next=node; temp=node; if(l2!=null) { l2=l2.next; } } } return res.next; } }
此算法通过了所有的案例,但是用时和空间复杂度仍然比较高
-
-
树的子树(leetcode)
-
描述:输入两棵二叉树A,B,判断B是不是A的子树
-
思路:一棵树是另外一颗树的子树,只有三种情况。分别是与这棵树,与这棵树的左子树,与这棵树的右子树相等,我们再调用一个相等函数即可判断。
-
题解:
/** public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } */ public class Solution { public boolean HasSubtree(TreeNode root1,TreeNode root2) { } public boolean IsSubtree(TreeNode root1,TreeNode root2){ if(root2==null&&root1==null) { return true; } if((root2==null&&root1!=null)||(root1==null&&root2!=null)) { return false; } if(root1.val!=root2.val) { return false; } else { return IsSubtree(root1.left,root2)&&IsSubtree(root1.right,root2); } } }
-
-
树的子结构
-
描述:输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)
-
思路:子结构是比子树更宽松的一种关系,但是子结构可能是他的1子树,也可能是他子树的子树,所以我们只需要一个函数来判断是否是子树即可,然后递归调用,注意,子结构不一定要到叶节点,所以调用函数IsSubtree的时候如果后面的参数是null,返回的是true
-
题解:
/** public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } */ public class Solution { public boolean HasSubtree(TreeNode root1,TreeNode root2) { if(root1==null||root2==null) { return false; } else{ return IsSubtree(root1,root2)||HasSubtree(root1.left,root2)||HasSubtree(root1.right,root2); } } public boolean IsSubtree(TreeNode root1,TreeNode root2){ if(root2==null) { return true; } if(root1==null) { return false; } else { if(root1.val!=root2.val) { return false; } return IsSubtree(root1.left,root2.left)&&IsSubtree(root1.right,root2.right); } } }
-
-
二叉树的镜像(已解决,简单,不记)
-
顺时针打印矩阵
-
描述:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
-
思路:用四个整型变量控制上下左右的边界,每遍历一行或一列,将对应的边界删除,同时检测边界的合理性,如右边大于等于左边,若出现不合理情况,则跳出循环
-
题解:
import java.util.ArrayList; public class Solution { public ArrayList<Integer> printMatrix(int [][] matrix) { ArrayList<Integer> res=new ArrayList<>(); if(matrix == null || matrix.length == 0 || matrix[0].length == 0){ return res; } int up=0; int down=matrix.length-1; int left=0; int right=matrix[0].length-1; while(true) { for(int i=left;i<=right;i++) { res.add(matrix[up][i]); } up++; if(up>down) { break; } for(int j=up;j<=down;j++) { res.add(matrix[j][right]); } right--; if(right<left) { break; } for(int m=right;m>=left;m--) { res.add(matrix[down][m]); } down--; if(down<up) { break; } for(int n=down;n>=up;n--) { res.add(matrix[n][left]); } left++; if(left>right) { break; } } return res; } }
-
-
包含min函数的栈
-
描述:定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。
-
思路:要想得到栈中所含的最小元素,且要求时间复杂度为O(1),使用两个栈,一个存放全部的元素,另一个存放每次压入新元素时的最小值,min函数对存放最小值的栈进行top操作即可。
-
题解:
import java.util.Stack; public class Solution { private static int mininum=10000; private static Stack<Integer> stacktotal = new Stack<Integer>(); private static Stack<Integer> stackmin = new Stack<Integer>(); public void push(int node) { if(node<mininum) { mininum=node; } stacktotal.push(node); stackmin.push(mininum); } public void pop() { if(stacktotal.size()>0) { stacktotal.pop(); stackmin.pop(); } } public int top() { return stacktotal.peek(); } public int min() { return stackmin.peek(); } }
-
-
栈的压入,弹出序列
-
描述:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
-
思路:将pushA数组压入栈,每次压入后检测与popA数组内元素是否相同,相同则弹出,popA的遍历序号加一
-
题解:
import java.util.ArrayList; import java.util.Stack; public class Solution { public boolean IsPopOrder(int [] pushA,int [] popA) { if (pushA.length == 0 || popA.length == 0 || popA.length != pushA.length) return false; Stack<Integer> stack = new Stack<>(); int j = 0; for (int i = 0; i < pushA.length; i++) { stack.push(pushA[i]); while (!stack.isEmpty() && stack.peek() == popA[j]){ stack.pop(); j++; } } return stack.isEmpty(); } }
-
-
从上到下打印二叉树(简化版的层次遍历)
-
描述::从上往下打印出二叉树的每个节点,同层节点从左至右打印
-
思路:二叉树的层次遍历算法中提到过,但是比这更复杂,需要判断哪一层,此题不用,直接用队列的数据结构,循环至队列空即可
-
题解:
import java.util.ArrayList; import java.util.LinkedList; import java.util.Queue; /** public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } */ public class Solution { public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) { Queue<TreeNode> restemp=new LinkedList<TreeNode>(); ArrayList<Integer> res=new ArrayList<>(); if(root==null) { return res; } restemp.offer(root); while(!restemp.isEmpty()) { TreeNode temp=restemp.poll(); res.add(temp.val); if(temp.left!=null) { restemp.offer(temp.left); } if(temp.right!=null) { restemp.offer(temp.right); } } return res; } }
-
-
二叉搜索树的后序遍历序列
-
描述:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
-
思路:对每一颗子树而言,其根节点都是后序遍历序列中的最后一个,循环的找到子树的根节点,判断其右子树的节点都比他大,左子树的节点都比他小,即可完成判断,当start>=end时,返回true
-
题解:
public class Solution { public boolean VerifySquenceOfBST(int [] sequence) { if(sequence==null) { return false; } return isBST(sequence,0,sequence.length-1); } public boolean isBST(int [] seq,int start,int end){ if(start>=end) { return true; } int root=seq[end]; int i=start; while(seq[i]<root) { i++; } int split=i-1; for(int k=i;k<end;k++) { if(seq[k]<root) { return false; } } return isBST(seq,start,split)&&isBST(seq,split+1,end-1); } }
问题:终结条件为什么是大于等于而不是等于呢?什么情况下会存在start<end的时候呢?
就在子树可能是null的时候,划分边界时将start减一,此时如果不判断这种情况,将会造成栈的溢出,这种情况return true即可。
注:[]是允许的,和null不一样在java中
-
-
二叉树的验证
-
描述:给定一个二叉树,判断其是否是一个有效的二叉搜索树。
假设一个二叉搜索树具有如下特征:
- 节点的左子树只包含小于当前节点的数。
- 节点的右子树只包含大于当前节点的数。
- 所有左子树和右子树自身必须也是二叉搜索树。
-
思路1:先返回中序序列,对中序序列进行检查即可
-
题解1:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public boolean isValidBST(TreeNode root) { List<Integer> res=inorderTraversal(root); for(int i=0;i<res.size()-1;i++) { if(res.get(i)>=res.get(i+1)) { return false; } } return true; } public List<Integer> inorderTraversal(TreeNode root) { List<Integer> res=new ArrayList<>(); helper(root,res); return res; } public void helper(TreeNode root,List<Integer> res){ if(root!=null) { helper(root.left,res); res.add(root.val); helper(root.right,res); } } }
-
思路2:递归(难点:判断的时候不能只判断左右子节点,还需要判断整棵左右子树),我们用一个布尔函数判断,他的传入值有树的根节点,上下界,对节点的右子树判断时,上界为无穷,下界为节点的值,左子树反之(卡边界值,左子树时上界是根节点的值,右子树时下界是根节点的值)
-
题解2:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public boolean helper(TreeNode root,Integer lower,Integer higher){ if(root==null) return true; if(lower!=null&&root.val<=lower) return false; if(higher!=null&&root.val>=higher) return false; if(!helper(root.left,lower,root.val)) return false; if(!helper(root.right,root.val,higher)) return false; return true; } public boolean isValidBST(TreeNode root) { return helper(root,null,null); } }
-
-
二叉搜索树的数组序列(困难)
-
描述:从左向右遍历一个数组,通过不断将其中的元素插入树中可以逐步地生成一棵二叉搜索树。给定一个由不同节点组成的二叉树,输出所有可能生成此树的数组。
-
错误思路:根节点是肯定比其左右子树的节点先插入,同一层的节点先后插入的结果是相同的,层次遍历的结果进行重新组合
-
反省:实际上不是每一层进行排列组合,唯一确定的关系只有根节点与其子节点
-
思路:当你插入一个节点的时候,你下一步可插入的节点放在一个List中,你需要对List进行for循环,每一步选择后,再递归的将当前节点的左右子节点加入List中,至List为空时完成了一次插入所有的节点,此时创建一个数组,加入res中,将上一步删除的节点重新加入队列中他原来的位置,就这样倒数着进行排列组合
-
题解:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { List<List<Integer>> res=new LinkedList<>();//结果数组 public List<List<Integer>> BSTSequences(TreeNode root) { if(root==null) { res.add(new LinkedList<>()); return res; } LinkedList<Integer> path=new LinkedList<>();//存放到目前为止的路径 path.add(root.val); helper(root,new LinkedList<>(),path); return res; } public void helper(TreeNode root,LinkedList<TreeNode> queue,LinkedList<Integer> path){ if(root==null) return; if(root.left!=null) queue.add(root.left);//root节点加进去后,其子节点和其兄弟节点都可能是下一个加入的对象 if(root.right!=null) queue.add(root.right); if(queue.isEmpty()) { res.add(new LinkedList<>(path)); return; } int len=queue.size(); for(int i=0;i<len;i++) { TreeNode cur=queue.get(i); queue.remove(i); path.add(cur.val); helper(cur,new LinkedList<>(queue),path);//注意此处传队列时传的是队列的复制,所以当返回到上一级的时候,queue并没有多出新的元素,每个节点的子节点还是由自己挑选 queue.add(i,cur); path.removeLast(); } } }
-
-
完全二叉树中的节点个数
-
描述:给出一个完全二叉树,求出该树的节点个数
-
思路:若用普通的遍历,可以求出结果,但是没有利用到完全二叉树的性质,故简便算法就此省略,我们递归的判断根节点的左右子树的深度,若相同,说明其左子树已经插满,其节点个数方便计算,再递归计算右子树即可,若不同,则右子树已满,递归计算左子树即可
-
题解:
/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * TreeNode right; * TreeNode(int x) { val = x; } * } */ class Solution { public int countNodes(TreeNode root) { if(root==null) { return 0; } else { int left=countLevels(root.left); int right=countLevels(root.right); if(left!=right) { return countNodes(root.left)+(1<<right); } else { return countNodes(root.right)+(1<<left); } } } public int countLevels(TreeNode root){ int i=0; if(root==null) { return i; } else return max(countLevels(root.left),countLevels(root.right))+1; } public int max(int a,int b){ if(a>=b) { return a; } return b; } }
-
-
字符串转换整数(正则处理)
4.10之前的刷题记录
最新推荐文章于 2023-09-14 20:48:59 发布