剑指offer

一、二维数组中的查找

题目描述:

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

我的代码:

import java.util.Scanner;

public class doubleDimensionalArrayFind {
	public static boolean Find(int target, int[][] array) {
		int x = array.length;
		int y = array[0].length;
		for(int i = 0; i < x; i++) {
			for(int j = 0; j < y; j++) {
				if(array[i][j] == target) {
					System.out.println("find it!");
					return true;
				}
			}
		}
		return false;
		
	}
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int num = sc.nextInt();
		int[][] array = new int[3][3];
		for(int i = 0; i < 3; i++) {
			for(int j = 0; j < 3; j++) {
				array[i][j] = sc.nextInt();
			}
		}
		Find(num,array);
	}

}

别人的代码:

import java.util.Scanner;

public class doubleDimensionalArrayFind {
	
	public static boolean Find(int target, int[][] array) {
		int x = array.length;
		int y = array[0].length;
		int i = 0;
		int j = y-1;
		while(i < x && j >= 0) {
			if(array[i][j] > target) {
				j = j-1;
			}else if(array[i][j] < target){
				i = i+1;
			}else {
				System.out.println("Find it!");
				return true;
			}
		}
		return false;
		
	}
	
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int num = sc.nextInt();
		int[][] array = new int[3][3];
		for(int i = 0; i < 3; i++) {
			for(int j = 0; j < 3; j++) {
				array[i][j] = sc.nextInt();
			}
		}
		Find(num,array);
	}

}

思路:

利用二维数组由上到下,由左到右递增的规律,

那么选取右上角或者左下角的元素a[row][col]与target进行比较,

当target小于元素a[row][col]时,那么target必定在元素a所在行的左边,

即col--;

当target大于元素a[row][col]时,那么target必定在元素a所在列的下边,

即row++;

相关知识点:

1.二维数组的行列数用a.length和a[0].length求得


二、替换空格

题目描述:

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

import java.util.Scanner;
public class replaceSpace {
	
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while(sc.hasNext()) {
			String str = sc.nextLine();
			StringBuffer str1 = new StringBuffer(str);
			replaceSpace(str1);
		}
	}
	
	public static String replaceSpace(StringBuffer str) {
		String str2 = str.toString();
		String str3 = str2.replace(" ","%20");
		System.out.println(str3);
		return str3;
		
	}

}

相关知识点:

1.String和StringBuffer的转换

2.replace函数的调用


三、从尾到头打印链表

题目描述:

输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。

解题思路:

利用栈存储链表再输出

import java.util.*;
public class Solution {
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode){
		Stack<Integer> stack = new Stack<>();
		if(listNode != null) {
			stack.push(listNode.val);
			listNode = listNode.next;
		}
		
		ArrayList<Integer> list = new ArrayList<>();
		while(!stack.isEmpty()) {
			list.add(stack.pop());
		}
		return list;
		
	}
}

利用递归:

import java.util.*;
public class Solution {
    private ArrayList<Integer> arr = new ArrayList<>();
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        
        if(listNode == null){
            return arr;
        }
        printListFromTailToHead(listNode.next);
        arr.add(listNode.val);
        return arr;
    }
}
import java.util.ArrayList;
import java.util.*;
public class Solution { 
    public ArrayList<Integer> arr = new ArrayList<>();
    public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
        process(listNode);
        return arr;
    }
    
    public void process(ListNode listNode){
        if(listNode==null){
            return;
        }
        process(listNode.next);
        arr.add(listNode.val);
        return;
    }
    
}

相关知识点:

1.栈的存取

2.链表的读取及指针

3.递归方法


四、重建二叉树

题目描述:

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

题目解析:

二叉树的前序遍历顺序是:先访问根节点,然后前序遍历左子树,再前序遍历右子树。中序遍历顺序是:中序遍历根节点的左子树,然后是访问根节点,最后中序遍历右子树。

1、二叉树的前序遍历序列一定是该树的根节点

