剑指offer 1-80
记录每天做的题,不定时更新
面试题03. 数组中重复的数字###
找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字
/**
方法一:排序后,遍历数组,判断条件:连续两个元素相等
方法二:遍历放入哈希表。时间复杂度O(n)
方法三:遍历元素,与它大小相等的数组下标元素位置交换,如果相同返回这个重复值
**/
class Solution {
//自己第一次写的,建立了一个数组来保存原始数组的信息,复杂度太高
// public int findRepeatNumber(int[] nums) {
// int []a=new int[nums.length];
// for(int i=0;i<nums.length;i++){
// a[nums[i]]++;
// if(a[nums[i]]>1)
// return nums[i];
// }
// return -1;
// }
public int findRepeatNumber(int[] nums) {
for(int i=0;i<nums.length;){
if(nums[i]!=i&&nums[i]!=nums[nums[i]]){
swap(nums,nums[i],i);
}
else if(nums[i]==i){
i++;
}
//这个else if必须放在上一个后面,否则不能判断0
else if(nums[i]==nums[nums[i]]){
return nums[i];
}
}
return -1;
}
//交换元素
public void swap(int[]nums,int i,int j){
int temp;
temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}
面试题04. 二维数组的查找###
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
注意可能输入的是空数组
/**
思路一:暴力遍历
思路二:将数组左下当做flag值,target小于flag就删除行,target大于flag就删除列,等于就返回。
**/
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
//i是二维数组的行数
int i=matrix.length-1;
int j=0;
//j是二维数组的列数
while(i>=0&&j<matrix[0].length){
if(target==matrix[i][j]){
return true;
}
int c=(target<matrix[i][j])?--i:++j;
}
return false;
}
}
面试题05. 替换空格###
因为strng在java里是定长的,需要构造 StringBuilder对象。
class Solution {
public String replaceSpace(String s) {
StringBuilder res = new StringBuilder();
// for(Character c : s.toCharArray())
// {
// if(c == ' ') res.append("%20");
// else res.append(c);
// }
// return res.toString();
// }
int i=0;
while(i<s.length()){
char a=s.charAt(i);
if(a==32)
res.append("%20");
else
res.append(a);
i++;
}
return res.toString();
}
}
面试题06. 从尾到头打印链表###
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
/**
思路一:递归链表,把节点的值添加到动态数组里
思路二:用链表实现一个栈,出栈对应输出
**/
class Solution {
private ArrayList<Integer>temp=new ArrayList<Integer>();
private LinkedList<Integer>stack=new LinkedList<Integer>();
public int[] reversePrint(ListNode head) {
//return first(head);
return second(head);
}
//方法一
private int[] first(ListNode head){
cur(head);
int result[]=new int[temp.size()];
for(int i=0;i<temp.size();i++){
result[i]=temp.get(i);
}
return result;
}
private void cur(ListNode head){
if(head==null)
return;
cur(head.next);
temp.add(head.val);
}
//方法二
private int[] second(ListNode head){
//入栈
while(head!=null){
stack.addLast(head.val);
head=head.next;
}
//出栈
int[]result=new int[stack.size()];
for(int i=0;i<result.length;i++){
result[i]=stack.removeLast();
}
return result;
}
}
面试题07. 重建二叉树###
输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
HashMap<Integer, Integer> idx_map = new HashMap<>();//用来记录中序遍历的书序
int[] preorder;
int[] inorder;
int pre_index=0;//根节点索引
public TreeNode buildTree(int[] preorder, int[] inorder) {
this.preorder = preorder;
this.inorder = inorder;
int idx = 0;
for (Integer val : inorder)
idx_map.put(val, idx++);
return helper(0, inorder.length-1);
}
TreeNode helper(int left,int right){
//递归返回条件
if(left>right)
return null;
//1.找出根节点值,根节点在中序遍历中的位置
int root_val=preorder[pre_index];
TreeNode root=new TreeNode(root_val);
int root_inorder=idx_map.get(root_val);
pre_index++;
//2.根节点指向左指树
root.left=helper(left,root_inorder-1);
//3.根节点指向右孩树
root.right=helper(root_inorder+1,right);
return root;
}
}
面试题09. 用两个栈实现队列###
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
//复杂度极高,因为需要在两个栈pop push出数据
class CQueue {
//思路:appendTail的数据push进stack1,stack1的数据pop到stack2.
//出队对应stack2的pop,入队对应stack1的push
private Stack<Integer> stack1=new Stack<Integer>();
private Stack<Integer> stack2=new Stack<Integer>();//栈有pop,push,peak三个方法
int size;
public CQueue() {
size=0;
}
public void appendTail(int value) {
while(!stack2.isEmpty()){
stack1.push(stack2.pop());
}
stack1.push(value);
size++;
}
public int deleteHead() {
if(size==0)
return -1;
while(!stack1.isEmpty()){
stack2.push(stack1.pop());
}
size--;
return stack2.pop();
}
}
/**
* Your CQueue object will be instantiated and called as such:
* CQueue obj = new CQueue();
* obj.appendTail(value);
* int param_2 = obj.deleteHead();
*/
面试题10- I. 斐波那契数列###
//显然用递归的思想解题,递归表达式F(N) = F(N - 1) + F(N - 2),返回条件
//递归做法在这里超时了
// class Solution {
// public int fib(int n) {
// return recursion(n)%1000000007;
// }
// private int recursion(int n){
// if(n==1)
// return 1;
// if(n==0)
// return 0;
// int result=recursion(n-1)+recursion(n-2);
// return result;
// }
// }
//因为递归里有很多重复计算的值,我们可以用循环自下而上的计算
class Solution {
public int fib(int n) {
if(n==0)
return 0;
if(n==1)
return 1;
int result=0;
int one=1;
int two=0;
for(int i=2;i<=n;i++){
result=(one+two)%1000000007;
two=one;
one=result;
}
return result;
}
}
面试题11. 旋转数组的最小数字###
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。
参考博客:二分法的细节
//遍历复杂度O(N),二分查找O(log(N))
//可以把数组分为两个排序数组,第一个中的数大于等于第二个数组中的数
class Solution {
public int minArray(int[] numbers) {
int left=0;
int right=numbers.length-1;
while(left<right){
int mid=(left+right)/2;
//mid在第一个排序数组中,我们要找的值在后面,所以left=mid+1;
if(numbers[mid]>numbers[right]){
left=mid+1;
//难点:因为数组中可能出现重复值
}else if(numbers[mid]==numbers[right]){
right--;
//mid在第二个数组中,搜索范围往前
}else if(numbers[mid]<numbers[right]){
right=mid;
}
}
return numbers[left];
}
}

浙公网安备 33010602011771号