从数据结构分java
一。链表:node
思路:递归调用,调一次,加一次到list中正则表达式
两个指针,第一个先走k步,第二个不动,而后第一个和第二个一块儿走,知道第一个到尾节点。数组
思路,主要是调整指针。数据结构
思路:两个链表先比较头,头比较小的作新链表的头,新链表的next递归调用本函数。app
思路:三步走。第一步:链接到后面。第二步:构建随机指针。第三部:拆除链接函数
最后记得把pClonedNode赋值给一个pClonedHead保存头结点。用于最后的返回。测试
见第四。ui
思路:分别遍历两个链表,获得他们的长度差,而后长链表走这个差值的步数。而后长链表和短链表一块儿走,直到它们碰到,返回。spa
思路:用两个指针,一个快指针一下走两步,一个慢指针,一下走一步。而后走着直到它们相遇。而后把慢指针从头走,快指针一步一步走,直到它们相遇就是环的入口节点。
思路:特别要注意须要保存节点到一个ListNode中,以防后面修改移动后找不到指针。须要Pre节点一个。指向前一个节点,pNode指向当前节点。因为当前节点的前一个是没有的,因此建立一个头节点val为-1.而后把它指向node。
二。数组
思路:从最右边最上边开始比较,target比之大,row++;比之小,col--,不然返回true
思路:本题考查二分查找。两个下标,一头一尾,循环条件是while(array[index1]>=array[index2])循环结束条件是index2-index1==1.循环的过程当中要不停的建立数组的重点,不停的去二分查找,若是中点值大于等于前半部分(隐含的条件是大于第二部分),那么中点的值就位于第一部分,第一部分的下标就要变换为mid。同理,若是中点值小于第一部分而且小于等于第二部分,那中点值必定位于第二部分,对于特殊状况,如// 3 3 3 0 3, 3 0 0 0 3两种状况没法判断哪一个属于旋转部分。就须要经过普通的办法来查找。
思路1:借助一个辅助数组,遍历第一遍把奇数放进去,第二遍把偶数放进去,再传递给原来的数组。
思路2:待思考
思路:遍历数组的时候保存两个值,一个是数组中的一个数字,另外一个是次数。但咱们遍历到下一个数字的时候,若是下一个数字和咱们以前保存的数字相同,次数加一,不一样次数减一:若是次数为0,那么咱们须要保存下一个数字,并把次数设为1,有与咱们要找的数字出现的次数比其余的数字次数之和还要多,MAME要找的数字确定是最后一次吧次数设为1对应的数字。另外要验证该数字是否大于长度的一半。
思路:快排+二分查找
思路:两个变量,一个存当前的和,另外一个存最大值,而后遍历数组,若是curSum小于0,就等于当前数组的值,不然就curSum+=num【i】,若是curSum比max大的话就更新。
思路:主要是考察对比较器的认识,比较器里面定义两个字符串,用字符串自带的比较器比较便可。要比较的对象是o1+o2与o2+o1两个字符串。
思路:参考归并排序。
思路:改进的二分查找,注意等于的时候还要看状况移动指针,觉得不必定是恰好是边界。
思路:全部数组元素的异或结果就是两个出现一次的数字的异或结果,因此把这两个数字分别划分到两个数组里,每一个数组的异或结果就是这个数,划分规则是根据第一次出现1的位数那一位是0仍是1。例如数组{2,4,3,6,3,2,5,5}异或的结果是0010,咱们就根据倒数第二位是0仍是1来划分。注意位数是从后往前的。
思路:有点相似二分查找或者partition过程,不一样的是,两个指针一个是从头开始,另外一个从第二个元素开始。记录small+big的和为cursum,若是这个值等于要求的sum和的话,那么就找到一个,small++;若是小于sum那么big++,cursum也要加上big处的数,大于的话small也要++,可是cursum要把small去掉,这个时候可能就到了边界,注意此处的边界是mid=()sum+1)/2,从1和2开始循环
思路:准备两个指针,一个指向头,一个指向尾,而后若是头的值和尾的值的和与要找的数相等的话就找到了,而后再找伺机最小的,注意指针的移动。若是不等,那么 若是和大于num,那么后面的指针前移,小于前面的指针后移。
思路:由于数组中的数字是从0-n-1,排序后每一个数字和它对应的下标相等,所以咱们能够遍历数组若是和下标不对应,就交换直到对应。
思路:不妨定义C【i】=A[0]*A[1]*...A[I-1],D[I]=A[i+1]*...A[n-2]*A[N-1]。c能够自上而下的顺序计算出来,即C【i】=C[I-1]*A[I-1],D能够经过自下而上计算出来,即D【i】=D【i+1】*A【i+1】;
从代码上看,第一个正序,第二个从后往前,注意第二次计算顺带把第一个的带进去了,result数组的最后一个值和C数组的最后一个值是相等的,看图就明白了。
思路:以{2,3,4,2,6,2,5,1}分析,主要要用到双端队列,在java中就是linkedList,数组的第一个数字是2,存入队列。第二个数字是3,因为它比前一个数字2大,所以2不可能成为滑动窗口的最大值。先把2从队列里删除,再把3存入队列。何时结束呢,当一个数字的下标与当前处理的数字的下标之差大于或者等于滑动窗口的大小时,这个数字已经从窗口划出,能够从队列中删除了。
思路:分三步,1:把数组排序;2.统计数组中0的个数;3.统计排序以后的数组中相邻数字之间的空缺总数。注意出现对子直接返回false。
三。字符串
思路:从后往前,设一个新的strbuilder,新str的长度是原来的长度+空格长度*2;从后往前挨着替换。因为是在原有的sb上替换,因此只能从后往前。须要两个下标,分别从后往前,新的下标三部顶旧下标的一步。已远数组的大小进行循环。
思路:暴力递归。不停的去切换下标(下一个字符),而后递归调用。递归终止的条件是下标到了length-1.最后集合里没有的有的话再添加。(相似的题,求子序列,思路也是找下一个,能够选择加仍是不加这个字符串)
一个哈希表,没有的话置为1,有的话+1,最后找等于1的。
思路:主要是一个头尾互换函数不停的调用。先总体调用。而后分出的两块分别调用。
思路:与上题有点像,一样是字符串翻转的应用。
注意:思路是result=result*10+c-'0';可是要注意第一个字符有正负号的状况,下标要移动,若是第一个是+或者-可是长度只有1也要返回0;遍历的过程当中也要判断是否在0-9中间。
思路:主要分为两种状况:模式的第二个字符(1)是*(2)第二个不是*,而后再分状况递归调用,移动下标。
正则表达式了解一下
思路:回溯法,不停的往前走两层循环,里面用递归,回溯,分别取i-1,i+1,j-1,j+1,k取下一个字符,暴力遍历,便可,若是访问成功记得修改flag
四。二叉树,二叉搜索树
思路:先把中序遍历的下标,健为中序中的数字,值为下标放入hashmap中。主要是下标变换.先创建根节点,root.left=递归调用。root.rigth=递归调用。
方法中的参数有(pre,pi,pj,ni,nj)index=map.get(pre[pi]).left=(pre,pi+1,pi+index-ni,ni,index-1) right=(pre,pi+index-ni+1,pj,index+1,nj)
思路:先判断根是否相等,相等的话再比较左孩子和右孩子,左右的比较确定是递归。固然不是的话,咱们还要把左孩子和root2树作一样的对比。
递归,左右孩子互换,而后调用本身的方法直到终止条件。
思路:把节点放一个队列里(linnkedList)有左子树就把左子树放入,有右子树就把右子树放入队列,而后再依次弹出,入队和出队是同在一个循环里面的。出队的时候放入ArrayList中。,要是有左右孩子就要把左右孩子依次放入队列。总体看就是出队的时候伴随着入队。出队的元素就是之前入队造成的。
思路:整体是递归思路。首先要想清楚二叉搜索树的性质:全部的左子树的值小于根节点的值,全部的右子树的值大于根节点。后序遍历的特色是:根节点在最后。
因而从头开始遍历,直到找到第一个数比根节点大的值的下标,若是以后的值有比根节点小的那么直接返回false。if(i>start)递归左子树 isPostBST(sequence,start,start+i-1);if(j<end) isPostBST(sequence,start+i,end-1);
思路:整体递归思路。两个ArrayList一个放全部路径,一个放一条当前路径。须要用到递归,每当到叶子节点时候就要判断是否等于target,target递归的过程当中不停的变换,findPath(root,target)target不不停的减去root值。开始的时候要把root.val放进去。最后到叶子节点记得把road(当前路径的list)的最后一个元素删掉(若是等于已经添加进roads里面了,若是没有找到就把最后一个值舍去,而后返回上一层节点)
思路:要注意点一点是要把T初始的TreeNode赋值给一个变量Head。而后递归调用,左节点去找,有节点去找。而后找到左孩子的最右边节点和右孩子最左边节点。而后把根节点和左孩子最右节点相连,右孩子最左和根节点相连。最后返回Head。
思路:递归。哪一个孩子的深度大,就把这个深度加上本身自己的1,就是实际的深度。
思路:与上题思路基本相同。可是要加上一句,if左右孩子深度差超过1,那么return false。
思路:一个节点若是有右孩子,那么它的中序遍历的下一个节点就是它的右孩子的最左边的节点,若是没有右孩子,它的中序遍历的下一个节点就须要:经过next向上遍历直到它是它父节点的左孩子为止。
思路:准备两棵如出一辙的树,这个题来讲就是第一颗树的左孩子和第二课树的左孩子对称而且,第一棵树的右孩子和第二棵树的左孩子对称。而后递归,注意跳出循环的条件。
思路:准备两个栈(linkedList或者Stack),一个放正序TreeNode,另外一个放逆序TreeNode,正序弹出来的时候,按从左到右的顺序压入,而后逆序栈弹出的就是逆序,而后按从右往左的顺序压栈,两个栈相互交替完成。而后每次循环须要一个list。用来添加一行的数字。(注意循环条件,当任意一个不为空就能够循环)
思路:与上一题有点像,不一样的是此处只须要一个数据结构,一个队列便可。须要准备一个num遍历存储队列的大小,当num>0时候要不停的出队,固然出队的同时它的孩子也要分别入队。其余操做基本相同。另外,队列使用linkedList。(队列和栈均可以使用)
思路:序列化是TreeNode到String字符串的过程,反序列化反之。注意用先序遍历。
序列化:递归调用,String res=root.val+",";res+=serialize(root.left) ;res=serialize(root.right);递归终止条件是root==null 此时要把"#,"返回。
反序列化:一样递归,首先要经过“,”分隔为一个字符串数组,而后用一个下标index表示字符串的下标,经过下标的不停移动来递归调用。TreeNode node=new TreeNode(Integer.parseInt(s[index])),递归终止条件是 if(s[index]=="#") return null,而且index++;
思路:整体思路是中序遍历,左中右,而后递归,左边若是找到了那么return,右边找到了右边return。中间是index++;而后if(index==k)return pRoot;
五。栈,队列
思路:push直接进第一个栈,出栈的话,若是stack2不为空才出,为空的话就把stack1以此pop弹出并压入2.
思路:准备两个栈,第一个放其余元素,第二个放最小元素,push过程当中,若是minStack为空或者新元素比minStack的栈顶元素小,则把新元素push进去,而后把新元素push进第一个栈。pop过程当中,若是第一个栈弹出的元素和minStack的栈顶元素相同,则须要把minStack元素和第一个栈的元素一并弹出。
获取最小值的操做就是minStack.peek()。
思路:准备一个辅助栈。先压入第一个要压入的元素。若是下一个要弹出的元素不等于栈顶,则要压栈,直到找到下一个要弹出的元素,若是下一个要弹出的元素等于栈顶元素,直接pop弹出。若是到最后都没找到就返回false。要注意两个下标:一个是pop数组的,这个是整个for循环的下标,另外一个是push数组的下标,这个index是push数组有没有到头的一个标志。
六。二级制玩法
思路:一个数和这个数减去1取与的次数就是二进制1的个数。
七。矩阵
思路:start为起点,因为起点始终都是0,0或者1,1这种,至关于0,0的位置,注意循环条件是rows>start*2&&cols>start*2,向下打印的时候,须要加入判断:endY>start,向左的时候:endY>start&&endx>start 向上的时候:endy>start+1&&endx>start。另外须要注意的是,转一圈,也就是循环一次,start要加一。而且每次endx=cols-1-start,endy=rows-1-start;
思路:见字符串分类里面。
思路:见数组
思路:二、三、5分别有本身的一个下标,每个丑数都是另外一个丑数乘以2/三、或者5。要用本身下标处的值乘以本身的2/三、或者是5,而后找他们中间的最小值放入list中,若是这个最小值和刚才获得的数字相等的话,那么下标就++。
须要的辅助空间:三个num保存有可能进入到list中的丑数,三个index保存2/3/5的下标,一个Arraylist保存结果。