2、中序遍历序列中根节点前面一定是该树的左子树,后面是该树的右子树从上面可知,题目中前序遍历的第一个节点{1}一定是这棵二叉树的根节点,根据中序遍历序列,可以发现中序遍历序列中节点{1}之前的{4,7,2}是这棵二叉树的左子树,{5,3,8,6}是这棵二叉树的右子树。然后,对于左子树,递归地把前序子序列{2,4,7}和中序子序列{4,7,2}看成新的前序遍历和中序遍历序列。此时,对于这两个序列,该子树的根节点是{2},该子树的左子树为{4,7}、右子树为空,如此递归下去(即把当前子树当做树,又根据上述步骤分析)。{5,3,8,6}这棵右子树的分析也是这样。

/*public class TreeNode {
     int val;
     TreeNode left;
     TreeNode right;
     TreeNode(int x) { val = x; }
 }*/

public class refactoringBinaryTree {
	public static void main(String[] args) {
		int[] pre = {1,2,4,7,3,5,6,8};
		int[] in = {4,7,2,1,5,3,8,6};
		reConstructBinaryTree(pre, in);
	}
	
	public static TreeNode reConstructBinaryTree(int[] pre, int[] in) {
		TreeNode treeNode = reConstructBinaryTree(pre, 0, pre.length-1, in, 0, in.length-1);
		return treeNode;
	}
	
	public static TreeNode reConstructBinaryTree(int[] pre, int prest, int preend, int[] in, int inst, int inend) {
		if(prest > preend || inst > inend || pre.length != in.length) {
			return null;
		}
		TreeNode treeNode = new TreeNode(pre[prest]);
		for(int i = inst; i <= inend; i++) {
			if(in[i] == pre[prest]) {
				treeNode.left = reConstructBinaryTree(pre, prest+1, prest+i-inst, in, inst, i-1);
				treeNode.right = reConstructBinaryTree(pre, prest+i+1-inst, preend, in, i+1, inend);
				break;
			}
		}
		return treeNode;
		
	}

}

相关知识点:

1.二叉树的遍历

2.递归的使用


五、用两个栈实现队列

问题描述:

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

import java.util.Stack;

public class twoStacktoQueue {
	Stack<Integer> stack1 = new Stack<Integer>();
	Stack<Integer> stack2 = new Stack<Integer>();
	
	public void push(int node) {
		stack1.push(node);
	}
	
	public int pop() {
		if(!stack1.isEmpty() && !stack2.isEmpty()) {
			throw new RuntimeException("Queue is empty!");
		}
		if(stack2.isEmpty()) {
			while(!stack1.isEmpty()) {
				stack2.push(stack1.pop());
			}
		}
		//将stack2中全部出栈后才能再次进栈
		return stack2.pop();
	}

}

相关知识点:

1.栈的入栈和出栈

2.入队:将元素进栈A

   出队:判断栈B是否为空,如果为空,则将栈A中所有元素pop,并push进栈B,栈B出栈; 如果不为空,栈B直接出栈。


六、用两个队列实现栈

问题描述:

用两个队列来实现一个栈,完成push和pop操作。

import java.util.LinkedList;

public class QueueToStack {
	LinkedList<Integer> l1 = new LinkedList<>();
	LinkedList<Integer> l2 = new LinkedList<>();
	public static void main(String[] args) {
		QueueToStack q1 = new QueueToStack();
		q1.push(1);
		q1.push(2);
		q1.push(3);
		q1.push(4);
		System.out.println(q1.pop());
		System.out.println(q1.pop());
		System.out.println(q1.pop());
		System.out.println(q1.pop());
	}
	public void push(int node) {
		l1.add(node);
	}
	
	public int pop() {
		if(!l1.isEmpty() && !l2.isEmpty()) {
			throw new RuntimeException("Queue is not empty");
		}
		
		if(l2.isEmpty()) {
			while(l1.size() > 1) {
				l2.add(l1.poll());
			}
		}
		swap();
		return l2.poll();
	}
	
	public void swap() {
		LinkedList<Integer> tmp = l2;
		l2 = l1;
		l1 = tmp;
	}

}

相关知识点:

1.队列的入队add和出队poll

2.将一个队列用于入栈,另一个队列作为出栈


七、旋转数组中的最小数字

题目描述:

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

import java.util.Scanner;

