剑指offer总结
题目描述
解法
该题目属于查找的类型。查找优化条件在于数组中的数据是按顺序排列的,那么一种方法是二分法,另一种就是通过指针去指向然后移动指针去查找。
二分法的方式,因为是二维数组,因此在举一个例子分析就可以知道,二分法查找是很费时的,因为我们完全可以判断该二维数组的每一行的最后一个元素是否大于这个数,而不是先从中间判断。
所以它的解法也就很明白:
先从第一行的最后一个元素开始比较,如果大于,那么就可以往左去比较,如果小于,那么就往下去比较。
题目描述
解法
很简单的实现方式是,遇到每一个空格的时候,就替换。但这样的方法每一次替换完成就需要把后面的字符向后移动2位。这样太慢了。
可以从后往前进行替换,先计算出空格的个数,固定替换后的字符串的长度,然后设置两个指针,第一个指针指向原来字符串的最后的元素,第二个指针指向现在字符串的最后一个索引。然后两个同时向前移动,每当遇到空格的时候,就让第一个索引向前移一位,而第二个索引向前移三位。这样就完成了字符串的替换,而且减少了字符串的移动次数。
题目描述
解法
利用链表的头插法,新建一个链表,然后按顺序把链表的元素依次插入到新链表的头部,这样就完成了从尾到头的功能。
题目描述
解法
知道了前序遍历和中序遍历数组,然后将二叉树重建出来。需要用到二叉树的特性。
首先分析前序遍历,先根节点,然后左节点,然后右节点。因此根节点是该数组的第一个元素(这是关键)
中序遍历,先左节点,然后根节点,然后是右节点。因此根节点是在该数组的中间,而且根节点的左边所有节点就是根的左子树组成,右边是根的右子树组成。
{1,2,4,7,3,5,6,8}、{4,7,2,1,5,3,8,6}
要重建二叉树,就需要利用一种算法每次都能固定一个根节点,然后递归实现。
第一次,取下前序数组的第一个元素,它就是二叉树的根节点。1 此时我们可以知道前序数组被分为了{2,4,7}、{3,5,6,8},而中序数组被分为{4,7,2}、{5,3,8,6}(这个划分就是在中序数组中找到那个前序数组的第一个元素)
第二次,我们把数组分为了部分,我们对每一个部分进行处理,
先处理前部:{2,4,7}、{4,7,2},取出根节点2,此时又会继续第一次的分裂。。。。这个2就是1的左子树。
再处理后部:{3,5,6,8}、{5,3,8,6},取出根节点3,此时也会继续分裂。。。。这个3就是1的右子树。
这样很明显这是个递归的过程。需要提供的方法是在分裂数组的方法。
题目描述
解法
栈是先进后出,队列是先进先出。
push操作就是压栈的操作,关键是pop操作,如何将栈底的元素取出来。可以很轻松想到的是,push的时候都压入第一个栈,但在pop的时候,将第一个栈的元素都取出来放在第二个栈中。那么第二个栈就将第一个栈的元素反序存储了。
那么问题就简单了,我们只需要判断第二个栈是否为空,如果为空的话就把第一个栈的元素压入第二个栈中。如果不为空,就不压栈,而是直接把第二个栈中的元素出栈。
题目描述
解法
看似和上面的很类似,但其实实现的思路不同。
问题在于,入队和出队的顺序一样,我们在出队的时候不能直接出队,因此需要使得一个队列中只有一个元素,然后将其出队,在入队的时候,我们要保证我们入的是有很多元素的队列。
那么就很清楚了,队列A。队列B
第一次入栈:入A中,B为空。如果出栈,则直接将A中的出。如果继续入栈,那么都入A队列。
当出栈时,需要将A队列中的元素都出队,只剩最后的一个元素。然后把出队的元素入队到B中,出栈操作就是将A中剩的一个元素出队。
然后继续出栈的话,判断哪个队列不为空,不为空的继续出栈操作。
如果入栈的话,也要判断哪个队列不为空,就入队哪个队列。
public class Main { private static Queue<Integer> q1=new LinkedList<Integer>(); private static Queue<Integer> q2=new LinkedList<Integer>(); public static void main(String[] args) { push(1); System.out.println(pop()); push(2); System.out.println(pop()); push(3); System.out.println(pop()); } public static void push(int e){ //这里只需要判断哪个不是空的然后将该元素入不是空的队 if(q1.isEmpty() && q2.isEmpty()){ q1.add(e); }else if(q2.isEmpty()){ q1.add(e); }else if(q1.isEmpty()){ q2.add(e); }else { System.out.println("出错了"); } } public static int pop(){ //需要判断哪个队列有元素就将哪个队列移除队,然后只剩一个 if(q1.isEmpty() && q2.isEmpty()){ System.out.println("不能出队"); return -1; }else if(q2.isEmpty()){ while(q1.size()>1){ q2.add(q1.remove()); } return q1.remove(); }else if(q1.isEmpty()){ while(q2.size()>1){ q1.add(q2.remove()); } return q2.remove(); }else{ System.out.println("不能出队"); return -1; } } }
题目描述
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
解法
本质上还是查找,找最小元素。如果是无序的只能按照遍历,但这是一个有一定规则的,那么按照这个规则,我们只需要去寻找当前元素比上一个元素小的第一个找到的元素就是最小了。
但如果这个元素在最后面,那需要很长时间,因此,可以使用二分查找。
每次查找数组中间的元素,该元素如与第一个元素比较,如果大于第一个元素,那么从它后面找;如果小于这个元素,那么从它前面找。直到开始与结束指针相差1个。
题目描述
n<=39
解法
1.递归,这个很简单,设置出口,当n=0,输出0,n=1,输出1;那么后面的直接输出f(n)=f(n-1)+f(n-2);
2.动态规划,0 1 1 2 3 5 8 13 21 34 。。。。
那么每次用两个变量来存储
a=0 b=1; n=0
a=1 b=0; n=1
a=1 b=1; n=2
a=2 b=1 n=3
a=3 b=2 n=4
....
这样就可以知道,
题目描述
解法
无论是正数还是负数,可以用一个位1与该整数进行相与,如果结果是1,那么该位就是1.
因此,先从整数的第一位开始与1运算,如果结果是1,就计数。
然后将1向左移一位,继续与,知道1移位之后变成0.
题目描述
解法
该题考查的是对条件的判断。列出各种不同的情况即可。
题目描述
解法
该题属于数组的移动问题。如何按要求移动,而且不会把顺序的相对位置打乱。
经过分析,可以用插入法:2 7 6 3 5 9 8 0 11
每次都找一个奇数插入数组的前面:
第一次:7 2 6 3 5 9 8 0 11
第二次:7 3 2 6 5 9 8 0 11
...每次都插入
public class Odd { public static void main(String[] args) { int[] a=new int[]{2,7,6,3,5,9,8,0,11}; for(int i=1;i<a.length;i++){ //待插入的位置 int index=i-1; //插入的值 int value=a[i]; while(index>=0 && a[index]%2==0 &&value%2==1){ a[index+1]=a[index]; index--; } a[index+1]=value; } System.out.println(Arrays.toString(a)); } }
题目描述
解法
设置两个指针,一个指针指向第k个节点,另一个指针指向第一个节点,然后同时向后移动,当第一个指针指向null的时候,另一个指针就指向了倒数第k个。
题目描述
解法
反转链表使用头插法即可实现。
题目描述
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。解法
链表合成也是用的插入法。这种方法分析之后其实很简单,设置两个指针,p和q。
p指向链表A的第一个节点,q指向链表B的第一个节点。
比较q和p指向的节点value值的大小,若p小于q,那么先将p的指针后移,然后将q连接到p之前的那个节点后面。
依次类推,最后,两个指针都指向null之后,链表就合成了。
题目描述
输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)解法
该题就是一个逻辑的问题,
第一步,先从A中找到B的根节点,如果存在则继续,若不存在,直接判断失败。
第二步,A.left==B.left && A.right==B.right,若A==null说明不存在,若B==null说明存在。
package com.liuxinghang.threadpool; public class Tree { public boolean HasSubtree(TreeNode root1,TreeNode root2) { if(root1==null ||root2==null){ return false; } return isSub(root1,root2) || HasSubtree(root1.left,root2) ||HasSubtree(root1.right,root2) ; } //判断是不是子结构 public boolean isSub(TreeNode root1,TreeNode root2){ if(root2==null){ return true; } if(root1==null){ return false; } if(root1.val!=root2.val){ return false; } return isSub(root1.left,root2.left) && isSub(root1.right,root2.right); } } class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } }
题目描述
解法
也要用到递归,有关树的大部分都要用到递归
这个问题分析了之后还是比较简单的,递归交换左右子树即可。
public void Mirror(TreeNode root){ if(root==null){ return; } TreeNode temp=root.left; root.left=root.right; root.right=temp; Mirror(root.left); Mirror(root.right); }
题目描述
解法
该题需要考虑的是边界条件。
根据分析,这就是一个转圈的算法,要分析的是圈的层数,和怎么转。
根据计算一个n*n的矩阵,其层数是(n+1)/2。然后需要判断每一层的起始位置和结束位置。 第一层起点是0,终点是length-1;第二层起点是1,终点是length-2.......
因此,就 可以写出代码了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Clock { public static void main(String[] args) { int[][] matrix=new int[][]{{1,2,3,4},{5,6,7,8},{9,10,11,12},{13,14,15,16}}; ArrayList<Integer> integers = printMatrix(matrix); System.out.println(integers); } public static ArrayList<Integer> printMatrix(int[][] matrix){ ArrayList<Integer> result=new ArrayList<>(); int n=matrix.length; int level=(n+1)/2; for(int i=0;i<level;i++){ int begin=i; int end=n-i; int x=i; int y=i; while(y<end){ result.add(matrix[x][y]); y++; } y--; x++; while(x<end){ result.add(matrix[x][y]); x++; } x--; y--; while(y>=begin){ result.add(matrix[x][y]); y--; } y++; x--; while(x>begin){ result.add(matrix[x][y]); x--; } x++; } return result; } }
题目描述
解法
只利用一个辅助栈,里面存放栈中的最小值,
分析一下就能知道其中的逻辑。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public class Main { private static Queue<Integer> q1=new LinkedList<Integer>(); private static Queue<Integer> q2=new LinkedList<Integer>(); public static void main(String[] args) { push(1); System.out.println(pop()); push(2); System.out.println(pop()); push(3); System.out.println(pop()); } public static void push(int e){ //这里只需要判断哪个不是空的然后将该元素入不是空的队 if(q1.isEmpty() && q2.isEmpty()){ q1.add(e); }else if(q2.isEmpty()){ q1.add(e); }else if(q1.isEmpty()){ q2.add(e); }else { System.out.println("出错了"); } } public static int pop(){ //需要判断哪个队列有元素就将哪个队列移除队,然后只剩一个 if(q1.isEmpty() && q2.isEmpty()){ System.out.println("不能出队"); return -1; }else if(q2.isEmpty()){ while(q1.size()>1){ q2.add(q1.remove()); } return q1.remove(); }else if(q1.isEmpty()){ while(q2.size()>1){ q1.add(q2.remove()); } return q2.remove(); }else{ System.out.println("不能出队"); return -1; } } }
题目描述
解法
模拟入栈出栈的顺序,看是否能完成出栈的操作,若完不成说明有问题。
public static boolean IsPopOrder(int[] pushA, int[] popA) { Stack<Integer> s=new Stack(); int length = pushA.length; int pop = 0; for (int i = 0; i < length; i++) { s.push(pushA[i]); if (pushA[i] == popA[pop]) { s.pop(); pop++; } if(i==length-1){ while(!s.isEmpty() &&s.peek() == popA[pop]){ s.pop(); pop++; } } } if(pop==length){ return true; }else { return false; } }
题目描述
解法
其实就是一个层序遍历。有点像广度优先搜索算法。
可以使用一个队列,按照左右节点将节点添加到队列里面,每次按顺序取。这样就可以层级打印
public void print1(TreeNode root){ if(root==null){ return; } Queue<TreeNode> queue=new LinkedList(); queue.add(root); while(queue.size()!=0){ //取出来,打印,然后得到左右子节点 TreeNode node = queue.remove(); System.out.println(node.val); if(node.left!=null) { queue.add(node.left); } if(node.right!=null) { queue.add(node.right); } } }
题目描述
解法
二叉搜索树的特点,左节点比根节点小,右节点比根节点大。
一个判断方法是,取数组最后一个,然后如果数组能够分成两部分,前一部分小于后一部分,那么就是,若不能,那么就不是。也要利用递归。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static boolean VerifySquenceOfBST(int [] sequence){ if(sequence==null || sequence.length==0 ||sequence.length==1){ return true; } int length=sequence.length; int root=sequence[length-1]; int index=-1; for(int i=0;i<length;i++){ if(sequence[i]<root){ index++; } } boolean flag1=false; for(int i=0;i<=index;i++){ if(sequence[i]>root){ flag1=true; } } boolean flag2=false; for(int i=index+1;i<length-1;i++){ if(sequence[i]<root){ flag2=true; } } if(flag1 ||flag2){ return false; } // if(!flag1 && !flag2){ // return true; // } return VerifySquenceOfBST(Arrays.copyOfRange(sequence,0,index+1)) && VerifySquenceOfBST(Arrays.copyOfRange(sequence,index+1,length-1));
题目描述
解法
利用递归的思路,按照前序遍历的路径,每进入一个节点,就在list里面加入该节点,并且对target-value,这样当target=0的时候,而且是叶子节点,就将该list加入到大的list里面。然后退回到上一个节点,这时要删掉liast里面的最后一个值,然后继续。一次类推。
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) { if(root==null){ return alllist; } list.add(root.val); target=target-root.val; if(target==0 && root.left==null && root.right==null){ alllist.add(new ArrayList<>(list)); } FindPath(root.left,target); FindPath(root.right,target); list.remove(list.size()-1); return alllist; }
题目描述
解法
复杂链表的复制,不能像普通那样的思路。要注意特殊的指针。
思路是这样的:
步骤1:复制,先把每一个节点都复制一份,特殊指针先不赋值,但每一个节点跟在自己复制的节点的后面。
步骤2:连接,复制的节点的复杂指针要指向源节点指向的复杂指针的下一个指针。
步骤3:分离,每隔一个节点进行连接。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static RandomListNode Clone(RandomListNode pHead) { RandomListNode head=pHead; //复制 while(head!=null){ RandomListNode node=new RandomListNode(head.label); RandomListNode now=head; head=head.next; node.next=now.next; now.next=node; } //连接 RandomListNode headyuan=pHead; RandomListNode headycopy=pHead.next; while (headyuan!=null && headycopy!=null){ headycopy.random=headyuan.random.next; if(headycopy.next==null){ break; } headyuan=headyuan.next.next; headycopy=headycopy.next.next; } head=pHead; RandomListNode subh=pHead.next; RandomListNode subhead=subh; while(head!=null){ head.next=head.next.next; if(subhead.next!=null){ subhead.next=subhead.next.next; } head=head.next; subhead=subhead.next; } return subh; }
题目描述
解法
因为二叉搜索书用中序遍历就可以得到一个排序的值。使用非递归的中序遍历算法。
用到了一个辅助栈。
第一步:先去找最左边的节点,在找的过程当中,将路过的左节点都压入栈中。
第二步:找到最左边,将栈中的最左边的节点出栈,然后设置一个pre的节点,将最左边的节点赋值给pre的左节点上。维护这个pre。然后去维护当前节点的left节点为pre。这样就构成了一个双向的链表。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public TreeNode Convert(TreeNode pRootOfTree) { //先实现一个非递归的中序遍历 //条件1:先从最左端出发, //条件2:如果当前节点有右子树,需要先遍历到右子树。(因为左子树肯定遍历过了) //条件3:如果没有右子树,那么需要回退,回退就是栈的结构,可以用栈来实现。 //定义一个栈来存放节点 Stack<TreeNode> s=new Stack<>(); //定义一个节点来表示当前的节点 TreeNode index=pRootOfTree; //定义一个节点来表示最左边的节点 TreeNode root=null; //定义一个节点来保存前一个节点 TreeNode pre=null; //设置一个标记,来判断是不是第一次寻找最左边节点 boolean flag=true; //循环条件是index不为空,并且栈里面有节点 while(index!=null || !s.isEmpty()) { while (index != null) { s.push(index); index = index.left; } index = s.pop(); if (flag == true) { pre = index; //确定了最左边的节点 root = pre; flag=false; } else { //把前一个节点的右指针指向当前节点,同时把当前节点的左指针指向前一个节点。 pre.right = index; index.left = pre; pre = index; } index=index.right; } return root; }
题目描述
解法
1.递归
先从最后面开始交换。
a b c d
递归的时候。先交换cd,然后再换回来,重复执行。
![](https://img2020.cnblogs.com/i-beta/1944174/202003/1944174-20200320184255952-922653497.png)
public static void PermutationHelper(char[] cs, int i, List<String> list) { //递归出口 if(i==cs.length-1){ list.add(String.valueOf(cs)); } for(int j=i;j<cs.length;j++){ //先把i和j交换 swap(cs,i,j); //然后执行递归。 PermutationHelper(cs,i+1,list); //然后把i和j换回来 swap(cs,i,j); }
题目描述
解法
解法很简单,主要是如何提高效率。
1。使用排序的方式,然后若该值满足条件,那么一定是排序后中间的数。
2.使用随时记录的方式,维护一个result值和times。维护到最后的result是可能的值,然后判断是否出现一半以上的次数
3.利用一个hashmap,然后把每个数字的个数都维护起来,然后判断各种值的出现个数。
题目描述
解法
1.排序
2.利用快速排序的思路,如果排好的数的索引=4,说明已经排好了。
3.最大堆的思路
这个问题可以好好研究。
题目描述
解法
使用动态规划的思路,
遍历数组,每次都判断前一个的和+现在数组的值,与现在数组的值做比较,取最大的。
这里使用result来存最终的值,
public static int FindGreatestSumOfSubArray(int[] array) { int res = array[0]; //记录当前所有子数组的和的最大值 int max=array[0]; //包含array[i]的连续数组最大值 for (int i = 1; i < array.length; i++) { max=Math.max(max+array[i], array[i]); res=Math.max(max, res); } return max; }
题目描述
解法
1.遍历每一个数字,然后得到该数字中1的个数。计数。
题目描述
解法
先将数组中的数字转换为字符串,然后将字符串按照从小到大排列,排列规则是,将其中每两个相加,然后比较是加在前面大,还是加在后面大。
上面例子来说,332 >323 那么选择32在3之前,32321 >32132,那么321排在32之前,因此最后的排序是321 32 3
题目描述
解法
第一个丑数是1
1*2 1*3 1*5 选择最小的 2 同时2的个数加1
2*2 1*3 1*5 选择最小的 3 同时3的个数加1
2*2 2*3 1*5 选择最小的 4 同时2的个数加1
3*2 2*3 1*5 选择最小的 5 同时5的个数加1
3*2 2*3 2*5 选择最小的 6 同时2的个数加1 同时3的个数加1
4*2 3*3 2*5 选择最小的 8 同时2的个数加1
。。。。
很容易写出代码
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public int GetUglyNumber_Solution(int index) { if(index==0) return 0; int [] res=new int[index]; res[0]=1; int t2=0,t3=0,t5=0; for(int i=1;i<index;i++){ res[i]=min(2*res[t2],3*res[t3],5*res[t5]); if(res[i]==2*res[t2]) t2++; if(res[i]==3*res[t3]) t3++; if(res[i]==5*res[t5]) t5++; } return res[index-1]; } public int min(int a,int b,int c){ if(a<=b && a<=c){ return a; } else if(b<=a && b<=c) return b; else return c; }
题目描述
解法
利用哈希的思路,将出现的字符都存入一个key中,value++。查看value==1的是哪个字符。
题目描述
解法
暴力解法很简单,直接遍历数组,前面减去后面,若为正数则加1,循环。
题目描述
解法
1.使用hashmap,把第一个链表的值存入map中,然后遍历第二个链表,如果链表中已经存在,那么可以直接返回。
2.将两个链表连接起来,那么总会找到相同的链表
题目描述
解法
使用二分查找
题目描述
解法
利用递归,每遍历一层+1
如果左边的层数大,那么就选择左边的,右边的层数大就选择右边的。
每次递归一层就在某一个子树上+1,最后选择最大的、
题目描述
解法
平衡二叉树左右子树的层数相差为不大于1
利用递归,去查看每一段左右子树是否都相差不大于1
题目描述
解法
1.暴力求解
2.利用异或。先对数组中的每一个进行异或,得到异或后的结果,然后根据异或或的1,将不同的数分成两个部分,然后再依次异或
题目描述
解法
利用滑窗法,设置两个指针,先固定左指针,然后右指针右移,如果累加和大于sum,就将左指针向左移,保证累加和的值是在两个指针之间的值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) { ArrayList<ArrayList<Integer>> listall=new ArrayList<>(); int left=1; int right=2; int sumValue=left+right; while(sum>right){ if(sumValue<sum){ //右指针右移 right++; sumValue+=right; }else if(sumValue>sum){ //左指针右移,并从sumValue中减去值 sumValue-=left; left++; }else { //sum相等了 ArrayList<Integer> list=new ArrayList<>(); for(int i=left;i<=right;i++){ list.add(i); } listall.add(list); right++; sumValue+=right; } } return listall; }
题目描述
解法
1.使用暴力解法,遍历
2.双指针法,一个指向开头,一个指向结尾,然后往里移动,知道两个指针相遇。
题目描述
解法
1.简单逻辑,转换为数组,然后寻找第k个索引,保存前k个,然后把后面的左移,最后把保存的加到后面。
2.使用字符串的三次翻转,abcXYZ,n=3
令A=abc,B=XYZ,AB翻转为cbaZYX,然后再翻转一次,XYZabc.
题目描述
解法
1.使用split函数,按照空格分隔。然后翻转数组。
2.先把整个字符串进行翻转,然后将其中的每个单词进行翻转。对单词的翻转可以借用滑窗的思路,记录单词的左右边界,然后翻转。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static String ReverseSentence(String str) { //先将整个字符串翻转 char[] chars = str.toCharArray(); Reverse(chars,0,chars.length-1); System.out.println(String.valueOf(chars)); //然后去利用滑窗,记录每一个单词的起始位置 int begin=0; int end=0; for(int i=0;i<chars.length;i++){ if(chars[i]==' '){ Reverse(chars,begin,end-1); end++; begin=end; }else if(end==chars.length-1){ Reverse(chars,begin,end); break; } else { end++; } } return String.valueOf(chars); } public static void Reverse(char[] chars,int begin,int end){ while(begin<end){ char temp=chars[begin]; chars[begin]=chars[end]; chars[end]=temp; begin++; end--; } }
题目描述
解法
分三个步骤
步骤1:排序
步骤2:得到0的个数以及确定有无重复的非0数
步骤3:得到间隔相差的值的累加
然后比较0的个数和累加。
题目描述
解法
1.利用一个链表来存储,没报到数的同学从链表中删除,然后继续循环,知道链表中剩下最后一个数。
题目描述
解法
1.用递归,递推公式是F(n)=F(n-1)+n
递归出口是n==1
2.利用动态规划
public static int Sum_Solution(int n) { if(n==1){ return 1; } return Sum_Solution(n-1)+n; } public static int Sum_Solution1(int n) { int a=0,b=1; for(int i=0;i<n;i++){ a=a+b; b++; } return a; }
题目描述
解法
步骤1:俩数异或。得sum 无进位
题目描述
解法
主要是条件判断
题目描述
解法
1.暴力法
2.利用特性,利用交换法,将每一个数都找到自己的位置,然后看其他位置上是不是也有这个数
比如,2 3 1 0 2 5 3 ,先看2,它应该在数组的2索引,将它与2索引的值交换,得:1 3 2 0 2 5 3
然后看1,将它与1索引位置的值交换:3 1 2 0 2 5 3
。。。。
就可以让每个数字放在自己的位置上,然后,如果我又去查看其他位置,发现我现在的值的位置上已经位置正确了,那么就出现了重复值。
public static boolean duplicate(int numbers[],int length,int [] duplication) { int index=0; while (index<length){ int now=numbers[index]; if(now!=index){ if(numbers[now]==now){ duplication[0]=now; return true; } int temp=numbers[index]; numbers[index]=numbers[now]; numbers[now]=temp; }else { index++; } } return false; }
题目描述
解法
利用数学的特性
public static int[] multiply(int[] A) { int[] C=new int[A.length]; int[] D=new int[A.length]; int[] B=new int[A.length]; C[0] = 1; for(int i=1;i<A.length;i++) { C[i] = C[i-1] * A[i-1]; } D[A.length-1]=1; for(int i=A.length-2;i>=0;i--){ D[i]=D[i+1]*A[i+1]; } for(int i=0;i<A.length;i++){ B[i]=C[i]*D[i]; } return B; }
题目描述
解法
分不同的情况讨论,
patten字符串的下一个字符不为*时,对于str的第一个字符的判断就是,相同,则指针后移,不相同则返回false。
patten字符串的下一个字符为*时,对于star的第一个字符的判断分两种:
第一种:当前字符str与petten匹配
1.str的第一个字符不动,将patten字符串往后跳过两个位置。(代表*表示的是匹配0次)
2.str的第一个字符右移一位,将patten字符串往后跳过两个位置。(代表*表示的是匹配1次)
2.patten的指针不动,将str的指针往后移动1位,知道与patten的第一个字符不匹配为止。表示匹配多次
第二种:当前字符不匹配,(当成匹配0次)
1.str的第一个字符不动,将patten字符串往后跳过两个位置。(代表*表示的是匹配0次)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static boolean match(char[] str, char[] pattern) { if(str==null ||pattern==null){ return false; } return matchHelper(str,0,pattern,0); } public static boolean matchHelper(char[] str,int istr, char[] pattern,int ipatten) { //递归出口 if(istr==str.length && ipatten==pattern.length)//指针都走完了 { return true; } if(istr<str.length && ipatten==pattern.length){//匹配的走完了,但str没走完,说明不对称 return false; } //判断*的情况 if(ipatten+1<pattern.length && pattern[ipatten+1]=='*'){ //匹配没有走完,并且下一个是* //判断当前字符的情况,如果当前指针一样,才会进行复杂判断 if(istr<str.length && str[istr]==pattern[ipatten]){ //进行三种操作 return matchHelper(str,istr,pattern,ipatten+2)|| //表示*匹配的个数为空 matchHelper(str,istr+1,pattern,ipatten+2) || //表示*匹配的个数为1个 matchHelper(str,istr+1,pattern,ipatten);//表示匹配多个 }else { return matchHelper(str,istr,pattern,ipatten+2); } } if((istr<str.length&&str[istr]==pattern[ipatten]) || (istr<str.length&&pattern[ipatten]=='.') ){ return matchHelper(str,istr+1,pattern,ipatten+1); }else { return false; } }
题目描述
解法
分析题意,可以知道一个数值的表示方法为【+|-】【A】【.】【B】【E|e】【+|-】【C】
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
public static boolean isNumeric(char[] str) { //判断A是不是整数 boolean isnum=num(str); //有小数点 if(index<str.length &&str[index]=='.'){ index++; isnum= unnum(str) && isnum ; //看小数点后面的B是不是整数 } //有e if(index<str.length &&(str[index]=='e'||str[index]=='E')){ index++; isnum=isnum && num(str); } return isnum &&(index==str.length); } public static boolean num(char[] str){ if(index<str.length && str[index]=='+' ||str[index]=='-'){ index++; } return unnum(str); } public static boolean unnum(char[] str){ int start=index; while(index<str.length && str[index]>='0' &&str[index]<='9'){ index++; } return index>start; }
题目描述
解法
1.遍历
2.hashtable
题目描述
解法
分三个步骤
步骤1:确定该链表中是否有环,可以设置一个快指针,一个慢指针,如果慢指针能追上快指针,说明该链表有环。
步骤2:确定环中节点的个数,使用一个指针去循环,转一圈之后的计数。
步骤3:设置两个指针,一个指针先走环个数步,另一个指针从开头追,两个指针相遇的地方就是入口。
题目描述
解法
因为是排序的链表,因此,只需要判断,当前节点是否与下一个节点相等,如果相等,则将当前节点和下一个节点都删掉。
维护一个pre节点表示当前节点的上一个节点。
题目描述
解法
仔细观察,可以把中序下一结点归为几种类型
有右子树,下一结点是右子树中的最左结点,例如 B,下一结点是 H
无右子树,且结点是该结点父结点的左子树,则下一结点是该结点的父结点,例如 H,下一结点是 E
无右子树,且结点是该结点父结点的右子树,则我们一直沿着父结点追朔,直到找到某个结点是其父结点的左子树,如果存在这样的结点,那么这个结点的父结点就是我们要找的下一结点。例如 I,下一结点是 A;例如 G,并没有符合情况的结点,所以 G 没有下一结点
题目描述
解法
解法1,求出其镜像,然后判断是不是相同,若相等,返回true
解法2,利用递归,判断左子树和右子树是否镜像
题目描述
解法
使用两个栈,一个存放奇数层的节点,一个存放偶数层的节点
题目描述
二叉树的反序列化是指:根据某种遍历顺序得到的序列化字符串结果str,重构二叉树。
解法
遍历存储
题目描述
解法
中序遍历的非递归形式
题目描述
解法
最大堆
思路如下:
维护一个大顶堆,一个小顶堆。
大顶堆维护的是中位数前面的小值,在堆顶存放的是最大值,而小顶堆存放的是大值,堆顶存放的是最小值。
我们只需要关心,两个堆里的最大值和最小值。利用优先队列来实现堆。 优先队列默认是一个最小堆。
题目描述
解法
暴力解法:遍历每一个滑窗求出最大值
题目描述
![](https://img2020.cnblogs.com/i-beta/1944174/202003/1944174-20200321175450759-530351749.png)
解法
停一停