链表
求链表环的入口结点
先用快慢指针判断是否为环,然后快慢指针相交的点就是在环内,然后让慢指针不动,快指针到起点改为每次一步,慢指针也每次一步,继续遍历,(更麻烦的方法:然后再走一圈环回到相交点,就是这个环的长度。有了环的长度,则让两个指针相隔这个长度,然后前进,一旦后一个指针进入环,这两个指针就一定会相遇。)相遇的这个点就是入口结点
数组
一个排好序的数组,找出两数之和为m的所有组合
从数组两边进行推进,如果两数之和小就把前指针往后移,反之也如此,直到找到之后,就可以两边同时缩紧
1 | function find( array, m ) { |
自然数序列,找出任意连续之和等于n的所有子序列
从0开始累加,如果到一个点累加的值比n大,就把这个累加数组从最小开始循环移出元素,直至找到匹配
1 | lst = [0,2,4,5,3,1,8,6,4,7,9,3,2] |
判断数组内是否有重复元素
总体思想都是利用额外的数据结构的方式减少时间复杂度,比如辅助数组和哈希表
1 | //第一种 |
寻找从小到大的第1500个丑数(只包含2,3,5为因子的数是丑数,如4,6,8)
利用空间换时间,保存1500个丑数就好了,关键是如果有序地生成丑数。
因为丑数肯定是丑数*(2,3,5),我们可以根据已有丑数数组来算,把列表的丑数读乘2,比较是否大于数组里最大的丑数,然后选最小的那个大于数作为后一位丑数,如果都小于,则再试乘3,一次类推
求连续子数组的的最大和,数组里面有正数也有负数。如{1,-2,3,10,-4,7,2,-5}中和最大的子数组是{3,10,-4,7,2}
问题的原型好像就是找出一段和最大的数组。我们可以先从起点开始,保持一个最大数,一直累加,如果累加后一位的时候,发现是负数,则还是保持之前的最大数,如果累加得到的值比当前这个数还小,则累加起点变成这个点,然后最大数变成这个数的值。
树
求二叉查找树中的两个结点的最低公共祖先
从上往下进行遍历,如果结点的值比这两个结点都大或小,则进入他的左子树或右子树,然后如果碰到的结点大小刚好在两个结点的中间,则这个结点就是最低公共祖先。如果遇到刚好等于其中一个结点的值,那么这个结点就是最低公共祖先
求普通二叉树的两个结点的最低公共祖先
既然不是排序树,则判断是否有指向父节点的指针,如果有,可以转化为链表求第一个公共节点的问题;如果还是没有指向父节点的指针,用两个链表分别保存从根节点到输入的两个节点的路径,然后把问题转换成两个链表的最后公共节点
我们首先得到一条从根结点到树中某一结点的路径,这就要求在遍历的时候,有一个辅助内存来保存路径.比如我们用前序遍历的方法来得到从根结点到H的路径的过程是这样的:( 1 )遍历到A,把A 存放到路径中去,路径中只有一个结点A; ( 2 )遍历到B,把B 存到路径中去,此时路径为A->B; ( 3 )遍历到D,把D 存放到路径中去,此,时路径为A->B->D; ( 4 ) :遍历到F,把F 存放到路径中去,此时路径为A->B->D->F;( 5) F 已经没有子结点了,因此这条路径不可能到这结点H. 把F 从路径中删除,变成A->B->D; ( 6 )遍历G. 和结点F 一样,这条路径也不能到达H. 边历完G 之后,路径仍然是A->B->D; ( 7 )由于D 的所有子结点都遍历过了,不可能到这结点H,因此D 不在从A 到H 的路径中,把D 从路径中删除,变成A->B; ( 8 )遥历E,把E 加入到路径中,此时路径变成A->B->E, ( 9 )遍历H,已经到达目标给点, A->B->E 就是从根结点开始到达H 必须经过的路径。
从上到下打印二叉树
效果如下
1 | 8 |
利用栈的思想,压入从左到右,从上到下的结点,再保持两个变量,一个是下层要打印的节点数,一个是当前层要打印的结点数。比如顶点8,先压栈8,打印后,8出栈,此层打完了就打换行符,再压栈6和10。接着打印6,压栈5和7,6出栈,再打印10,压栈9和11,10出栈,最后打印5,7,9,11
循环
十进制转十六进制
先算出他有几个16次方,然后每次都转换成一个十六进制符号
1 | public static String toHex(int num, int base) { |
十六进制转十进制
从字符串的头开始算,然后每次都乘转换数,比如16,然后累加
1 | public static int stoi(String src, int base) { |
约瑟夫环问题:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列,求出列人的顺序编号
关键在于获取每次出列的人的index,其实index=startNum+countNum(开始的序号‘‘0~1’’+规定的长度-1),如果index大于列表总长了,就要用%取余,获取到index后,就remote它,接着开始的序号就变成刚刚remote的index了
1 | import java.util.ArrayList; |
参考路径
http://www.jianshu.com/p/8e794879dd29
递归
求找钱的所有方法数
1 | public static int findNum(int[] arr, int aim){ |
汉诺塔
汉诺塔有3个柱子,求把n个叠在左侧的塔全移到右侧和搬运的次数,要求一次只能移一个塔,并且小的不能在大的下面
通过递归的方式,寻找抽象的动作,每次都有一个起点,暂放点和重点,可以抽象地从搬多个可以递归到搬一个
1 | public static int hanoi(int n){ |
深度优先遍历
假设在一个9 × 9的格子里,每一步可以往正上、正下、正左、正右、左上、左下、右上、右下八个方向走,但是空白区域不能走,走过的点不能再走。求能吃最多果实的路线。
1 | public class Pos { |
1 | public class Sdbl { |
动态规划
你正在爬n层的楼梯,但每次你只能爬一步或者两步,你能有多少种不同的方法爬到楼顶部?
已知爬1层和2层的方法数,在前面的基础上,可以知道爬3层的数方法数,同理4层只能是通过2层的基础+2步或3层的基础+1步来实现的,而前面已经知道2层和3层的方法数了,相加就好了,这个是动态规划的思想
1 | function climb(floor){ |
矩阵求从[0,0]路径[3,3]点的最小值
1 | 2 5 4 8 |
先求上面一行和左边一行的路径最小值
2 7 11 19
3
12
12然后可以开始遍历剩余的点,左边和上边选一个最小值,再加上自己的值就是这个点的最小路径值,直到遍历到[3,3]点
最长递增子序列(不连续)
先从末尾分析,如果n位大于n-1位,则dp(n) = dp(n-1)+1,然后从0位开始积累dp数组
1 | public static int lis(int[] arr){ |
最长公共子序列(不连续)
从末尾分析,如果
X[i]==Y[j]
,则c[i][j] = c[i-1][j-1]+1
;如果X[i]!=Y[j]
,则c[i][j] = Math.max(c[i][j-1],c[i-1][j])
1 | public static int[][] lengthofLCS(char[] X, char[] Y){ |
最长公共子串(连续)
和上面的基本一样,但是如果
X[i]!=Y[j]
,则c[i][j]
为0
1 | public static int lengthofLCString(String X, String Y){ |
设dp是走到这一步最少要有的血量,先算出最后一行的dp,然后在从下遍历到上一行,因为每一个点的dp是依赖于正下和右边的dp的,所以遍历结束,就知道了
dp[0][0]
需要多少的初始血量了
1 | public static int minHP(int[][] m){ |
回溯法
尝试一下,如果失败则回退到之前的逻辑
八皇后
在8*8的棋盘上要求放8个棋子,而且他们互不在同一行同一列同一斜线
先放8个不同的行,然后遍历放列,每次遍历判断是否和之前的不在同一列同一斜线
1 | /** |