剑指offer

1.1重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

分析:

因为是树的结构,一般都是用递归来实现。用数学归纳法的思想就是,假设最后一步,就是root的左右子树都已经重建好了,那么我只要考虑将root的左右子树安上去即可。根据前序遍历的性质,第一个元素必然就是root,那么下面的工作就是如何确定root的左右子树的范围。根据中序遍历的性质,root元素前面都是root的左子树,后面都是root的右子树。那么我们只要找到中序遍历中root的位置,就可以确定好左右子树的范围。正如上面所说,只需要将确定的左右子树安到root上即可。递归要注意出口,假设最后只有一个元素了,那么就要返回。

代码:

import java.util.Arrays;
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
    //数组长度为0的时候要处理
    if(pre.length == 0){
        return null;
    }

    int rootVal = pre[0];

    //数组长度仅为1的时候就要处理
    if(pre.length == 1){
        return new TreeNode(rootVal);
    }

    //我们先找到root所在的位置,确定好前序和中序中左子树和右子树序列的范围
    TreeNode root = new TreeNode(rootVal);
    int rootIndex = 0;
    for(int i=0;i<in.length;i++){
        if(rootVal == in[i]){
            rootIndex = i;
            break;
        }
    }

    //递归,假设root的左右子树都已经构建完毕,那么只要将左右子树安到root左右即可
    //这里注意Arrays.copyOfRange(int[],start,end)是[)的区间
    root.left = reConstructBinaryTree(Arrays.copyOfRange(pre,1,rootIndex+1),Arrays.copyOfRange(in,0,rootIndex));
    root.right = reConstructBinaryTree(Arrays.copyOfRange(pre,rootIndex+1,pre.length),Arrays.copyOfRange(in,rootIndex+1,in.length));


    return root;
}

}

1.2二叉树的子树

输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构)

分析

凡二叉树,必递归。使用递归的方法依次判断B是否为A的左右子树。

代码

/**
public class TreeNode {
	int val = 0;
	TreeNode left = null;
	TreeNode right = null;

	public TreeNode(int val) {
    	this.val = val;

	}

}
*/
public class Solution {
	public boolean HasSubtree(TreeNode root1,TreeNode root2) {
    	if(root2==null){
        	return false;
    	}
    	if (root1==null && root2!=null){
        	return false;
    	}
    	boolean flag = false;
    	if(root1.val==root2.val){
        	flag = isSubtree(root1,root2);
        	}
    	if(!flag){
        	flag=HasSubtree(root1.left,root2);
        	if(!flag){
            	flag=HasSubtree(root1.right,root2);
        	}
    	}
    	return flag;
	}
	public boolean isSubtree(TreeNode root1,TreeNode root2){
    	if(root2 == null){
        	return true;
    	}
    	if(root1 == null && root2!=null){
        	return false;
    	}
    	if(root1.val==root2.val){
        	return isSubtree(root1.left,root2.left) && isSubtree(root1.right,root2.right);
    	}
    	return false;
	}
}

1.3二叉树的翻转

操作给定的二叉树,将其变换为源二叉树的镜像。
二叉树的镜像定义:源二叉树
8
/
6 10
/ \ /
5 7 9 11
镜像二叉树
8
/
10 6
/ \ /
11 9 7 5

分析

从根节点开始遍历,将每一个节点的左右子树进行互换

代码

/**
public class TreeNode {
	int val = 0;
	TreeNode left = null;
	TreeNode right = null;

	public TreeNode(int val) {
    	this.val = val;

	}

}
*/
public class Solution {
	public void Mirror(TreeNode root) {
    	if(root!=null){
        	TreeNode t = null;
        	t = root.left;
        	root.left = root.right;
        	root.right=t;
        	Mirror(root.left);
        	Mirror(root.right);
    	}
	}
}

1.4广度遍历二叉树

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

分析

将节点按层放入队列中,依次弹出打印即可。

代码

import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
/**
public class TreeNode {
	int val = 0;
	TreeNode left = null;
	TreeNode right = null;

	public TreeNode(int val) {
    	this.val = val;

	}

}
*/
public class Solution {
	public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
    	Deque<TreeNode> deque = new LinkedList<>();
    	ArrayList<Integer> list = new ArrayList<>();
    	if(root == null){
        	return list;
    	}
    	deque.add(root);
    	while(!deque.isEmpty()){
        	TreeNode t = deque.pop();
        	list.add(t.val);
        	if(t.left!=null){
            	deque.add(t.left);
        	}
        	if(t.right!=null){
            	deque.add(t.right);
        	}
    	}
    	return list;
	}
}