public class rotateArrayMinNum {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while(sc.hasNext()) {
			int[] num = new int[5];
			for(int i = 0; i < num.length; i++) {
				num[i] = sc.nextInt();
			}
			minNumberInRotateArray(num);
		}
	}
	
	public static int minNumberInRotateArray(int[] array) {
		System.out.println("!!!!");
		int start = 0;
		int mid = start;
		int end = array.length - 1;
		if(array.length == 0) {
			return 0;
		}
		while(array[start] >= array[end]) {
			//左右指针相邻时退出循环
			if(start + 1 == end) {
				mid = end;
				break;
			}
			mid = (start + end)/2;
			if(array[start] == array[mid] && array[start] == array[end]) {
				return minNum(array, start, end);
			}
			if(array[start] <= array[mid]) {
				start = mid;
			}else if(array[mid] < array[end]) {
				end  = mid;
			}
		}
		System.out.println(array[end]);
		return array[end];
		
	}
	
	public static int minNum(int[] array, int start, int end) {
		int result = array[start];
		for(int i = start + 1; i < array.length; i++) {
			result = Math.min(result, array[i]);
		}
		return result;
	}

}

相关知识点:

1.二分法

2.利用两个指针分别指向数组的第一个元素和最后一个元素,找到数组的中间元素,如果该中间元素位于前面的递增子数组,那么它应该大于或者等于第一个指针指向的元素,此时,数组中最小的元素应该位于该中间元素的后面。可以把第一个指针指向该中间元素。如果中间元素位于后面的递增子数组,那么它应该小于或者等于第二个指针指向的 元素,此时该数组中最小的元素应该位于该中间元素的前面,我们可以把第二个指针指向该中间元素。第一个指针总是指向前面递增数组的元素,第二个指针指向后面递增数组的元素,最终第一个指针指向第一个递增数组的最后一个元素,第二个指针指向后面递增数组的第一个元素。

3.注意{1,1,1,0,1}这种情况,遇到该情况需要顺序遍历该数组。


八、跳台阶

题目描述:

import java.util.Scanner;

public class fogJump {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while(sc.hasNext()) {
			int num = sc.nextInt();
			JumpFloor(num);
		}
	}
	
	public static int JumpFloor(int target) {
		int n= 0;
		if(target == 1) {
			n = 1;
		}else if(target == 2) {
			n = 2;
		}else {
			n = JumpFloor(target-1) + JumpFloor(target-2);
		}
		return n;
	}

}

相关知识点:

1.首先考虑只有一个台阶,只有一种跳法,两个台阶则有两种跳法。

2.把n级台阶时的跳法看成n的函数,记为f(n)。n>=2时,第一次只跳一级则为f(n-1),第一次跳两级则为f(n-2),因此,n级台阶的不同跳法总数为f(n)=f(n-1)+f(n-2)。实际上就是斐波那契数列。


九、变态跳台阶

题目描述:

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

import java.util.Scanner;

public class fogJumpII {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while(sc.hasNext()) {
			int num = sc.nextInt();
			System.out.println(JumpFloorII(num));
		}
	}
	
	public static int JumpFloorII(int num) {
		int n = 0;
		if(num == 0) {
			n = 1;
		}else if(num == 1) {
			n = 1;
		}else if(num == 2) {
			n = 2;
		}else {
			for(int i = 0; i < num; i++) {
				n += JumpFloorII(i);
			}
		}
		return n;
	}

}

相关知识点:

1.需要注意该题目可直接从第一阶跳到第n阶,所以需要计入0。


十、矩形覆盖

题目描述:

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

import java.util.Scanner;

public class rectangleCover {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		while(sc.hasNext()) {
			int num = sc.nextInt();
			System.out.println(RectCover(num));
		}
	}
	
	public static int RectCover(int target) {
		int n = 0;
		if(target < 1) {
			n = 0;
		}else if(target == 1) {
			n = 1;
		}else if(target == 2) {
			n = 2;
		}else {
			n = RectCover(target-1) + RectCover(target-2);
		}
		return n;
	}

}

相关知识点:

1.如果用1*m的方块覆盖m*n区域,递推关系式为f(n) = f(n-1) + f(n-m),(n > m)。


十一、二叉树的镜像

题目描述:

操作给定的二叉树,将其变换为源二叉树的镜像。

