《剑指offer》 要背的代码

目录

3--二维数组中的查找(过)

背:

题4:替换空格(过)

背:

String与StringBuffer之间的转换

类似的题目

题目5:从尾到头打印链表(过)

背:ArrayList的用法

ArrayList的源码分析:

题6 :重建二叉树(过)

背:三种遍历(前序、中序、后序)的六种实现(递归、循环--栈)、层次遍历

39--二叉树的深度(过)

18--树的子结构(过)

25--二叉树中和为某一值的路径(过)

24--二叉树的后序遍历(过)

宽度优先遍历

23--从上到下遍历二叉树(过)

背:

(打个标签,看到这里了)

50--树中两个结点的最低公共祖先(二叉搜索树系列)

27--二叉搜索树与双向链表***

63--二叉搜索树的第k个节点

堆与红黑树

30--求最小的k个数字

背代码:

58--二叉树的下一个节点--活页笔记

66--矩阵中的路径--活页笔记

技巧:

67--机器人的运动范围--活页笔记

32--从1到n整数中1出现的次数--纸质考点

数字序列中某一位的数字--活页笔记

背:

33--把数组排成最小的数

背:

把数字翻译成字符串--活页笔记

礼物的最大价值--活页笔记

最长不含重复字符的子字符串--活页笔记

34--丑数

35--(字符串中)第一个只出现一次的字符

55--字符流中第一个不重复(只出现一次)的字符

36--数组中的逆序对*****

37--两个链表的第一个公共结点

38--数字在排序数组中出现的次数

背:整数二分的终极模板--不咋懂,背下来吧

0~n-1中缺失的数字--活页笔记

40--数组中只出现一次的两个数字

数组中唯一只出现一次的数字--活页笔记*****

数组中数值和下标相等的元素--活页笔记

41--和为S的两个数

41--和为S的连续正数序列

42--翻转单词顺序

StringBuilder

42--左旋字符串

65--滑动窗口的最大值*****

几道滑动窗口有关的面试题

0)何为滑动窗口算法

1) 滑动窗口最大值

2) 无重复字符的最长子串

3) 存在重复元素 II

题目解析

4)长度最小的子数组

面试题28--字符串的排列


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中,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值