【刷题笔记/剑指Offer】Part 1 (1-10)
2016-02-23 10:43
330 查看
剑指Offer,在牛客网上可以在线刷题,感觉比看书本要来的更爽一些,现在开始刷起来!
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
输入一个链表,从尾到头打印链表每个节点的值。
这是一道经典的题目,有两种思路:
1. 利用栈结构,Stack
2. 利用递归
相对来说,利用库中的已有结构通常可以简化问题,以后要多注意。下面附上代码:
1. 从前往后遍历原链表的同时,新建一个链表,从后往前建立新链表:
2. 从前往后遍历原链表的同时,改变原来链表节点指针的指向:
参考:http://www.cnblogs.com/xing901022/p/3760080.html
3.
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
利用java中的已有指令可以很容易达到要求,关键在于命令的选择,怎么过又快又节省空间呢,下面是代码,运行时间<1ms,占用0k:
1. 前序遍历的第一个元素就是根节点。
2. 中序遍历就是所有数从小到大排列,另外,左子树都在根节点的左边,右子树都在根节点的右边,所以根据这点定位根节点来分离左右子树,递归地重建二叉树。
附上代码:
广度优先遍历:
压入root-》【弹出-》压入左节点-》压入右节点】 【·】内循环直到队列为空
深度优先遍历:
压入root-》【弹出-》压入右节点-》压入左节点】 【·】内循环直到栈为空
,痛定思痛,决定用表达式计算,代码如下:
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
这个题目延续了前一道题目的风格,找规律,递归的题目一般都和找规律有关,找到规律,程序就很好编写了:
后面附上找规律的思路:http://www.nowcoder.com/profile/286927/codeBookDetail?submissionId=1522855
1. 二维数组中的查找
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
public class Solution { public boolean Find(int [][] array,int target) { int i, j; for(i = 0; i < array.length; i++) { for(j = 0; j < array[0].length; j++){ if(array[i][j] == target) return true; } } return false; } }这无疑是最简单的方法,但是可以通过按行或者按列进行预判断范围来省去一部分操作。继续想,矩阵是按着从左向右递增,从上到下递增的方式进行排序,利用这点,从左下角开始搜索的话,那么就可以按着元素大于目标就上移,元素小于目标就右移的方式快速寻找目标:
public class Solution { public boolean Find(int [][] array,int target) { int row = array.length - 1; int list = 0; while(row >= 0 && list < array[0].length){ if(array[row][list] == target) return true; else if(array[row][list] > target) row--; else list++; } return false; } }
2. 从尾到头打印链表
输入一个链表,从尾到头打印链表每个节点的值。
这是一道经典的题目,有两种思路:1. 利用栈结构,Stack
2. 利用递归
相对来说,利用库中的已有结构通常可以简化问题,以后要多注意。下面附上代码:
/** * public class ListNode { * int val; * ListNode next = null; * * ListNode(int val) { * this.val = val; * } * } * */ import java.util.Stack; import java.util.ArrayList; public class Solution { public ArrayList<Integer> printListFromTailToHead(ListNode listNode) { Stack<Integer> stack = new Stack<Integer>(); while (listNode != null) { stack.push(listNode.val); listNode = listNode.next; } ArrayList<Integer> list = new ArrayList<Integer>(); while (!stack.isEmpty()) { list.add(stack.pop()); } return list; } }今天和实验室的一个同学聊了一下这个题目,目前她会用三种方法来搞定这个问题,不包括用栈,下面说一下她的方法:
1. 从前往后遍历原链表的同时,新建一个链表,从后往前建立新链表:
public static ListNode reverse1(ListNode node){ if (node != null) { ListNode tmp = new ListNode(node.val); while(node.next != null){ node = node.next; ListNode previous = new ListNode(node.val); previous.next = tmp; tmp = previous; } return tmp; } return node; }
2. 从前往后遍历原链表的同时,改变原来链表节点指针的指向:
参考:http://www.cnblogs.com/xing901022/p/3760080.html
3.
3. 替换空格
请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
利用java中的已有指令可以很容易达到要求,关键在于命令的选择,怎么过又快又节省空间呢,下面是代码,运行时间<1ms,占用0k:public class Solution { public String replaceSpace(StringBuffer str) { if(str==null) { return null; } for(int i=0;i<str.length();i++) { if(str.charAt(i)==' ') { str.replace(i,i+1,"%20"); } } return str.toString(); } }
4. 重建二叉树
输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。
要牢记的几点:1. 前序遍历的第一个元素就是根节点。
2. 中序遍历就是所有数从小到大排列,另外,左子树都在根节点的左边,右子树都在根节点的右边,所以根据这点定位根节点来分离左右子树,递归地重建二叉树。
附上代码:
/** * 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) { if(pre.length == 0 || in.length == 0) return null; TreeNode root = new TreeNode(pre[0]); root = reConstructBinaryTree(pre, 0, pre.length - 1, in, 0, in.length - 1); return root; } public TreeNode reConstructBinaryTree(int[] pre, int pres, int pree, int[] in, int ins, int ine){ if(pres > pree || ins > ine) return null; TreeNode root = new TreeNode(pre[pres]); int index = locate(in, root.val); int d = index - ins; root.left = reConstructBinaryTree(pre, pres + 1, pres + d, in, ins, index - 1); root.right = reConstructBinaryTree(pre, pres + d + 1, pree, in, index + 1, ine); return root; } public int locate(int[] in, int target) { int index = 0; while(in[index] != target) index++; return index; } }看了牛客网上大神的代码,受启发,用哈希表会更简单,利用hashmap中的get可以轻易索引根节点的位置:
/** * 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) { if(pre==null||in==null){ return null; } java.util.HashMap<Integer,Integer> map= new java.util.HashMap<Integer, Integer>(); for(int i=0;i<in.length;i++){ map.put(in[i],i); } return preIn(pre,0,pre.length-1,in,0,in.length-1,map); } public TreeNode preIn(int[] p,int pi,int pj,int[] n,int ni,int nj,java.util.HashMap<Integer,Integer> map){ if(pi>pj){ return null; } TreeNode head=new TreeNode(p[pi]); int index=map.get(p[pi]); head.left=preIn(p,pi+1,pi+index-ni,n,ni,index-1,map); head.right=preIn(p,pi+index-ni+1,pj,n,index+1,nj,map); return head; } }顺便查了一下广度优先和深度优先遍历的方法,核心思想是通过利用队列结构存储来实现广度优先遍历,通过利用栈结构来实现深度优先遍历。
广度优先遍历:
压入root-》【弹出-》压入左节点-》压入右节点】 【·】内循环直到队列为空
public void levelOrderTraversal(){ if(root==null){ System.out.println("empty tree"); return; } ArrayDeque<TreeNode> queue=new ArrayDeque<TreeNode>(); queue.add(root); while(queue.isEmpty()==false){ TreeNode node=queue.remove(); System.out.print(node.value+" "); if(node.left!=null){ queue.add(node.left); } if(node.right!=null){ queue.add(node.right); } } System.out.print("\n"); }
深度优先遍历:
压入root-》【弹出-》压入右节点-》压入左节点】 【·】内循环直到栈为空
public void depthOrderTraversal(){ if(root==null){ System.out.println("empty tree"); return; } ArrayDeque<TreeNode> stack=new ArrayDeque<TreeNode>(); stack.push(root); while(stack.isEmpty()==false){ TreeNode node=stack.pop(); System.out.print(node.value+" "); if(node.right!=null){ stack.push(node.right); } if(node.left!=null){ stack.push(node.left); } } System.out.print("\n"); }
5. 用两个栈实现队列
用两个栈来实现一个队列,完成队列的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.push(stack1.pop()); } int top = stack2.pop(); while(!stack2.isEmpty()) { stack1.push(stack2.pop()); } return top; } }今天牛客网服务器好像出了点儿问题,没法看到其他大神的解答,等看看有没有更好的答案我再贴上来。服务器终于好了,看了一下,这个应该就是最佳解答了。终于对了一次。。。
6. 旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个非递减序列的一个旋转,输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
注意到题目中说的是非递减序列,所以从这里入手,考虑用二分法进行查找,但是注意二分法对于递增有效,所以还要考虑重复元素存在的情况,具体的算法解释请看:http://blog.csdn.net/jsqfengbao/article/details/47108069 。程序如下:public class Solution { public static int minNumberInRotateArray(int [] array) { if(array.length == 0) return 0; if(array.length == 1) return array[0]; int left = 0, mid = left, right = array.length - 1; while(array[left] >= array[right]){ if(right - left <= 1){ mid = right; break; } mid = (left + right) / 2; if(array[left] == array[right] && array[left] == array[mid]) return regular(array, left, right); if(array[left] <= array[mid]) left = mid; else right = mid; } return array[mid]; } public static int regular(int[] array, int left, int right){ int min = array[left]; for(int i = left + 1; i <= right; i++){ if(min > array[i]) min = array[i]; } return min; } }
7. 斐波那契数列
大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
这个题目之前就接触过,最直观的肯定是要用递归啦,几行代码搞定,但是发现栈溢出,痛定思痛,决定用表达式计算,代码如下:
public class Solution { public int Fibonacci(int n) { if(n == 0) return 0; if(n == 1) return 1; int numfn1 = 0, numfn2 = 1; int currentnum = 0; for(int i=2; i<=n; ++i) { currentnum = numfn1+numfn2; numfn1 = numfn2; numfn2 = currentnum; } return currentnum; } }
8. 跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
刚看到这题目的时候,我理解错了题意,以为是要求1和2的组合正好凑成target的种数,没有考虑到顺序问题。考虑顺序问题就纠结了好一阵子,最后写出前几项,找出规律,其实就是斐波那契数列的样式,这给我的提示是遇到递归问题的时候要先写出前几项,找规律。9. 变态跳台阶
一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
这个题目延续了前一道题目的风格,找规律,递归的题目一般都和找规律有关,找到规律,程序就很好编写了:public class Solution { public int JumpFloorII(int target) { int sum = 1; for(int i = 1; i < target; i++) sum*=2; return sum; } }
后面附上找规律的思路:http://www.nowcoder.com/profile/286927/codeBookDetail?submissionId=1522855
10. 矩形覆盖
我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法?
依旧找规律,发现和 题目8 一样的规律,so:public class Solution { public int RectCover(int target) { if(target < 0) return -1; else if(target == 1 || target == 0) return 1; else if(target == 2) return 2; else{ int sum = 0; int fn1 = 1; int fn2 = 2; for(int i = 3; i <= target; i++){ sum = fn1 + fn2; fn1 = fn2; fn2 = sum; } return sum; } } }
相关文章推荐
- Java Web自定义邮箱服务器,实现前端跨域访问
- Mootools遮罩层练习(原为网上的jquery写法)
- 无废话ExtJs 入门教程十七[列表:GridPanel]
- javascript 小白学习指南专题 -- 执行上下文 执行上下文栈
- jQuery.Validate验证库
- JQuery给动态加载的节点绑定有效事件
- jQuery 操作导航栏
- ZK 最少限度加载页面js文件
- javaScript里的二维数组转换为json格式的字符串
- js加减乘除运算重写
- 关于feof()多读取一次的问题(还未解决)
- 理解javascript封装
- Android React Native环境配置以及第一个实例
- 1.0.1 JavaScript Array对象
- 学习Javascript面向对象编程之封装
- 前端资源汇总
- nodejs实现遍历文件夹及其子文件
- HTML 5基础 — 熟悉Canvas
- 神通广大的CSS3选择器
- jquery toggle(listenerOdd, listenerEven)