/**
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) {
        TreeNode temp;
        if(root != null){
            temp = root.left;
            root.left = root.right;
            root.right = temp;
            if(root.left != null){
                Mirror(root.left);
            }
            if(root.right != null){
                Mirror(root.right);
            }
        }
    }
}

相关知识点:

1.先前序遍历这棵树的每个结点,如果遍历到的结点有子结点,就交换它的两个子节点,当交换完所有的非叶子结点的左右子结点之后,就得到了树的镜像


十二、二进制中1的个数

题目描述:

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

第一种直接用十进制转换为二进制

public class Solution {
    public int NumberOf1(int n) {
        String two =  Integer.toBinaryString(n);
        int count = 0;
        for(int i = 0; i < two.length(); i++){
            if(two.charAt(i) == '1'){
                count++;
            }
        }
        return count;
    }
}

第二种方法用减一相与的方法

public class Solution {
    public int NumberOf1(int n) {
        int count = 0;
        while(n != 0){
            count++;
            n = n & (n-1);
        }
        return count;
    }
}

相关知识点:

1.第一种利用十进制转二进制Integer.toBinaryString()方法完成进制转换,再求出1的个数

第二种方法,将整数位减一然后再与原数相与,假设1不在第一位,则减1后第一次出现的1变成了0,1右边的0全都变成了1,1左边的高位不变,将得到的数与原数相与,得到将计算过的1位变成0,原来为0的数还原为0。


十三、数值的整数次方

题目描述:

给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。

保证base和exponent不同时为0

public class Solution {
    public double Power(double base, int exponent) {
        double base1 = base;
        if(exponent > 0){
            for(int i = 1; i < exponent; i++){
            base1 = base1 * base;
            }
        }else if(exponent == 0){
            base1 = 1;
        }else{
            base1 = 1/base;
            base = 1/base;
            for(int i = 1; i < exponent*(-1); i++){
                base1 = base1 * base;
            }
        }
        
        return base1;
  }
}

相关知识点:

1.注意指数为负的情况


十四、调整数组顺序使奇数位于偶数前面

题目描述:

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

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

相关知识点:

1.利用插入排序,遇到偶数时忽略,遇到奇数时与前面数字对比,是偶数的话,将偶数向后移,直到前面所有偶数都移动完成,再将奇数放到指定位置。


十五、树的子结构

题目描述:

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

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

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

    }

}
*/
public class Solution {
    public static boolean HasSubtree(TreeNode root1, TreeNode root2) {
        boolean result = false;
        //当Tree1和Tree2都不为零的时候,才进行比较。否则直接返回false
        if (root2 != null && root1 != null) {
            //如果找到了对应Tree2的根节点的点
            if(root1.val == root2.val){
                //以这个根节点为为起点判断是否包含Tree2
                result = doesTree1HaveTree2(root1,root2);
            }
            //如果找不到,那么就再去root的左儿子当作起点,去判断时候包含Tree2
            if (!result) {
                result = HasSubtree(root1.left,root2);
            }
             
            //如果还找不到,那么就再去root的右儿子当作起点,去判断时候包含Tree2
            if (!result) {
                result = HasSubtree(root1.right,root2);
               }
            }
            //返回结果
        return result;
    }
 
    public static boolean doesTree1HaveTree2(TreeNode node1, TreeNode node2) {
        //如果Tree2已经遍历完了都能对应的上,返回true
        if (node2 == null) {
            return true;
        }
        //如果Tree2还没有遍历完,Tree1却遍历完了。返回false
        if (node1 == null) {
            return false;
        }
        //如果其中有一个点没有对应上,返回false
        if (node1.val != node2.val) {   
                return false;
        }
         
        //如果根节点对应的上,那么就分别去子节点里面匹配
        return doesTree1HaveTree2(node1.left,node2.left) && doesTree1HaveTree2(node1.right,node2.right);
    }
}

相关知识点:

1、首先设置标志位result = false,因为一旦匹配成功result就设为true,

剩下的代码不会执行,如果匹配不成功,默认返回false

2、递归思想,如果根节点相同则递归调用DoesTree1HaveTree2(),

如果根节点不相同,则判断tree1的左子树和tree2是否相同,

再判断右子树和tree2是否相同

3、注意null的条件,HasSubTree中,如果两棵树都不为空才进行判断,

DoesTree1HasTree2中,如果Tree2为空,则说明第二棵树遍历完了,即匹配成功,

tree1为空有两种情况(1)如果tree1为空&&tree2不为空说明不匹配,

(2)如果tree1为空,tree2为空,说明匹配。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值