剑指offer这本书,做点笔记吧,以后方便自己再查看,文中也有很多借鉴,在此就不一一引述了。
1、singleton 设计一个类,我们只能生成该类的一个实例
**effective java中推荐
public class Singleton{
private static class SingletonHolder{
private static final Singleton INSTANCE= new Singleton();
}
private Singleton(){}//私有构造
public static final Singleton getInstance(){
return SingletonHolder.INSTANCE;
}
}
**饱汉模式(优点:实现简单;缺点:在不需要的时候,白创建了对象,造成资源的浪费)
public class Singleton{
private singleton(){}
private final static SingleTon instance = new SingleTon();
public static SingleTon getInstance(){
return instance;
}
}
**懒汉式(优点:需要对象的时候才创建;缺点:线程不安全)
public class SingleTon {
private SingleTon(){}
private static instance = null;//new SingleTon();
public static synchronized SingleTon getInstance(){
if(instance == null){
instance = new SingleTon();
}
return instance;
}
}
2、在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数
解析:首先选取数组中右上角的数字。如果该数字等于要查找的数字,查找过程结束;如果该数字大于要查找的数字,剔除这个数字所在的列;如果该数字小于要查找的数字,剔除这个数字所在的行。也就是说如果要查找的数字不在数组的右上角,则每一次都在数组的查找范围中剔除一行或者一列,这样每一步都可以缩小查找的范围,直到找到要查找的数字,或者查找范围为空。
// 二维数组matrix中,每一行都从左到右递增排序,
// 每一列都从上到下递增排序
public static bool find(int[][] matrix, int rows, int columns, int number)
{
bool isFind = false;
if (matrix != null && rows > 0 && columns > 0){
int row = 0; // 从第一行开始
int column = columns - 1; // 从最后一列开始
// 行:从上到下,列:从右到左
while (row < rows && column >= 0){
if (matrix[row, column] == number){
isFind = true;
break;
}
else if (matrix[row, column] > number){
column--;
}
else{
row++;
}
}
}
return isFind;
}
3、请实现一个函数,把字符串中的每个空格替换成”%20”。例如输入“We are happy.”,则输出“We%20are%20happy.”。
解析:
Step1.先遍历一次字符串,这样就能统计出字符串中空格的总数,并可以由此计算出替换之后的字符串的总长度。以前面的字符串”We arehappy.”为例,”We are happy.”这个字符串的长度是14(包括结尾符号’\0’),里面有两个空格,因此替换之后字符串的长度是18。
Step2.从字符串的后面开始复制和替换。准备两个指针,P1和P2。P1指向原始字符串的末尾,而P2指向替换之后的字符串的末尾。接下来向前移动指针P1,逐个把它指向的字符复制到P2指向的位置,直到碰到第一个空格为止。接着向前复制,直到碰到第二、三或第n个空格。
public static void ReplaceBlank(char[] target, int maxLength){
if (target == null || maxLength <= 0){
return;
}
// originalLength 为字符串target的实际长度
int originalLength = 0;
int blankCount = 0;
int i = 0;
while (target[i] != '\0'){ //判断是否结束
originalLength++;
// 计算空格数量
if (target[i] == ' '){
blankCount++;
}
i++;
}
// newLength 为把空格替换成'%20'之后的长度
int newLength = originalLength + 2 * blankCount;
if (newLength > maxLength){
return;
}
// 设置两个指针,一个指向原始字符串的末尾,另一个指向替换之后的字符串的末尾
int indexOfOriginal = originalLength;
int indexOfNew = newLength;
while (indexOfOriginal >= 0 && indexOfNew >= 0){
if (target[indexOfOriginal] == ' '){
target[indexOfNew--] = '0';
target[indexOfNew--] = '2';
target[indexOfNew--] = '%';
}
else{
target[indexOfNew--] = target[indexOfOriginal];
}
indexOfOriginal--;
}
}
4、输入一个链表的头结点,从尾到头反过来打印出每个结点的值。
解析:
首先遍历链表。遍历的顺序是从头到尾,输出的顺序为从尾部到头部,即第一个遍历的最后输出,最后遍历的第一个输出。后进先出(栈结构)。
每经过一个结点的时候,把该结点放到一个栈中。当遍历完整个链表后,再从栈顶开始逐个输出结点的值,此时输出的结点的顺序已经反转过来了。
//链表的结构类定义
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
//(1)使用额外的一个ArrayList存储链表
public static ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
ArrayList<Integer> list=new ArrayList<Integer>();
ListNode head=listNode;
while(head!=null) {
list.add(head.val);
head=head.next;
}
ArrayList<Integer> result=new ArrayList<Integer>();
for(int i=list.size()-1;i>=0;i--) {
result.add(list.get(i));
}
return result;
}
//(2)单向链表通过栈(stack)结构实现
public static ArrayList<Integer> printListFromTailToHeadWithStack(ListNode listNode) {
Stack<Integer> stack = new Stack<Integer>();
ArrayList<Integer> list=new ArrayList<Integer>();
ListNode head=listNode;
while(head!=null) {
stack.push(head.val);
head=head.next;
}
while(!stack.isEmpty()) {
Integer item=stack.pop();
list.add(item);
}
return list;
}
5、重建二叉树(输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。)
解析
在二叉树的前序遍历中,第一个数字是树的根节点的值。在中序遍历中,根节点的值在序列中间,左子树的结点的值位于根节点的值的左边,右子树的位于根节点的右边。再对先序遍历,从第二个节点可是,到中序遍历左子树的长度,就是左子树的先序遍历。递归的过程,右子树同理。
//Definition for binary tree
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
public class Solution {
public TreeNode reConstructBinaryTree(int [] pre,int [] in) {
return build(pre,in,0,pre.length-1,0,in.length-1);
}
public TreeNode build(int[] pre,int[] in,int pstart,int pend,int istart,int iend){
if(pstart>pend) return null;
int cur = pre[pstart];//根节点
int find = istart;
while(find<=iend){//在中序遍历中查找根节点
if(cur==in[find]) break;
else find++;
}
int len = find-istart;
TreeNode res = new TreeNode(cur);//创建根节点
res.left = build(pre,in,pstart+1,pstart+len,istart,find-1);//创建左子树
res.right = build(pre,in,pstart+len+1,pend,find+1,iend);//创建右子树
return res;
}
}
6、用两个栈实现一个队列。分别完成在队列尾部插入结点和在队列头部删除结点的功能。
解析
一个队列包含了两个栈stack1和stack2,操作这两个“先进后出”的栈实现一个“先进先出”的队列CQueue。对于两个栈而言,对于插入操作,都先插入到stack1,也就实现了入队操作。但是出队操作,则需要借助stack2的过渡:当stack2中不为空时,在stack2中的栈顶元素是最先进入队列的元素,可以弹出。如果stack2为空时,我们把stack1中的元素逐个弹出并压入stack2。由于先进入队列的元素被压到stack1的底端,经过弹出和压入之后就处于stack2的顶端了,又可以直接弹出。
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.add(node);
}
public int pop() {
if(stack2.isEmpty()){ //stack2为空的话,将把stack1中的放入到stack2中
while(!stack1.isEmpty()){
stack2.add(stack1.pop());
}
}
return stack2.pop();
}
}
7、旋转数组的最小数字(把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减序列的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1)
解析
旋转数组可以划分为两个排序的子数组,前面的子数组的元素都大于或者等于后面子数组的元素,最小元素恰好是这两个数组的分界线。可以通过二分查找法实现。取中间mid元素,如果array[mid]>array[end],说明最小元素应该在mid的右边,因为我们实际是要找没有旋转之前的第一个元素,观察数组规律可以知道,在mid的右边如果array[mid]
8、斐波那契数列 定义:F(0)=0,F(1)=1,F(n)=F(n-1)+F(n-2)(n≥2,n∈N*)
解析:递归解决的话,由于重复计算导致很慢,解决办法就是从下往上计算。首先根据f(0)和f(1)计算出f(2),再根据f(1)和f(2)计算出f(3)……时间复杂度为O(n)。
public class Solution{
public int Fibonacci(int n){
int result[2]={0,1}
if(n<2)
return result[n];
int fibNminusOne=1;
int fibNminusTwo=0;
int fibN=0;
for(int i=2;i<=n;i++){
fibN=fibNminusOne+fibNminusTwo;
fibNminusTwo=fibNminusTwo;
fibNminusOne=fibN;
}
return fibN;
}
}
9、二进制中1的个数(输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。)
解析:把一个整数减去1,再和原整数做与运算,会把改整数最右边一个1变成0.一个整数的二进制表示中有多少个1,就可以进行多少次这样的运算。(p81)
public class Solution {
public int NumberOf1(int n) {
int count = 0;
while(n!=0){
n=(n&(n-1));
count++;
}
return count;
}
}
10、数值的整数次方(给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。)
//(1)递归
public class Solution {
public double Power(double base, int exponent) {
if(exponent<0){
exponent = -exponent;
return 1/solve(base,exponent);
}
return solve(base,exponent);
}
public double solve(double base, int exponent){
if(exponent==0) return 1;
if(exponent%2==1){
return base*solve(base,(exponent-1)/2)*solve(base,(exponent-1)/2);
}else{
return solve(base,exponent/2)*solve(base,exponent/2);
}
}
}
//(2)
public class Solution {
public double Power(double base, int exponent) {
// 当底数为0,指数为负数时,则抛出异常或者返回0.0
if (equal(base, 0) && exponent < 0) {
return 0.0;
}
// 先对指数进行取绝对值计算
int absExponent = Math.abs(exponent);
double result = powerWithExponent(base, absExponent);
// 判断如果传入的指数是负数,进行取反,否则直接返回
if (exponent < 0) {
result = 1.0 / result;
}
return result;
}
// 计算数值的整数次方
public double powerWithExponent(double base, int absExponent) {
double result = 1.0;
for (int i = 1; i <= absExponent; ++i) {
result *= base;
}
return result;
}
// 判断两个double类型的数值是否相等
public boolean equal(double num1, double num2) {
if ((num1 - num2 > -0.0000001) && (num1 - num2 < 0.0000001)) {
return true;
} else
return false;
}
}
11、打印1到最大的n位数(输入数字n,按顺序打印出从1到最大的n位十进制数,比如输入3,则打印出1,2,3一直到最大的3位数即999)
解析:用数字排列的方法表示:如果我们在数字前面补0的话,就会发现n位所有十进制数其实就是n个从0到9的全排列。也就是说,我们把数字的每一位都从0到9排列一遍,就得到了所有的十进制数。当然打印的时候,我们应该将前面的0补位去掉。
public static void Print1ToMaxOfNDigits_3(int n){
if(n < 0){
return;
}
StringBuffer s = new StringBuffer(n);
for(int i = 0; i < n; i++){
s.append('0');
}
for(int i = 0; i < 10; i++){
s.setCharAt(0, (char) (i+'0'));
Print1ToMaxOfNDigits_3_Recursely(s, n, 0);
}
}
public static void Print1ToMaxOfNDigits_3_Recursely(StringBuffer s, int n , int index){
if(index == n - 1){
PrintNumber(s);
return;
}
for(int i = 0; i < 10; i++){
s.setCharAt(index+1, (char) (i+'0'));
Print1ToMaxOfNDigits_3_Recursely(s, n, index+1);
}
}
public static void PrintNumber(StringBuffer s){
boolean isBeginning0 = true;
for(int i = 0; i < s.length(); i++){
if(isBeginning0 && s.charAt(i) != '0'){
isBeginning0 = false;
}
if(!isBeginning0){
System.out.print(s.charAt(i));
}
}
System.out.println();
}