【面试编程题】-8剑指offer之解决面试题的思路
2016-07-27 22:38
495 查看
下面的例题来着剑指offer第4章的,主要是告诉大家,做面试题的时候,怎么找到思路。
画图让抽象问题形象化
不少于数据结构相关的问题,如二叉树、二维数组、链表等问题,都可以采用画图的方式来分析,找到题目隐含的规律和特点。
1.二叉树的镜像
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
输入描述:
二叉树的镜像定义:源二叉树
8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5
思路:
这个题就是其实就是让树的左右节点进行交换,树的问题,一般递归解决就比较简单。
2.顺时针打印矩阵
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
思路
这道题没有什么复杂的算法,就是循环的开始结束条件要弄清楚。
举例让抽象问题具体化
与画图的方法一样,我们也可以借助举例的方法来思考分析复杂的问题。
3.包含min函数的栈
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
思路:
如果用暴力搜索的方法,每次都要查询一遍,复杂度过高。如果用一个变量min来保存当前最小的,当栈中数据被弹出时,这时候min就有可能失效了。所以使用一个辅助的栈,通过入栈顺序保存所有可能的最小值。
4.栈的压入、弹出序列
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
思路:
通过模拟入栈出栈的顺序找到思路。
5.从上往下打印二叉树
题目描述
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
思路:
树的层数遍历使用队列。
6.二叉搜索树的后序遍历序列
题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
思路:
后序遍历,根在数组的最后一个位置。在根据二叉搜索树性质:左子树节点的值小于根,右子数的值大于根,可以把数组分成前后两部分。不满足现在返回false,满足对左右子树继续递归。
7.二叉树中和为某一值的路径
题目描述
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
思路:
树的深度优先遍历,可以使用递归。
分解让复杂问题简单化
对于复杂的问题,可以尝试把问题进行分解成若干的小问题,然后再逐个的解决这些小问题。
8.复杂链表的复制
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
思路:
可以先根据链表创建新的节点,同时进行next节点连接,而不管random节点,新链表建立后,然后再进行各个节点与其random节点连接。在创建r节点时候可以使用map记录节点间的映射关系,再连接的时候查找更快。
9.二叉搜索树与双向链表
题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
思路:
双向链表和二叉树的结构很像,面试的时候经常遇到相互转换的问题。还是采用分治递归
10.字符串的排列
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 结果请按字母顺序输出。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
思路:
排列问题都可以采用从前向后依次交换数组位置,来构造新的排列。
画图让抽象问题形象化
不少于数据结构相关的问题,如二叉树、二维数组、链表等问题,都可以采用画图的方式来分析,找到题目隐含的规律和特点。
1.二叉树的镜像
题目描述
操作给定的二叉树,将其变换为源二叉树的镜像。
输入描述:
二叉树的镜像定义:源二叉树
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 tmp=root.right; root.right=root.left; root.left=tmp; //递归 Mirror(root.right); Mirror(root.left); } } }
2.顺时针打印矩阵
题目描述
输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字,例如,如果输入如下矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.
思路
这道题没有什么复杂的算法,就是循环的开始结束条件要弄清楚。
import java.util.ArrayList; public class Solution { public ArrayList<Integer> printMatrix(int [][] matrix) { ArrayList<Integer> result=new ArrayList<Integer>(); //循环控制条件 int m=0; int row=matrix.length-1; if(row<0) return result; int col=matrix[0].length-1; //位置坐标 int i=0,j=0; while(m<=row && m<=col){ //右 while(j<=col){ result.add(matrix[i][j++]); } j--; i++; //向下,需满足剩余的矩阵不止一行 if(m<row){ while(i<=row) result.add(matrix[i++][j]); i--; j--; } //向左,需满足剩余的矩阵不止一行,不止一列 if(m<row && m<col){ while(j>=m) result.add(matrix[i][j--]); j++; i--; } if(m<col && m<row-1){ while(i>m) result.add(matrix[i--][j]); i++; j++; } row--; col--; m++; } return result; } }
举例让抽象问题具体化
与画图的方法一样,我们也可以借助举例的方法来思考分析复杂的问题。
3.包含min函数的栈
题目描述
定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。
思路:
如果用暴力搜索的方法,每次都要查询一遍,复杂度过高。如果用一个变量min来保存当前最小的,当栈中数据被弹出时,这时候min就有可能失效了。所以使用一个辅助的栈,通过入栈顺序保存所有可能的最小值。
import java.util.Stack; public class Solution { private Stack<Integer> stack1=new Stack<>(); private Stack<Integer> stack2=new Stack<>(); public void push(int node) { stack1.push(node); if(stack2.isEmpty() || node<stack2.peek()) stack2.push(node); else stack2.push(stack2.peek()); } public void pop() { stack1.pop(); stack2.pop(); } public int top() { return stack1.peek(); } public int min() { return stack2.peek(); } }
4.栈的压入、弹出序列
题目描述
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
思路:
通过模拟入栈出栈的顺序找到思路。
import java.util.Stack; public class Solution { public boolean IsPopOrder(int [] pushA,int [] popA) { Stack<Integer> stack=new Stack<>(); int j=0; for(int i=0;i<popA.length;i++){ while(stack.isEmpty() || stack.peek()!=popA[i]){ if(j<pushA.length) stack.push(pushA[j++]); else return false; } stack.pop(); } return true; } }
5.从上往下打印二叉树
题目描述
从上往下打印出二叉树的每个节点,同层节点从左至右打印。
思路:
树的层数遍历使用队列。
import java.util.ArrayList; /** public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } */ import java.util.LinkedList; public class Solution { public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) { ArrayList<Integer> result=new ArrayList<>(); if(root==null) return result; LinkedList<TreeNode> queue=new LinkedList<>(); queue.add(root); while(!queue.isEmpty()){ TreeNode curNode=queue.pop(); result.add(curNode.val); if(curNode.left!=null) queue.add(curNode.left); if(curNode.right!=null) queue.add(curNode.right); } return result; } }
6.二叉搜索树的后序遍历序列
题目描述
输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。
思路:
后序遍历,根在数组的最后一个位置。在根据二叉搜索树性质:左子树节点的值小于根,右子数的值大于根,可以把数组分成前后两部分。不满足现在返回false,满足对左右子树继续递归。
import java.util.Arrays; public class Solution { public boolean VerifySquenceOfBST(int [] sequence) { int len=sequence.length; if(len==0) return false; return VerifySquenceOfBST(sequence,0,len-1) ; } // public boolean VerifySquenceOfBST(int[] sequence,int from,int to){ if(from>=to) return true; int root=sequence[to]; int i=from; int mid=0; for(;i<to;i++){ if(sequence[i]>root){ break; } } mid=i; for(;i<to;i++){ if(sequence[i]<root) return false; } return VerifySquenceOfBST(sequence,from,mid-1) && VerifySquenceOfBST(sequence,mid,to-1); } }
7.二叉树中和为某一值的路径
题目描述
输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。
思路:
树的深度优先遍历,可以使用递归。
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 { ArrayList<ArrayList<Integer>> allPath=new ArrayList<>(); public ArrayList<ArrayList<Integer>> FindPath(TreeNode root,int target) { ArrayList<Integer> path=new ArrayList<>(); if(root==null) return allPath; DFS(root,path,0,target); return allPath; } public void DFS(TreeNode root,ArrayList<Integer> path,int sum,int target){ ArrayList<Integer> childPath=new ArrayList<>(path); childPath.add(root.val); sum=sum+root.val; if(sum==target && root.left==null && root.right==null){ allPath.add(childPath); return; } if(sum<target){ if(root.left!=null){ DFS(root.left,childPath,sum,target); } if(root.right!=null){ DFS(root.right,childPath,sum,target); } } } }
分解让复杂问题简单化
对于复杂的问题,可以尝试把问题进行分解成若干的小问题,然后再逐个的解决这些小问题。
8.复杂链表的复制
题目描述
输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)
思路:
可以先根据链表创建新的节点,同时进行next节点连接,而不管random节点,新链表建立后,然后再进行各个节点与其random节点连接。在创建r节点时候可以使用map记录节点间的映射关系,再连接的时候查找更快。
import java.util.Map; import java.util.HashMap; /* public class RandomListNode { int label; RandomListNode next = null; RandomListNode random = null; RandomListNode(int label) { this.label = label; } } */ public class Solution { public RandomListNode Clone(RandomListNode pHead) { if(pHead==null) return null; Map<RandomListNode,RandomListNode> map1=new HashMap<>(); Map<RandomListNode,RandomListNode> map2=new HashMap<>(); RandomListNode result=new RandomListNode(-1); RandomListNode oldNode=pHead; RandomListNode newNode=result; while(oldNode!=null){ newNode.next=new RandomListNode(oldNode.label); map1.put(newNode.next, oldNode); map2.put(oldNode, newNode.next); newNode=newNode.next; oldNode=oldNode.next; } newNode=result.next; while(newNode!=null){ newNode.random=map2.get(map1.get(newNode).random); newNode=newNode.next; } return result.next; } }
9.二叉搜索树与双向链表
题目描述
输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
思路:
双向链表和二叉树的结构很像,面试的时候经常遇到相互转换的问题。还是采用分治递归
/** public class TreeNode { int val = 0; TreeNode left = null; TreeNode right = null; public TreeNode(int val) { this.val = val; } } */ public class Solution { public TreeNode Convert(TreeNode pRootOfTree) { TreeNode reult; if(pRootOfTree==null) return null; if(pRootOfTree.left!=null){ TreeNode leftHead=Convert(pRootOfTree.left); TreeNode tmpNode=leftHead; while(tmpNode.right!=null){ tmpNode=tmpNode.right; } tmpNode.right=pRootOfTree; pRootOfTree.left=tmpNode; reult=leftHead; }else{ reult=pRootOfTree; } if(pRootOfTree.right!=null){ TreeNode rightHead=Convert(pRootOfTree.right); pRootOfTree.right=rightHead; rightHead.left=pRootOfTree; } return reult; } }
10.字符串的排列
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 结果请按字母顺序输出。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
思路:
排列问题都可以采用从前向后依次交换数组位置,来构造新的排列。
import java.util.ArrayList; import java.util.Collections; public class Solution { public ArrayList<String> Permutation(String str) { ArrayList<String> list=new ArrayList<>(); char[] c=str.toCharArray(); changeStr(c,0,list); Collections.sort(list); return list; } public void changeStr(char[] c,int index,ArrayList<String> list){ if(index==c.length-1){ list.add(String.valueOf(c)); } for(int i=index;i<c.length;i++){ if(i==index || c[i]!=c[index]){ swap(c,index,i); changeStr(c, index+1, list); swap(c,i,index); } } } public void swap(char[] c,int i,int j){ char tmp=c[i]; c[i]=c[j]; c[j]=tmp; } }
相关文章推荐
- [置顶]Android 面试题汇总
- [置顶]数据类型转换(面试题)
- java面试题2--方法重写与重载、this和super关键字
- 经典算法面试题目-替换字符串的内容(1.5)
- 经典算法面试题目-替换字符串的内容(1.5)
- iOS面试题,看看你究竟知道多少(二)
- iOS面试题,看看你究竟知道多少(一)
- 程序员必须知道的几个Git代码托管平台
- java面试题1--继承--看程序写结果
- 如何在面试中提问
- 阿里巴巴常考面试题及汇总答案
- 程序猿面试经验
- 码农生产工具之:rc文件
- Java Web-1: 初级程序员目录和纲要
- 阿里2015校招面试回忆录(成功拿到offer)
- 做一个有品位的程序员
- Android面试知识点汇集
- 做好这些面试准备,你还担心什么?
- XML和JSON优缺点-- 面试题
- 那些年薪百万的程序员“咸鱼翻身”没有透露的秘密