1.5二叉树的后序遍历

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。

分析

BST的后序序列的合法序列是,对于一个序列S,最后一个元素是x (也就是根),如果去掉最后一个元素的序列为T,那么T满足:T可以分成两段,前一段(左子树)小于x,后一段(右子树)大于x,且这两段(子树)都是合法的后序序列。

代码

public class Solution {
	public boolean VerifySquenceOfBST(int [] sequence) {
    	if(sequence.length==0){
        	return false;
    	}
    	return isBST(sequence,0,sequence.length-1);
	}
	public boolean isBST(int []sequence,int start,int end){
    	if (start>=end){
        	return true;
    	}
    	int i = start;
    	for(;i<end;i++){
        	if(sequence[i]>sequence[end]){
            	break;
        	}
    	}
    	for(int j=i;j<end;j++){
        	if(sequence[j]<sequence[end]){
            	return false;
        	}
    	}
    	return isBST(sequence,0,i-1) && isBST(sequence,i,end-1);
	}
}

1.6二叉树的深度遍历

输入一颗二叉树的根节点和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。(注意: 在返回值的list中,数组长度大的数组靠前)

分析

对二叉树进行深度遍历即可。

代码

import java.util.ArrayList;
/**
public class TreeNode {
	int val = 0;
	TreeNode left = null;
	TreeNode right = null;

	public TreeNode(int val) {
    	this.val = val;

	}

}
*/
public class Solution {
	public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) {
    	ArrayList<ArrayList<Integer>> arr=new ArrayList<ArrayList<Integer>>();
    	if(root==null)
        	return arr;
    	ArrayList<Integer> a1=new ArrayList<Integer>();
    	int sum=0;
    	pa(root,target,arr,a1,sum);
    	return arr;
	}
	public void pa(TreeNode root,int target,ArrayList<ArrayList<Integer>> arr, 			ArrayList<Integer> a1,int sum){
    	if(root==null)
        	return ;
    	sum+=root.val;
    
    	if(root.left==null&&root.right==null){
        	if(sum==target)
            	{ a1.add(root.val);
            	arr.add(new ArrayList<Integer>(a1));
            	a1.remove(a1.size()-1);
           
        	}
      	return ;
         
    	}
     
     	a1.add(root.val);
     	pa(root.left,target,arr,a1,sum);
    	pa(root.right,target,arr,a1,sum);
    	a1.remove(a1.size()-1);  
	}
}

1.7二叉树中序遍历

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

分析

使用二叉树的非递归中序遍历,改变指针指向即可

代码

/**
public class TreeNode {
	int val = 0;
	TreeNode left = null;
	TreeNode right = null;

	public TreeNode(int val) {
    	this.val = val;

	}

}
*/
import java.util.Stack;
public class Solution {
	public TreeNode Convert(TreeNode pRootOfTree) {
    	if(pRootOfTree==null)
        	return null;
    	Stack<TreeNode> stack = new Stack<TreeNode>();
    	TreeNode p = pRootOfTree;
    	TreeNode pre = null;// 用于保存中序遍历序列的上一节点
    	boolean isFirst = true;
    	while(p!=null||!stack.isEmpty()){
        	while(p!=null){
            	stack.push(p);
            	p = p.left;
        	}
        	p = stack.pop();
        	if(isFirst){
            	pRootOfTree = p;// 将中序遍历序列中的第一个节点记为root
            	pre = pRootOfTree;
            	isFirst = false;
        	}else{
            	pre.right = p;
            	p.left = pre;
            	pre = p;
        	}      
        	p = p.right;
    	}
    	return pRootOfTree;
	}
}

2.1用两个栈实现队列

用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。

分析:

两个栈,一个用作入队列,一个用作出队列。

代码

import java.util.Stack;

public class Solution {
	Stack<Integer> stack1 = new Stack<Integer>();
	Stack<Integer> stack2 = new Stack<Integer>();

public void push(int node) {
    stack1.push(node);//入队列
}

public int pop() {
    while (stack1.isEmpty() && stack2.isEmpty()) {
        new RuntimeException("队列为空");
    }
    if (stack2.isEmpty()) {
        while (!stack1.isEmpty()) {
            stack2.push(stack1.pop());
        }
    }
    return stack2.pop();
}

}

