(1)二维数组中的查找
(2)重建二叉树
利用Arrays.copyOfRange(n,from,to)进行数组截取,实现起来更简便。
public TreeNode reConstructBinaryTree(int [] pre,int [] vin) {
int pren = pre.length;
int vinn = vin.length;
if(pren==0 || vinn==0)
return null;
int node = pre[0];
TreeNode root = new TreeNode(node);
for(int i = 0;i<vin.length;i++)
if(vin[i]==node){
root.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,i+1),Arrays.copyOfRange(vin,0,i));
root.right = reConstructBinaryTree(Arrays.copyOfRange(pre,i+1,pren),Arrays.copyOfRange(vin,i+1,vinn));
break;
}
return root;
}
(3)二叉树的下一个结点
(4)矩阵中的路径
更简便的代码:
1、将矩阵路径经过时改为‘.’,使用完再回复为原值。来代替flag矩阵记1
2、关于i,j是否超届的判断均放到最开始的if中用,超届则直接false;
3、易错点,到index最后一位匹配上就直接给true
public boolean hasPath (char[][] matrix, String word) {
for(int i=0;i<matrix.length;i++){
for(int j=0;j<matrix[0].length;j++){
if(deepPath(matrix,i,j,word,0))
return true;
}
}
return false;
}
public boolean deepPath(char[][] m,int i,int j,String word,int index){
//不需要进行m[i][j]=='.'置false的判断,因为只有和word中值匹配上才会通过,其已包含在m[i][j]!=word.charAt(index)判断中
if(i<0||i>=m.length||j<0||j>=m[0].length||m[i][j]!=word.charAt(index))
return false;
if(index==word.length()-1)
return true;
char t = m[i][j];
m[i][j] = '.';
boolean res = deepPath(m,i-1,j,word,index+1)||
deepPath(m,i+1,j,word,index+1)||
deepPath(m,i,j-1,word,index+1)||
deepPath(m,i,j+1,word,index+1);
m[i][j]=t;
return res;
}
(5)剪绳子
动态规划的思想是一定要从最小子问题开始求解,之后更大的问题可以应用其子问题的结果。
若该子问题出现多次,只需计算一次。
1、本题n=2和n=3与裁成了2和3的值不同。
因为n=2和n=3必须进行裁剪故对应1,2。但裁的段中有2和3时不需要继续裁最大为2,3.
其他的可以依次便利或得(不必排除裁成长度为1的段,因为肯定不如其他的值大)
public int cutRope (int n) {
// write code here
if(n==2)
return 1;
if(n==3)
return 2;
int[] max = new int[n+1];
max[1] = 1;
max[2] = 2;
max[3] = 3;
for(int i = 4;i<=n;i++){
for(int j=1;j<i;j++){
max[i] = Math.max(max[i],max[j]*max[i-j]);
}
}
return max[n];
}
(6) 数值的整数次方
注意指数为负数时
注意此题是调整后保持奇偶数内部的相对位置
(8)链表中环的入口结点
这里忽视的问题是,slow走一步,fast走两步是slow=pHead.next,fast=pHead.next.next;
错误1:slow=pHead,fast=pHead.next.next;这相当于slow一步没走
错误2:在一起走c步时,从pHead开始
(9)树的子结构
重点看一下递归部分。易忽略,如果一开始root1==null则肯定是false
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
if(root1==null||root2==null)
return false;
Stack<TreeNode> stack1 = new Stack();
stack1.push(root1);
while(!stack1.isEmpty()){
TreeNode node = stack1.pop();
if(isSame(node,root2)){
return true;
}
if(node.left!=null)
stack1.push(node.left);
if(node.right!=null)
stack1.push(node.right);
}
return false;
}
public boolean isSame(TreeNode node,TreeNode root2){
if(node==null&&root2!=null)
return false;
if(root2==null)
return true;
if(node.val!=root2.val)
return false;
return isSame(node.left,root2.left)&&isSame(node.right,root2.right);
}
(10)栈的压入、弹出序列
空间复杂度小的方式:与用栈辅助思想一致,但时利用数组已经入栈的部分当作栈。
top指向栈顶,pushIndex指向下一个要操作的数
public boolean IsPopOrder(int [] pushA,int [] popA) {
if(pushA.length==0)
return true;
int pushIndex = 0,top=-1,popIndex=0;
while(popIndex<popA.length){
if(top>=0&&popA[popIndex]==pushA[top]){
top--;
popIndex++;
}
else{
if(pushIndex>=pushA.length)
return false;
else{
pushA[++top]=pushA[pushIndex];
pushIndex++;
}
}
}
return true;
}
(11)二叉搜索树的后序遍历序列
对于使用stack实现的得递归方法,不理解
(12)二叉树中和为某一值的路径(二)
此题递归与利用队列时间控件复杂度一致,且非递归实现起来麻烦,故仅练习了递归。
(12)二叉搜索树与双向链表
1、采用中序遍历的思想,这样便利结束才是有序的
2、利用head记录第一个节点,用pre记录遍历序列的前一个节点。注意当需要对pre进行更新时,pre为null,此时遍历序列的第一个节点记为head;
3、不要陷入要记录后一个节点的误区,无法做到。变通即pre.right指向现在的,现在的left指向pre。
递归方法实现:
private TreeNode head = null;
private TreeNode pre = null;
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree==null)
return null;
convert(pRootOfTree);
return head;
}
public void convert(TreeNode root){
if(root==null)
return ;
convert(root.left);
if(pre == null){
head = root;
}
else{
pre.right = root;
root.left = pre;
}
pre = root;
convert(root.right);
}
非递归方法:也可以看作用栈实现中序遍历
1、当指针不为空,或栈不为空都要继续进行操作
2、利用while将当前指向的节点左子节点进栈,直到再无左子树
此指针为空,或进栈到再无左子树。进行出栈(此时对出栈节点操作与递归的一致)
3、将指针指向当前出栈节点的右孩子,
因为此时此节点的左孩子一定已经进行过操作,下一个要操作的是右孩子
即每次都遍历到此节点最左,操作完成,指向当前节点右孩子,循环往复。
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree==null)
return null;
TreeNode head = null;
TreeNode pre = null;
Stack<TreeNode> stack = new Stack();
while(pRootOfTree!=null||!stack.isEmpty()){
while(pRootOfTree!=null){
stack.push(pRootOfTree);
pRootOfTree = pRootOfTree.left;
}
TreeNode node = stack.pop();
if(pre==null)
head = node;
else{
pre.right = node;
node.left = pre;
}
pre = node;
pRootOfTree = node.right;
}
return head;
}
(13) 字符串的排列
一种更为简便的方法,利用字符串的子串获取与拼接。
递归传到现在的新串,剩余的字母,ArrayList<String>
这里注意,ArrayList在函数中传递是地址的拷贝,不是仅仅参数的拷贝
public ArrayList<String> Permutation(String str) {
ArrayList<String> result = new ArrayList();
if(str==null||str.length()==0)
return null;
newString(str,"",result);
return result;
}
public void newString(String str,String newstr,ArrayList<String> result){
if(str.length()==0){
if(!result.contains(newstr.toString()))
result.add(newstr.toString());
return ;
}
for(int i = 0;i<str.length();i++){
newString(str.substring(0,i)+str.substring(1+i,str.length()),newstr+str.charAt(i),result);
}
}
(14)最小的K个数
本题可利用三种方法实现:堆排序,有控制的快排,冒泡
对于堆排序思想:要用大顶推,因为堆顶的值是方便获取的,每次要看是不是前4小,就要看是不是比目前进堆的最大值还小,故要用大顶堆。
/*
这里采用优先队列的思想(大顶堆)
维护一个大顶堆,每次加入元素后如果多余k个就删除最大的
所有加入一遍后就剩下了k个最小的。
PriorityQueue的用法
大顶堆 new PriorityQueue<Integer>((o1,o2)->(o2-o1));
小顶堆 new PriorityQueue< >();
自定义 Queue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
*/
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k){
ArrayList<Integer> res = new ArrayList();
if(input.length==0||k==0)
return res;
Queue<Integer> queue = new PriorityQueue<Integer>((o1,o2)->(o2-o1));
for(int i=0;i<input.length;i++){
if(queue.isEmpty()||queue.size()<k)
queue.add(input[i]);
else{
if(input[i]<queue.peek()){
queue.poll();
queue.add(input[i]);
}
}
}
while(!queue.isEmpty()){
res.add(queue.poll());
}
return res;
}
(15) 数据流中的中位数
提议目的理解清楚
ArrayList的add可以在对应位置添加(即插入元素)
private ArrayList<Integer> res = new ArrayList();
public void Insert(Integer num) {
int index = 0;
if(res.isEmpty())
res.add(num);
else{
while(index<res.size()){
if(num<=res.get(index)){
//如果在这里添加,当要出入的是末尾时则无法插入,因为不会进入if
//res.add(i,num);
break;
}
index++;
}
res.add(index,num);
}
}
public Double GetMedian() {
if(res.size()%2==0){
int index1 = res.size()/2;
int index2 = index1-1;
return (res.get(index1)*1.0+res.get(index2))/2;
}
else{
return (double)res.get(res.size()/2) ;
}
}
计算每一位上1出现的次数做计算
分成两部分 ,假设此数位12345,计算百位上数字base=100
1、百位前为12则有12个100~199故有12*100个一。(n/(base*10))*base
2、百位后包括百位为345(n%(base*10)),即计算包含100~199之间多少个数。
故n%(base*10)<base 0个
base<= n%(base*10) <base*2 有n%(base*10)-base+1 eg:123有23+1个100~123
n%(base*10) >= base*2 即包含了100~199 有base个
public int NumberOf1Between1AndN_Solution(int n) {
int sum = 0;
int index = 1, flag = 10;
while(n/index!=0){
sum+=n/flag*index;
if(n%flag>=index&&n%flag<index*2)
sum+= n%flag - index +1;
else if(n%flag>=index*2)
sum+=index;
index*=10;
flag*=10;
}
return sum;
}
(17) 把数组排成最小的数
时间复杂度:O(nlog2n),可利用自带的快排
思想:进行数组的排序;这里排序的规则,即最小的无论谁放在其前面,都没有其放在前面小。
每一位都如此控制,则最终组合最小。
public String PrintMinNumber(int [] numbers) {
if(numbers.length==0)
return "";
ArrayList<String> res = new ArrayList();
String result = "";
for(int i = 0;i < numbers.length;i++){
res.add(Integer.toString(numbers[i]));
}
res.sort(new Comparator<String>(){
public int compare(String s1,String s2){
return (s1+s2).compareTo(s2+s1);
}
});
for(String str:res){
result+=str;
}
return result;
}
(18)把数字翻译成字符串
思想见代码:
public int solve (String nums) {
if(nums==null||nums.length()==0||nums.charAt(0)=='0')
return 0;
//先排除特殊情况即若中间出现了30、60,不为10,20则直接不可编译。因为0必须要和前一个数结合
for(int i = 1;i<nums.length();i++){
if(nums.charAt(i)=='0'&&nums.charAt(i-1)!='1'&&nums.charAt(i-1)!='2')
return 0;
}
//此处预留0,方便计算第二个字符位置有集中可能
int[] kinds = new int[nums.length()+1];
Arrays.fill(kinds,1);
for(int i =2;i<kinds.length;i++){
//因为kinds 下标1对应的是nums下标0,故i-2,i-1
//若与前一位组合后范围在11~19,21~26
if((nums.charAt(i-2)=='1'&&nums.charAt(i-1)!='0')||(nums.charAt(i-2)=='2'&&nums.charAt(i-1)!='0'&&nums.charAt(i-1)<'7'))
kinds[i] = kinds[i-1]+kinds[i-2];
else//无法组合或组合为10,20是等于前一位结果
kinds[i] = kinds[i-1];
}
return kinds[kinds.length-1];
}
(19) 礼物的最大价值
本次主要是时间复杂度。为了使时间复杂度更小,用sum去记录右上到此格的最大值。若此出已有值则不向下递归。
1、递归实现
public int maxValue (int[][] grid) {
int[][] sum = new int[grid.length][grid[0].length];
sumcount(grid,grid.length-1,grid[0].length-1,sum);
return sum[sum.length-1][sum[0].length-1];
}
public int sumcount(int[][] g ,int i, int j,int[][] sum){
if(i==0&&j==0){
sum[0][0] = g[0][0];
return sum[0][0];
}
if(sum[i][j]==0){
//以下i==0 和 j==0的判断为保证不越界访问。
if(i==0)
sum[i][j] = sumcount(g,i,j-1,sum)+g[i][j];
else if(j==0)
sum[i][j] = sumcount(g,i-1,j,sum)+g[i][j];
//不存在i==0或j==0则不会越界,可向下递归计算
else
sum[i][j] = g[i][j]+Math.max(sumcount(g,i-1,j,sum),sumcount(g,i,j-1,sum));
}
return sum[i][j];
}
2、非递归方法:因为每个位置要都是获取其上,左的做大值。使固定的。可从晓的开始依次累加计算。
public int maxValue (int[][] grid) {
if(grid==null||(grid.length==0&&grid[0].length==0))
return 0;
for(int i = 0;i<grid.length;i++){
for(int j = 0;j<grid[0].length;j++){
if(i==0&&j==0)
continue;
if(i==0)
grid[i][j] += grid[i][j-1];
else if(j==0)
grid[i][j] += grid[i-1][j];
else
grid[i][j] += Math.max(grid[i-1][j],grid[i][j-1]);
}
}
return grid[grid.length-1][grid[0].length-1];
}
(20) 最长不含重复字符的子字符串
思想:利用Hashmap 实现字典
如果map中已有此字符,且在f,l之间(因为此时l为当前位置故只用判断是否大于等于f)则重复。f 变为与当前重复的位置+1
右边界一直移动,长度变大就更新
public int lengthOfLongestSubstring (String s) {
// write code here
if(s==null)
return 0;
Map<Character,Integer> map = new HashMap();
int max = Integer.MIN_VALUE;
int f = 0,l = -1;
for(int i = 0;i<s.length();i++){
if(map.get(s.charAt(i))!=null&&map.get(s.charAt(i))>=f){
f = map.get(s.charAt(i))+1;
}
l++;
if(l-f+1>max)
max=l-f+1;
map.put(s.charAt(i),i);
}
return max;
}
(21) 丑数
1、非递归思路,用最小堆,每次弹出最小的数。(即为当前低i个抽数)再将min*2、min*3,min*5入队(前提此数在堆中不存在,用一个HashMap辅助判断)
中间加入的值可能出现long类型超过int
public int GetUglyNumber_Solution(int index) {
if(index==0)
return 0;
Queue<Long> queue = new PriorityQueue();
Map<Long,Integer> map = new HashMap();
long num=0;
int sum=0;
queue.add(1L);
map.put(1L,1);
while(sum<index){
num = queue.poll();
sum++;
if(!map.containsKey(num*2)){
queue.add(num*2);
map.put(num*2,1);
}
if(!map.containsKey(num*3)){
queue.add(num*3);
map.put(num*3,1);
}
if(!map.containsKey(num*5)){
queue.add(num*5);
map.put(num*5,1);
}
}
return (int)num;
}
2、递归方式
(22)数组中的逆序对
import java.util.*;
public class Solution {
public int InversePairs(int [] array) {
if(array.length<=0)
return 0;
return merge(array,0,array.length-1);
}
public int merge(int[] arr,int l, int r ){
if(l>=r)
return 0;
int m = (r+l)/2;
int sum = merge(arr,l,m)+merge(arr,m+1,r);
int[] t = new int[r-l+1];
int lindex = l,rindex=m+1;
for(int k = 0;k<t.length;k++){
//右侧归完,则一定不会再出现逆序对
if(rindex>r){
t[k]=arr[lindex++];
}
//还能入for循环说明,左右一定有一侧未归完.即rindex>m则lindex一定不大于l
//arr[lindex]<arr[rindex]可能此时rindex已大于r故要作为第二个判断
else if(lindex>m||arr[lindex]<arr[rindex])
t[k] = arr[rindex++];
else{
sum=(sum+(r-rindex+1))%1000000007;
t[k] = arr[lindex++];
}
}
int k = 0;
for(int i = l;i<=r;i++)
arr[i] = t[k++];
return sum;
}
}
(23)二叉搜索树的第k个节点
这里其实运用的是中序遍历的思想,故用栈实现中序遍历是关键
/*
当前
指向节点不为null(即还有内容待入栈)
栈不为空(即还有节点未出栈)
进入循环
每次循环入栈到当前节点的最左(指针变空则最左完成)
出栈顶为当前遍历元素
因出栈元素左边一定已经入栈(入的时候入到了最左),故再指向其右边
(若右边是空的,进入下一个循环就不会执行入栈,直接出下一个)
*/
public int KthNode (TreeNode proot, int k) {
if(proot==null||k<=0)
return -1;
Stack<TreeNode> stack = new Stack<TreeNode>();
TreeNode node = proot;
while(node!=null||!stack.isEmpty()){
while(node!=null){
stack.push(node);
node = node.left;
}
k--;
node = stack.pop();
if(k==0)
return node.val;
node = node.right;
}
return -1;
}
(24)数组中只出现一次的两个数字
- 时间复杂度:O(n)
- 空间复杂度:O(1)
- 的方法思路
异或运算满且相同的数字作异或会被抵消掉,相同为0 不同为1.a^x^y^z^c^x^y^z^c^b=a^b
(1)想分别得到a,b则需要把输入内容分成两组。已知a与b不同,一定至少有一位异或结果为1。故可利用此位是否为0来区分。(可从a^b的低位开始寻找,找到第一个为1的位)
(2)此为是否为0可以把输入内容划分为两组.a^x^y^x^y=a;b^z^c^z^c=b
则分别对这两组内容异或可得a和b。
public int[] FindNumsAppearOnce (int[] array) {
// write code here
int ab = 0;
for(int i = 0;i<array.length;i++){
ab^=array[i];
}
int k = 1;
while((ab & k) == 0)
k = k << 1;
int[] num = new int[2];
for(int i = 0;i<array.length;i++){
if((array[i] & k) == 0)
num[0]^=array[i];
else
num[1]^=array[i];
}
if(num[0]>num[1]){
int t = num[0];
num[0] =num[1];
num[1]=t;
}
return num;
}
(25)和为S的两个数字
时间复杂度更小的方法
方法一:
思路:sum - a 即为当前数对应的另一个数。若在此数组中则可输出此对。
要想快速定位在不在可用HashMap存储,这样计算包不包含比较快。
注意:可能出现[1445]8,[1456]8。故不可以见简单的先把数存入哈希,再从头遍历
不然 [1456]8中,4包含但是是自己,若通过下标判断是否为自己,则 [1445]8 中存入key4,前将后覆盖。
故应该边存边,比较。当前的与之前的比较。
若与其匹配的数之前存在则输入数对;若不存在则存入hash表。则可排除上边两种意外。
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> res = new ArrayList();
if(array.length<=1)
return res;
Map<Integer,Integer> map = new HashMap();
for(int i = 0;i<array.length;i++){
if(map.containsKey(sum-array[i])){
res.add(array[i]);
res.add(sum-array[i]);
return res;
}
else{
map.put(array[i],i);
}
}
return res;
}
方法二:充分利用数组的有序。可以一个指针指向最小值即开始,一个指针指向最大值即结束。
当两数和大于sum,说明需要指向更小的,故右指针移动。
当两数和小于sum,说明需要指向更大的,故左指针移动。
public ArrayList<Integer> FindNumbersWithSum(int [] array,int sum) {
ArrayList<Integer> res = new ArrayList();
if(array.length<=1)
return res;
int l = 0, r = array.length-1;
while(l<r){
if(array[l]+array[r]>sum)
r--;
else if(array[l]+array[r]<sum)
l++;
else{
res.add(array[l]);
res.add(array[r]);
return res;
}
}
return res;
}
(26)左旋转字符串
(27) 二叉树中和为某一值的路径(二)
注意:牵扯记录路径要使用递归方法比较容易实现。再次过程中path用LinkedList声明,因为链表方便删除元素。且其中有removeFirst与removeLast方便删除头尾。
每次对结点的左右孩子遍历完,再弹出路径中最后一个元素。
private static ArrayList<ArrayList<Integer>> res = new ArrayList();
public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int expectNumber) {
LinkedList<Integer> arr = new LinkedList<Integer>();
dfs(root,expectNumber,arr);
return res;
}
public void dfs(TreeNode root, int sum ,LinkedList<Integer> arr){
if(root==null)
return ;
sum-=root.val;
arr.add(root.val);
if(root.left==null&&root.right==null&&sum==0)
res.add(new ArrayList<Integer>(arr));
dfs(root.left,sum,arr);
dfs(root.right,sum,arr);
arr.removeLast();
}
我们知道第一个人(编号一定是m%n-1) 出列之后,剩下的n-1个人组成了一个新 的约瑟夫环(以编号为k=m%n的人开始):
k k+1 k+2 ... n-2, n-1, 0, 1, 2, ... k-2并且从k开始报0。
现在我们把他们的编号做一下转换:
k --> 0
k+1 --> 1
k+2 --> 2
...
...
k-2 --> n-2
k-1 --> n-1
变换后就完完全全成为了(n-1)个人报数的子问题,假如我们知道这个子问题的解: 例如x是最终的胜利者,那么根据上面这个表把这个x变回去不刚好就是n个人情 况的解吗?!!变回去的公式很简单,相信大家都可以推出来:x'=(x+k)%n。
public int LastRemaining_Solution(int n, int m) {
if(n<1||m<1)
return -1;
if(n==1)
return 0;
int x = LastRemaining_Solution(n-1,m);
return (x+m)%n;
}
(29) 求1+2+3+...+n
(30)把字符串转换成整数(atoi)
思路无问题,主要是实现细节。
public int StrToInt (String s) {
// write code here
s = s.trim();
if(s.length()==0)
return 0;
//直接用符号1、-1参与计算来表示正负
int flag = 1;
int index = 0;
if(s.charAt(index)=='-'){
index++;
flag = -1;
}
else if(Character.isDigit(s.charAt(index))) flag = 1;
else if(s.charAt(index)=='+'){
flag = 1;
index++;
}
else return 0;
int sum = 0;
while(index<s.length()&&Character.isDigit(s.charAt(index))){
int c = s.charAt(index)-'0';
//先判断计算后是否超界,超界则停止计算
//判断是否大于对大值
if(sum>Integer.MAX_VALUE/10||(sum==Integer.MAX_VALUE/10&&c>Integer.MAX_VALUE%10)){
return Integer.MAX_VALUE;
}
//判断是都小于最小值
if(sum<Integer.MIN_VALUE/10||(sum==Integer.MIN_VALUE/10&&-c<Integer.MIN_VALUE%10)){
return Integer.MIN_VALUE;
}
//标记正负参与计算
sum=sum*10+flag*(s.charAt(index)-'0');
index++;
}
if(flag == 0)
sum = -sum;
return (int)sum;
}
(31)矩形覆盖 (利用斐波那契数列)
空间复杂度 O(1) ,时间复杂度 O(n)。利用动态规划,但每次仅记录当前n的,n-1与n-2
(32) 和为S的连续正数序列
实现上的一些小细节问题:
public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) {
ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
if(sum==0)
return res;
int lindex=1,rindex=2;
while(rindex - lindex>=1){
int num = (rindex+lindex);
//此时注意计算有多少对时,(rindex-lindex+1)/2中的加一
num*=((rindex-lindex)/2==0)?1:(rindex-lindex+1)/2;
if((rindex-lindex)%2==0){
num+=(rindex+lindex)/2;
}
if(num<sum){
rindex++;
}
else if(num>sum){
lindex++;
}
else{
ArrayList<Integer> al = new ArrayList<Integer>();
for(int i = lindex;i<=rindex;i++){
al.add(i);
}
res.add(al);
//相等时也要记得改变左右指向,不然会在原地死循环。
rindex++;
lindex++;
}
}
return res;
}
(33) 字符流中第一个不重复的字符
public class Solution {
//Insert one char from stringstream
private HashMap<Character,Integer> map = new HashMap<Character,Integer>();
private Queue<Character> queue = new LinkedList<Character>();
public void Insert(char ch)
{
queue.add(ch);
map.put(ch,map.getOrDefault(ch,0)+1);
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
while(!queue.isEmpty()&&map.get(queue.peek())!=1){
queue.poll();
}
if(queue.isEmpty())
return '#';
return queue.peek();
}
}
(34)删除链表中重复的结点
一种实现起来更方便的方法:
1、为此表加一个头节点
2、如何判断是否将此节点加入?p指向当前,q一直寻找到与p值不相同的节点(有可能q最后为空),若p的next为q说明没有与p重复的,若p的next不为q则说明有与其重复的故不加入此节点。
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
if(pHead == null)
return pHead;
ListNode h = new ListNode(-1);
ListNode cur = h,p = pHead;
while(p!=null){
ListNode q = p.next;
//寻找与p值不同的节点
while(q!=null&&p.val == q.val) q = q.next;
//如果q为p的next说明无与p重复的
if(p.next == q){
p.next = cur.next;
cur.next = p;
cur = p;
p = q;
}else{//否则说明由于p重复的
p = q;
}
}
return h.next;
}
}
(35) 按之字形顺序打印二叉树
方法一:利用两个栈,代码量比较大
方法二:利用队列,在需要逆序输出的行将其ArrayList反转即可
注意,在进行下一行输入时,用变量记录此时queue.size()即可知道本层要输出多少节点。然后利用for循环仅将本层节点输出。
public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) {
ArrayList<ArrayList<Integer>> res = new ArrayList();
if(pRoot == null)
return res;
int index = 1;
Queue<TreeNode> queue = new LinkedList();
queue.offer(pRoot);
while(!queue.isEmpty()){
ArrayList<Integer> arr = new ArrayList();
int sum = queue.size();
for(int i = 0;i<sum;i++){
TreeNode node = queue.poll();
arr.add(node.val);
if(node.left!=null)
queue.offer(node.left);
if(node.right!=null)
queue.offer(node.right);
}
if(index%2==0)
Collections.reverse(arr);
index++;
res.add(arr);
}
return res;
}
(36)把二叉树打印成多行
(37)二叉树中和为某一值的路径(三)
方法一:任何一个节点都可能是路径的起点。故计算以每个节点为起点,有多少条路径
public int FindPath (TreeNode root, int sum) {
// write code here
if(root==null)
return 0;
//dfs用来计算以当前节点为起点,其下有多少条路径
int count = dfs(root,sum);
count+=FindPath(root.left,sum);
count+=FindPath(root.right,sum);
//递归计算所有累加和
return count;
}
public int dfs(TreeNode root, int sum){
if(root==null)
return 0;
sum-=root.val;
//因为可能节点有复制,故就算sum=0,也可继续向下找可能回再次数显sum=0的情况
if(sum==0)
return 1+dfs(root.left,sum)+dfs(root.right,sum);
return dfs(root.left,sum)+dfs(root.right,sum);
}
方法二用hashmap辅助实现不理解
(38)连续子数组的最大和(二)
本题在做的过程中忽略的几种情况
1、最大值相等,但当前的长度更长,也要进行长度更新
2、此序列均为负值,故sum<0时也可能出现目标i结果,故sum也要进行max值比较
public int[] FindGreatestSumOfSubArray (int[] array) {
// write code here
int max = Integer.MIN_VALUE;
int sum = 0,l=0,r=0;
int lindex = 0, rindex = 0;
while(rindex<array.length&&lindex<=rindex){
sum+=array[rindex];
if(sum<0){
//解决情况2
if(max<=sum){
max = sum;
l=lindex;
r=rindex;
}
lindex = rindex+1;
rindex = rindex+1;
sum = 0;
}
else{
//添加等号解决情况1
if(max<=sum){
max = sum;
l=lindex;
r=rindex;
}
rindex++;
}
}
return Arrays.copyOfRange(array,l,r+1);
}
另外一种思路:如果我们拿到了当前的和,对于后面一个即将加入的元素
如果加上他这一串会变得更大,我们肯定会加上它,
如果它自己会比加上前面这一串更大,说明从它自己开始连续子数组的和可能会更大。
即dp[i]=max(dp[i−1]+array[i],array[i])
public int[] FindGreatestSumOfSubArray (int[] array) {
int max=array[0];
int sum = array[0];
int lres=0,rres=0,l=0;
for(int i = 1;i<array.length;i++){
//若加上当前值不如当前值本身大,则要更换起点
if(sum+array[i]<array[i]){
l=i;
sum = array[i];
}
//若加上当前值比当前值大,则可继续累计
else{
sum += array[i];
}
if(max<sum||(max==sum&&(i-l+1)>(rres-lres+1))){
max=sum;
lres=l;
rres=i;
}
}
return Arrays.copyOfRange(array,lres,rres+1);
}
(39) 在二叉树中找到两个节点的最近公共祖先
public class Solution {
/**
*
* @param root TreeNode类
* @param o1 int整型
* @param o2 int整型
* @return int整型
*/
public int lowestCommonAncestor (TreeNode root, int o1, int o2) {
return dfs(root,o1,o2).val;
}
private TreeNode dfs(TreeNode root,int o1,int o2){
/*
分支遍历结束的条件
1、到叶子节点,返回null说明无想要的结点
2、找到了o1,o2对应的节点,返回
*/
if(root==null||root.val==o1||root.val==o2)
return root;
TreeNode left = dfs(root.left,o1,o2);
TreeNode right = dfs(root.right,o1,o2);
/*
这里有四种情况
1、两个值都在右子树,则左为空,返回右 (覆盖)
2、两个值都在左子树,则右为空,返回左 (覆盖)
3、两个值分别左右子树,则左右均不为空,返回当前 (覆盖)
4、当前父节点左右子树均无匹配节点,则左右均为空,应返回空,走第一条语句即可完成。
*/
if(left==null)
return right;
if(right==null)
return left;
return root;
}
}