目录
背:三种遍历(前序、中序、后序)的六种实现(递归、循环--栈)、层次遍历
3--二维数组中的查找(过)
题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路:
整个二维数组中数字的规律是:从右向左递减、从上到下递增;当寻找目标时,要么从右上角开始,要么从左下角开始
如果目标数比当前数小,向左移动;如果目标数比当前数大,向下移动;如果相等,即找到目标数
注意边界,不要越界了
背:
二维数组的行数:array.length
二维数组的列数:array[0].length
public class Solution {
public boolean Find(int target, int [][] array) {
int i = 0;
int j = array.length - 1;
while(j >= 0 && i < array[0].length){
if(array[j][i] > target)
j--;
else if(array[j][i] < target)
i++;
else
return true;
}
return false;
}
}
题4:替换空格(过)
题目描述
请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
思路:
方法一:用现成的方法 str.replace(oldstr , newstr);
方法二:O(n)的时间复杂度,(1)先从前往后遍历一遍,得到空格总数;(2)获得需要增加的总量;(3)逐个后移。
public String replaceSpace(StringBuffer str) {
String str1 = str.toString();
str1=str1.replace(" ","%20");
return str1;
}
背:
一、String类的常用方法
摘自: https://blog.csdn.net/zhao_yu_lei/article/details/71719504
1.获取:
1)获取字符串str长度
int i = str.length();
2)根据位置(index)获取字符
char c = str.charAt(index);
3)获取字符在字符串中的位置
int i =str.indexOf(char ch); //获取的是第一次出现的位置
int i =str.indexOf(char ch ,int index); //从位置index后获取ch出现的第一次的位置
int i =str.indexOf(str1) ;// 获取str1 在str 第一次出现的位置
int i=str.indexOf(str1, index0);//获取从index位置后str第一次出现的位置
int i = str.lastIndexOf(ch或者 str1) //获取ch或者str1最后出现的位置
2.判断
1)判断是否以指定字符串str1开头、结尾
boolean b = str.startWith(str1) //开头
boolean b = str.endsWith(str1) //结尾
2)判断是否包含某一子串
boolean b = str.contains(str1)
3)判断字符串是否有内容
boolean b = str.isEmpty();
4)忽略大小写判断字符串是否相同
boolean b = str.equalsIgnoreCase(str1);
3.转换
1)将字符数组 -char[] ch- 转化成字符串
i. String str =new String(ch); //将整个数组变成字符串
ii. String str =new String(ch,offset,count)
//将字符数组中的offset位置之后的count个元素转换成字符串
1. String str =String.valueOf(ch);
2. String str =String.copyValueOf(ch,offset,count);
3. String str =String.copyValueOf(ch);
2)将字符串转化为字符数组
char[] ch = str.toCharArray();
3)将字节数组转换为字符串
同上1) 传入类型变为Byte[];
4)将字符串转换为字节数组
Byte[] b = str.toByteArray();
5)将基本数据类型装换成字符串
String str = String.valueOf(基本数据类型数据);
若是整形数据可以用 字符串连接符 + ""
eg : String str = 5+"";
得到字符串 “5”
4.替换 replace();
str.replace(oldchar,newchar)//将str里oldchar变为newchar
str.replace(str1,str2)//将str中str1,变为str2
5.切割 split();
String[] str1 = str.split(","); //将str用 ","分割成String数组
6.子串
String s = str.substring(begin);
// s 为 str 从begin位置到最后的字符串
String s = str.substring(begin,end)
//s 是 str 从begin 位置到end 位置的字符串
7.转换大小写:
String s1 = str. toUpperCase(); //将str变成大写字母
String s2 = str. toLowerCase(); //将str变成小写字母
除去空格:
String s =str.trim();
比较:
int i = str.compareTo(str1);
二、 StringBuffer常用方法
/***StringBuffer 是一个容器,长度可变,可以直接操作字符串,用toString方法变为字符串 **/
1.存储
1)append(); //将指定数据加在容器末尾,返回值也是StringBuffer
eg:
StringBuffer sb = new StringBuffer(//可以加str);
StringBuffer sb1=ab.append(数据) //数据可以任何基本数据类型
注:此时sb == sb1他们是同一对象,意思是可以不用新建sb1直接 sb.append(数据) 使用时之后接使用sb
2)insert();// 插入
sb.insert(index ,数据);
2.删除
sb.delete(start ,end); //删除start到end的字符内容
//注意:这里的所有包含index的操作都是含头不含尾的
sb.deleteCharAt(index);//删除指定位置的字符
//清空StringBuffer缓冲区
sb=new StringBuffer();
sb.delete(0,sb.length());
3.获取
char c = sb.charAt(index);//获取index上的字符
int i = sb.indexOf(char)://获取char字符出现的第一次位置
//与 String 中的获取方法一致参考前面
4.修改 String类中无次操作方法
sb =sb.replace(start,end,string)//将从start开始到end的字符串替换为string;
sb.setCharAr(index ,char);//将index位置的字符变为新的char
5.反转 sb.reverse();//将sb倒序
6. getChars(int srcBegin,int srcEnd,char[] ch,int chBegin)
//将StringBuffer缓冲区中的指定数据存储到指定数组中
三、StringBuilder
StringBuilder 和 StringBuffer 方法和功能完全一致只是一个是早期版本(StringBuffer)是线程安全的,由于发现利用多线程对同一String数据操作的情况是很少的,为了提高效率idk1.5以后有StringBuilder 类。意思是多线程操作同一字符串的时候用StringBuffer 安全,现在一般用StringBuilder
String与StringBuffer之间的转换
来源:http://www.oschina.net/code/snippet_2261089_47352
package demo;
/* String与StringBuffer之间的转换
* String -> StringBuffer
* 方式一:构造方法
* 方式二:通过append方法
* StringBuffer -> String
* 方式一:通过构造方法
* 方式二:通过toString方法
* */
public class StringAndStringBufferSwitch {
public static void main(String[] args) {
//String -> StringBuffer
//创建一个String对象
String str = "Hi Java!";
System.out.println(str);
//方式一:构造方法
StringBuffer buffer = new StringBuffer(str);
System.out.println(buffer);
//方式二:通过append方法
StringBuffer buffer2 = new StringBuffer();
buffer2.append(str);
System.out.println(buffer2);
//StringBuffer -> String
//创建一个StringBuffer对象
StringBuffer buffer3 = new StringBuffer();
buffer3.append("Happy birthday Java!");
System.out.println(buffer3);
//方式一:通过构造方法
String str2 = new String(buffer3);
System.out.println(str2);
//方式二:通过toString方法
String str3 = buffer3.toString();
System.out.println(str3);
}
}
类似的题目
有两个排序的数组A1和A2,内存在A1的末尾有足够多的空余空间容纳A2。请实现一个函数,把A2中所有的数字插入到A1中并且所有的数字是排序的。
思路:
从尾到头比较A1和A2中的数字,并把较大的数字复制到A1的合适位置。
题目5:从尾到头打印链表(过)
题目描述
输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
思路:
方法一:利用特定的函数,插入到链表的第一个位置
import java.util.ArrayList;
public class Solution {
public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList List = new ArrayList();
ListNode t = listNode;
while(t!=null){
List.add(0,t.val);
t = t.next;
}
return List;
}
}
方法二:利用递归
方法三:借助栈
背:ArrayList的用法
ArrayList是动态数组,动态增加和减少元素
初始化:
方式一:
ArrayList<String> list = new ArrayList<String>();
String str01 = String("str01");
String str02 = String("str02");
list.add(str01);
list.add(str02);
方式二:
ArrayList<String> list = new ArrayList<String>(){
{add("str01"); add("str02");}};
其他:
增加元素到链表中
boolean add(Element e)
增加指定元素到链表尾部.
void add(int index, Element e)
增加指定元素到链表指定位置.
从链表中删除元素
void clear()
从链表中删除所有元素.
E remove(int index)
删除链表中指定位置的元素.
protected void removeRange(int start, int end)
删除链表中从某一个位置开始到某一个位置结束的元素。
获取链表中的元素
E get(int index)
获取链表中指定位置处的元素.
Object[] toArray()
获取一个数组,数组中所有元素是链表中的元素.(即将链表转换为一个数组)
修改某个元素
E set(int index, E element)
将链表中指定位置上的元素替换成新元素。
搜索元素
boolean contains(Object o)
如果链表包含指定元素,返回true.
int indexOf(Object o)
返回元素在链表中第一次出现的位置,如果返回-1,表示链表中没有这个元素。
int lastIndexOf(Object o)
返回元素在链表中最后一次出现的位置,如果返回-1,表示链表中没有这个元素。
检查链表是否为空
boolean isEmpty()
返回true表示链表中没有任何元素.
获取链表大小
int size()
返回链表长度(链表包含元素的个数).
ArrayList的源码分析:
https://blog.csdn.net/sihai12345/article/details/79382649
第一遍看得模糊,以后再去看一看
/**
* The size of the ArrayList (the number of elements it contains).
*/
private int size; // 实际元素个数
transient Object[] elementData;
上面的 size 是指 elementData 中实际有多少个元素,而 elementData.length 为集合容量,表示最多可以容纳多少个元素
题6 :重建二叉树(过)
题目描述
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
思路:
不含重复的数字 很关键,所以用一个hash来存储:<中序元素,位置>,递归(前序的开始,前序的结束,中序的开始,中序的结束),每次前序的第一个元素就是当前的根结点(建立新节点),再去遍历右子树,再去遍历左子树
易错:随着递归的次数的增加,右子树的前序和中序不是一样的范围。
代码实现
import java.util.HashMap;
import java.util.Map;
/**
* Definition for binary tree
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Solution {
public static TreeNode reConstructBinaryTree(int [] pre,int [] in) {
//用一个hash可以快速找到中序中的元素的位置
Map<Integer,Integer> hash = new HashMap<Integer, Integer>();
for(int i = 0;i<in.length;i++) hash.put(in[i] , i);//(元素,位置)
//递归
return dfs(hash,pre ,in , 0 ,pre.length-1, 0 , in.length-1);
}
public static TreeNode dfs(Map<Integer,Integer> hash,int [] pre,int [] in,int preLeft , int preRight , int inLeft , int inRight){
//如果当前长度为零呢?
if(preLeft>preRight) return null;
//当前区间一定有元素
//先找一下当前区间的根结点
TreeNode root = new TreeNode(pre[preLeft]);//前序遍历的第一个元素就是根结点
//再在中序遍历里面找出根结点的位置
int k = hash.get(root.val);
//然后开始左子树、右子树
//左边的长度是 k-inLeft
TreeNode left = dfs(hash , pre , in , preLeft+1 , preLeft+k-inLeft,inLeft,k-1);
//右子树的前序和中序是一样的范围???为什么写一样的范围就报错呢?
TreeNode right = dfs(hash , pre ,in , preLeft+k-inLeft+1 , preRight , k+1 , inRight);
root.left = left;
root.right = right;
return root;
}
}
背:三种遍历(前序、中序、后序)的六种实现(递归、循环--栈)、层次遍历
摘自:https://blog.csdn.net/coder__666/article/details/80349039
前序(先序):
package test;
//前序遍历的递归实现与非递归实现
import java.util.Stack;
public class Test
{
public static void main(String[] args)
{
TreeNode[] node = new TreeNode[10];//以数组形式生成一棵完全二叉树
for(int i = 0; i < 10; i++)
{
node[i] = new TreeNode(i);
}
for(int i = 0; i < 10; i++)
{
if(i*2+1 < 10)
node[i].left = node[i*2+1];
if(i*2+2 < 10)
node[i].right = node[i*2+2];
}
preOrderRe(node[0]);
}
public static void preOrderRe(TreeNode biTree)
{//递归实现
System.out.println(biTree.value);
TreeNode leftTree = biTree.left;
if(leftTree != null)
{
preOrderRe(leftTree);
}
TreeNode rightTree = biTree.right;
if(rightTree != null)
{
preOrderRe(rightTree);
}
}
public static void preOrder(TreeNode biTree)
{//非递归实现
Stack<TreeNode> stack = new Stack<TreeNode>();
while(biTree != null || !stack.isEmpty())
{
while(biTree != null)
{
System.out.println(biTree.value);
stack.push(biTree);
biTree = biTree.left;
}
if(!stack.isEmpty())
{
biTree = stack.pop();
biTree = biTree.right;
}
}
}
}
class TreeNode//节点结构
{
int value;
TreeNode left;
TreeNode right;
TreeNode(int value)
{
this.value = value;
}
}
中序:
public static void midOrderRe(TreeNode biTree)
{//中序遍历递归实现
if(biTree == null)
return;
else
{
midOrderRe(biTree.left);
System.out.println(biTree.value);
midOrderRe(biTree.right);
}
}
public static void midOrder(TreeNode biTree)
{//中序遍历费递归实现
Stack<TreeNode> stack = new Stack<TreeNode>();
while(biTree != null || !stack.isEmpty())
{
while(biTree != null)
{
stack.push(biTree);
biTree = biTree.left;
}
if(!stack.isEmpty())
{
biTree = stack.pop();
System.out.println(biTree.value);
biTree = biTree.right;
}
}
}
后序(难):
算法核心思想:
首先要搞清楚先序、中序、后序的非递归算法共同之处:用栈来保存先前走过的路径,以便可以在访问完子树后,可以利用栈中的信息,回退到当前节点的双亲节点,进行下一步操作。
后序遍历的非递归算法是三种顺序中最复杂的,原因在于,后序遍历是先访问左、右子树,再访问根节点,而在非递归算法中,利用栈回退到时,并不知道是从左子树回退到根节点,还是从右子树回退到根节点,如果从左子树回退到根节点,此时就应该去访问右子树,而如果从右子树回退到根节点,此时就应该访问根节点。所以相比前序和后序,必须得在压栈时添加信息,以便在退栈时可以知道是从左子树返回,还是从右子树返回进而决定下一步的操作。
public static void postOrderRe(TreeNode biTree)
{//后序遍历递归实现
if(biTree == null)
return;
else
{
postOrderRe(biTree.left);
postOrderRe(biTree.right);
System.out.println(biTree.value);
}
}
public static void postOrder(TreeNode biTree)
{//后序遍历非递归实现
int left = 1;//在辅助栈里表示左节点
int right = 2;//在辅助栈里表示右节点
Stack<TreeNode> stack = new Stack<TreeNode>();
Stack<Integer> stack2 = new Stack<Integer>();//辅助栈,用来判断子节点返回父节点时处于左节点还是右节点。
while(biTree != null || !stack.empty())
{
while(biTree != null)
{//将节点压入栈1,并在栈2将节点标记为左节点
stack.push(biTree);
stack2.push(left);
biTree = biTree.left;
}
while(!stack.empty() && stack2.peek() == right)
{//如果是从右子节点返回父节点,则任务完成,将两个栈的栈顶弹出
stack2.pop();
System.out.println(stack.pop().value);
}
if(!stack.empty() && stack2.peek() == left)
{//如果是从左子节点返回父节点,则将标记改为右子节点
stack2.pop();
stack2.push(right);
biTree = stack.peek().right;
}
}
}
eg:39--二叉树的深度;18--树的子结构;25--二叉树中和为某一值的路径;6--重建二叉树;24--二叉树的后序遍历
39--二叉树的深度(过)
题目描述
输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
public int TreeDepth(TreeNode root) {
if(root == null) return 0;
return Math.max(TreeDepth(root.left),TreeDepth(root.right))+1;
}
题目2:输入一棵二叉树的根结点,判断该树是不是平衡二叉树。如果某二叉树中任意结点的左右子树的深度不超过1,那么它就是一棵平衡二叉树。
分析:上面那种解法是需要重复遍历结点的多次的解法,简单但不足以打动面试官
而每个节点只遍历一次的解法,才是面试官喜欢的:
后序遍历,从下至上,最后遍历根结点
18--树的子结构(过)
题目:输入两棵二叉树A和B,判断A和B是不是A的子结构。
分析:分为两步:(1)在树A中找到和B的根结点的值一样的节点R;--遍历树(2)再判断树A中以R为根结点的子树是不是包含和树B一样的结构
注意:一定要检查边界情况,一定要检查树为null的情况
public class Solution {
public boolean IsSubtree(TreeNode root1,TreeNode root2) {
if(root2==null) return true;
if(root1==null) return false;
if(root2.val != root1.val) return false;
else{
return IsSubtree(root1.left,root2.left)&&IsSubtree(root1.right,root2.right);
}
}
public boolean BianLi(TreeNode root1,TreeNode root2){
if(root1==null) return false;
if(root1.val == root2.val){
return IsSubtree(root1,root2)||BianLi(root1.left,root2)||BianLi(root1.right,root2);
}
return BianLi(root1.left,root2)||BianLi(root1.right,root2);
}
public boolean HasSubtree(TreeNode root1,TreeNode root2) {
if(root2==null) return false;
return BianLi(root1,root2);
}
}
25--二叉树中和为某一值的路径(过)
题目描述
输入一颗二叉树的跟节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,