2.2重新定制栈

定义栈的数据结构,请在该类型中实现一个能够得到栈中所含最小元素的min函数(时间复杂度应为O(1))。
注意:保证测试中不会当栈为空的时候,对栈调用pop()或者min()或者top()方法。

分析

新建一个主栈,一个辅助栈。辅助栈用来存放一系列最小值,当辅助栈为空时,数据同时压入两个栈,不为空时,若数据小于辅助栈栈顶的值,同时压入两个栈。弹出时,若主栈和辅助栈的栈顶值相等,则一起弹出。

代码

import java.util.Stack;

public class Solution {

	private Stack<Integer> s = new Stack<Integer>();
	private Stack<Integer> smin = new Stack<Integer>();
	public void push(int node) {
    	s.push(node);
    	if(smin.isEmpty()){
        	smin.push(node);
    	}
    	else if(smin.peek()>=node){
        	smin.push(node);
    	}
	}

	public void pop() {
    	if(s.peek()==smin.peek()){
        
        	smin.pop();
    	}
    	s.pop();
	}

	public int top() {
    	return s.peek();
	}

	public int min() {
    	return smin.peek();
	}
}

2.3判断栈的弹出顺序

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)

分析

新建一个栈,把压入数组中的数依次压入,碰到跟弹出数组中前面的数值相等的数就弹出,同时弹出数组的数向后挪一位,知道压入数组的数全部压入。此时判断栈中数据的弹出顺序与与弹出数组剩余数据顺序是否一致。

代码

import java.util.ArrayList;
import java.util.Stack;
public class Solution {
	public boolean IsPopOrder(int [] pushA,int [] popA) {
    	int i = 0;
    
    	Stack<Integer> s = new Stack<>();
    	for(int j=0;j<pushA.length;j++){
        	s.push(pushA[j]);
        	if(pushA[j]==popA[i]){
            	s.pop();
            	i++;
        	}
    	}
    	while(!s.isEmpty()){
        	int p = s.pop();
        	if (p!=popA[i]){
            	return false;
        	}
        	i++;
    	}
    	return true;
	}
}

3台阶问题

3.1

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。

分析

如果台阶数为0,则只有0种跳法;
台阶数为1,则只有一种跳法;
台阶数为2,则有一次跳两级和两次各跳一级两种跳法;
若最后一次跳跃只跳了一个台阶,则之前跳跃的台阶数为n-1;若最后一次跳跃跳了两个台阶,则之前跳跃的台阶数为n-2;以此类推。

代码

public class Solution {
	public int JumpFloor(int target) {
    	if (target<=2){
        	return target;
   	 	}
    	return JumpFloor(target-1)+JumpFloor(target-2);
	}
}

3.2

一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

分析

每个台阶可以看作一块木板,让青蛙跳上去,n个台阶就有n块木板,最后一块木板是青蛙到达的位子, 必须存在,其他 (n-1) 块木板可以任意选择是否存在,则每个木板有存在和不存在两种选择,(n-1) 块木板 就有 [2^(n-1)] 种跳法,可以直接得到结果。

代码

public class Solution {
	public int JumpFloorII(int target) {
    	if (target<=2){
        	return target;
    	}
    	return (int)Math.pow(2,target-1);
	}
}

3.3

我们可以用21的小矩形横着或者竖着去覆盖更大的矩形。请问用n个21的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?

比如n=3时,2*3的矩形块有3种覆盖方法.

分析

先看最后一块木板,若木板竖着,则该情况下的覆盖方法为f(n-1)种;若木板横着,则倒数第二块木板也必然横着,则该情况下的覆盖方法为f(n-2)种。

代码

public class Solution {
	public int RectCover(int target) {
    	if (target<=3){
       		return target;
    	}
    	return RectCover(target-1)+RectCover(target-2);
	}
}

4二进制

输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。

分析

Java种有将十进制数转化为二进制数的方法Integer.toBinaryString(n),再使用.ToCharArray()将字符串转化为字符数组。

代码

