【面试编程题】-9剑指offer之优化时间和空间效率
来源:互联网 发布:关联矩阵法案例 编辑:程序博客网 时间:2024/05/29 06:59
下面的例题来自剑指offer第5章的内容。很多公司的面试官都把代码的时间效率当做一个考查重点。面试官除了考查应聘者的编程能力之外,还关注应聘者有没有不断优化效率、追求完美的态度和能力。
1.数组中出现次数超过一半的数字
题目描述
数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
思路:
如果采用暴力方,对array[i]都查看是否该值超过一半,时间复杂度为n的平方,太高。如果先排序再查看时间复杂度为nlogn。也可以直接利用额外的空间一次遍历进行次数统计,找出超过一半的数。
public class Solution { public int MoreThanHalfNum_Solution(int [] array) { int len=array.length; if(len==0) return 0; int key=array[0]; int count=1; for(int i=1;i<len;i++){ if(array[i]==key) count++; else{ if(count>0) count--; else{ key=array[i]; count=1; } } } if(count>0 && checkMoreHalf(array,key)) return key; return 0; } public boolean checkMoreHalf(int[] array,int key){ int count=0; for(int i=0;i<array.length;i++){ if(array[i]==key) count++; } if(count>array.length/2) return true; return false; }}
由于该值超过数组的一半那么排序过后该值一定处于中间位置,也就是中位数。可以利用快速排序找partition的方法找到中位数(和找第k大的数类似)。
public int MoreThanHalfNum_Solution(int [] array) { int len=array.length; if(len==0) return 0; int p=partition(array, 0, len-1); while(p!=len/2){ if(p>len/2) p=partition(array,0,p-1); else p=partition(array,p+1,len-1); } if(checkMoreHalf(array,array[p])) return array[p]; return 0; } public int partition(int[] array,int i,int j){ int x=array[i]; while(i<j){ while(i<j && array[j]>=x) j--; if(i<j){ array[i]=array[j]; i++; } while(i<j && array[i]<=x) i++; if(i<j){ array[j]=array[i]; j--; } } array[i]=x; return i; } public boolean checkMoreHalf(int[] array,int key){ int count=0; for(int i=0;i<array.length;i++){ if(array[i]==key) count++; } if(count>array.length/2) return true; return false;}
2.最小的K个数
题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
思路:
和上题一样采用快速排序中partition的思想,时间复杂度是n
import java.util.ArrayList;public class Solution { public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) { ArrayList<Integer> result=new ArrayList<Integer>(); int len=input.length; if(len==0 || len<k || k==0) return result; int p=partition(input,0,len-1); while(p!=k-1){ if(p>k) p=partition(input,0,p-1); else p=partition(input,p+1,len-1); } for(int i=0;i<=p;i++){ result.add(input[i]); } return result; } public int partition(int[] array,int i,int j){ int x=array[i]; while(i<j){ while(i<j && array[j]>=x) j--; if(i<j){ array[i]=array[j]; i++; } while(i<j && array[i]<=x) i++; if(i<j){ array[j]=array[i]; j--; } } array[i]=x; return i; }}
这个题类似于Top k问题。所以可以利用容器来保存当前top k,然后一次遍历后面的数,对容器里的top k进行变换,使得容错中的数始终是top k的数。容器可以使用堆、平衡二叉树、红黑数等。时间复杂度是nlogn。
3.连续子数组的最大和
题目描述
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?
思路:
可以实现动态规划,用个表记录前面位置子数组的最大和,然后利用该表一步步扩大。
也可以采用贪心,取当前和最大的,如下:
public class Solution { public int FindGreatestSumOfSubArray(int[] array) { int len=array.length; if(len==0) return 0; int max=array[0]; int sum=array[0]; for(int i=1;i<len;i++){ sum=Math.max(sum+array[i], array[i]); max=Math.max(max, sum); } return max; }}
4.整数中1出现的次数(从1到n整数中1出现的次数)
题目描述
求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数。
思路:
如果对1到n每个数都计算其1出现的个数,时间复杂度太高了。这道题需要利用数字特点和规律,计算每一位上1出现的次数:
例如百位上1出现次数,数值n在百位上的值是curNum则:
if(curNum==0)
1出现的次数等于比百位更高位数*100。例如n=1023,高位数就是1,百位上出现1的次数是1*100;
if(curNum==1)
1出现的次数等于比百位更高位数*100,再加上低位上的数,再加1。例如n=1123,高位数就是1,低位数是23,百位上出现1的次数是1*100+23+1;
if(curNum>1)
1出现的次数等于比百位更(高位数+1)*100,例如n=1223,高位数就是1,次数百位上出现1的次数是(1+1)*100;
public class Solution { public int NumberOf1Between1AndN_Solution(int n) { int count=0; int factor=1; int curNum; int highNum; int lowNum; while(n/factor!=0){ curNum=(n/factor)%10; lowNum=n%factor; highNum=n/(factor*10); if(curNum==0) count+=highNum*factor; if(curNum==1) count+=highNum*factor+lowNum+1; if(curNum>1) count+=(highNum+1)*factor; factor*=10; } return count; }}
5.把数组排成最小的数
题目描述
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
思路:
可以利用全排列,然后比较所以排序的值找出最小的数,此时时间复杂度是n!。可以利用排序直接排出一个最小的数。
import java.util.ArrayList;import java.util.Arrays;import java.util.Comparator;public class Solution { public String PrintMinNumber(int [] numbers) { int len=numbers.length; if(len==0) return ""; String[] str=new String[len]; for(int i=0;i<len;i++){ str[i]=numbers[i]+""; } Arrays.sort(str, new Comparator<String>(){ @Override public int compare(String o1, String o2) { // TODO Auto-generated method stub String s1=o1+o2; String s2=o2+o1; return s1.compareTo(s2); } }); String result=""; for(int i=0;i<len;i++){ result+=str[i]; } return result; }}
6.丑数
题目描述
把只包含因子2、3和5的数称作丑数(Ugly Number)。例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
思路:
如果从1开始每个数都判断是不是丑数,时间复杂度太高。根据丑数的定义,丑数应该是例一个丑数乘以2、3、5的结果。因此我们可以建一个数组,里面的数字是排好序的丑数,每一个丑数都是前面丑数乘以2、3、5得到的。
public class Solution { public int GetUglyNumber_Solution(int index) { if(index==0) return 0; int[] record=new int[index]; record[0]=1; int i2=0,i3=0,i5=0; int i=1; while(i<index){ int tmp=Math.min(record[i2]*2, record[i3]*3); record[i]=Math.min(record[i5]*5, tmp); while(record[i2]*2<=record[i]) i2++; while(record[i3]*3<=record[i]) i3++; while(record[i5]*5<=record[i]) i5++; i++; } return record[index-1]; }}
7、第一个只出现一次的字符
题目描述
在一个字符串(1<=字符串长度<=10000,全部由大写字母组成)中找到第一个只出现一次的字符,并返回它的位置
思路:
这题没想到什么特别的方法,只能利用HashMap进行统计字符出现的次数,找出第一个次数为1的字符。
import java.util.Map;import java.util.HashMap;public class Solution { public int FirstNotRepeatingChar(String str) { Map<Character,Integer> map=new HashMap<>(); int len=str.length(); for(int i=0;i<len;i++){ char c=str.charAt(i); int count=1; if(map.containsKey(c)) count+=map.get(c); map.put(c, count); } for(int i=0;i<len;i++){ if(map.get(str.charAt(i))==1) return i; } return -1; }}
8.数组中的逆序对
题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
输入例子:
1,2,3,4,5,6,7,0
输出例子:
7
思路:
如果采用暴力法,对每个array[i]都统计一遍逆序对,时间复杂度是n的平方。我们可以利用归并排序的思想(对一个有序的的数组排序排序时间复杂度更低),所以这里对利用有序的数组算逆序对更快。
public class Solution { public int InversePairs(int [] array) { int from=0; int to=array.length-1; return countPairs(array, from,to)%1000000007; } public int countPairs(int[] array,int from,int to){ int count=0; if(from<to){ int mid=(from+to)/2; count=countPairs(array,from,mid); count+=countPairs(array,mid+1,to); int[] newarray=new int[to-from+1]; int i=from; int j=mid+1; int m=0; while(i<=mid && j<=to ){ if(array[i]>array[j]){ count+=mid-i+1; if(count>1000000007)//数值过大求余 { count%=1000000007; } newarray[m++]=array[j++]; } else{ newarray[m++]=array[i++]; } } while(i<=mid){ newarray[m++]=array[i++]; } while(j<=to){ newarray[m++]=array[j++]; } m=0; for(i=from;i<=to;i++){ array[i]=newarray[m++]; } } return count; }}
9、两个链表的第一个公共结点
题目描述
输入两个链表,找出它们的第一个公共结点。
思路:
如果两个链表出现一个公共节点,那么之后的节点都相同。这道题可以先计算两个链表的长度,计算出长度的差n,然后从头遍历两个链表,让较长的链表先走n步。那么两个链表将会在第一个公共节点相遇。
也可以利用空间换时间,利用一个set统计一个链表中出现的节点,然后遍历另一个链表,找出第一个重复的节点。
import java.util.HashSet;/*public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; }}*/public class Solution { public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) { HashSet<ListNode> set=new HashSet<>(); ListNode pNode=pHead1; while(pNode!=null){ set.add(pNode); pNode=pNode.next; } pNode=pHead2; while(pNode!=null){ if(set.contains(pNode)) return pNode; pNode=pNode.next; } return null; }}
- 【面试编程题】-9剑指offer之优化时间和空间效率
- 剑指offer-5-面试34:丑数(优化时间和空间效率)
- 《剑指offer》 第五章优化时间和空间效率 总结
- 剑指offer——优化时间和空间效率
- 剑指offer 第五章 优化时间和空间效率
- 剑指offer-5-面试36:数组中的逆序对(时间效率和空间效率的平衡)
- 剑指offer:(34)时间效率和空间效率的平衡 :丑数
- 剑指offer:(36)时间效率和空间效率的平衡 :数组中的逆序对
- 剑指offer-5-面试31:连续子数组的最大和(时间效率)
- 剑指offer:(35)时间效率和空间效率的平衡 :第一个只出现一次的字符
- 剑指offer:(37)时间效率和空间效率的平衡 :两个链表的第一个公共结点
- 剑指offer 5.3 时间与空间效率平衡 -丑数
- 剑指offer 算法 (时间空间效率的平衡)
- 时间,空间和效率
- 时间,空间和效率
- 剑指offer 35题 【时间空间效率的平衡】第一个只出现一次的字符
- 剑指offer 34题 【时间空间效率的平衡】丑数
- 剑指offer 36题 【时间空间效率的平衡】数组中的逆序对
- JVM基本概念预览
- java-基础-hashmap剖析
- JVM垃圾收集器与内存分配策略
- tomcat 集群,session丢失,黏贴,辅助,session共享总结
- java学习之URL类
- 【面试编程题】-9剑指offer之优化时间和空间效率
- android 调用前摄像头进行拍照
- 芯片积累 74HC4075 三个 三输入 或门 在Multisim中 搜索4075
- VSS
- GZIP压缩原理分析(17)——第五章 Deflate算法详解(五08) 算法分析(02) 格式说明(01) 块首部
- java-基础-hashset剖析
- 关于Maven pom.xml中的元素modules、parent、properties以及import讲解推荐博客
- JackSon学习笔记(一)
- D - Stars