public class Solution {
	public int NumberOf1(int n) {
    	char chars[] = Integer.toBinaryString(n).toCharArray();
    	int count = 0;
    	for (int i = 0; i <chars.length ; i++) {
        	if(chars[i] == '1'){
            	count++;
        	}
    	}
    	return count;
	}	
}

5排序

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。

分析

使用快速排序的思想容易使偶数的相对位置颠倒,可以退而求其次,使用冒泡的思想来调整数字顺序。

代码

public class Solution {
	public void reOrderArray(int [] array) {
    	for(int i= 0;i<array.length-1;i++){
       		for(int j=0;j<array.length-1-i;j++){
            	if(array[j]%2==0&&array[j+1]%2==1){
                	int t = array[j];
                	array[j]=array[j+1];
                	array[j+1]=t;
            	}
        	}
    	}
	}
}

6链表

6.1

输入一个链表,输出该链表中倒数第k个结点。

分析

暴力遍历链表,将链表中的元素按顺序存放在数组中,然后返回第k个节点。

代码

/*
public class ListNode {
int val;
ListNode next = null;

ListNode(int val) {
    this.val = val;
}
}*/
import java.util.ArrayList;
public class Solution {
	public ListNode FindKthToTail(ListNode head,int k) {
    	if (k==0){
        	return null;
    	}
    	ArrayList<ListNode> list = new ArrayList<>();
    	while(head!=null){
        	list.add(head);
        	head = head.next;
    	}
    	if (k>list.size()){
        	return null;
    	}
    	return list.get(list.size()-k);
	}
}

6.2

输入一个链表,反转链表后,输出新链表的表头

分析

每一次将当前节点的下一个节点保存起来,防止链表断裂,再将当前节点指向上一个节点。

代码

/*
public class ListNode {
	int val;
	ListNode next = null;

	ListNode(int val) {
    this.val = val;
	}
}*/
public class Solution {
	public ListNode ReverseList(ListNode head) {
    	ListNode pre = null;
    	ListNode next = null;
    	if(head==null){
        	return null;
    	}
    	if (head.next == null){
        	return head;
    	}
    	while(head !=null){
       		next = head.next;
        	head.next=pre;
        	pre = head;
        	head = next;
    	}
    	return pre;
	}
}

6.3

输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

分析

首先创建一个头节点并进行记录,然后对两个链表节点值进行比较,按大小顺序插入新的链表。其中一个链表对节点全部插入后停止比较,将剩下的链表全部插入新链表,返回第二个节点。

代码

/*
public class ListNode {
	int val;
	ListNode next = null;

	ListNode(int val) {
    	this.val = val;
	}
}*/
public class Solution {
	public ListNode Merge(ListNode list1,ListNode list2) {
    	ListNode head = new ListNode(0);
    	ListNode root = head;
    	while(list1!=null && list2!=null){
        	if(list1.val<list2.val){
            	head.next=list1;
            	head = head.next;
            	list1=list1.next;
        	}
        	else{
            	head.next = list2;
            	head = head.next;
            	list2 = list2.next;
        	}
    	}
    	if(list1 == null){
        	head.next = list2;
    	}
    	else{
        	head.next=list1;
    	}
    	return root.next;
	}
}

7字符串遍历

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。

分析

从第一个字符开始,依次跟后面不相等的字符交换位置。

代码

import java.util.*;
public class Solution {
	public ArrayList<String> Permutation(String str) {
    	ArrayList<String> result = new ArrayList<String>() ;
    	if(str==null || str.length()==0) { return result ; }

    	char[] chars = str.toCharArray() ;
    	TreeSet<String> temp = new TreeSet<>() ;
    	Permutation(chars, 0, temp);
    	result.addAll(temp) ;
    	return result ;
	}

	public void Permutation(char[] chars, int begin, TreeSet<String> result) {
    	if(chars==null || chars.length==0 || begin<0 || begin>chars.length-1) { return ; }

    	if(begin == chars.length-1) {
        	result.add(String.valueOf(chars)) ;
    	}else {
        	for(int i=begin ; i<=chars.length-1 ; i++) {
            	swap(chars, begin, i) ;

            	Permutation(chars, begin+1, result);

            	swap(chars, begin, i) ;
        	}
    	}
	}

	public void swap(char[] x, int a, int b) {
    	char t = x[a];
    	x[a] = x[b];
    	x[b] = t;
	}
 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值