面试题39:数组中出现次数超过一半的数字
如果将数组排序,位于数组中间的数字一定是出现次数超过的那个数,有o(n)的算法可以得到数组中第k大的数字;在快排中,随机选择一个数字,调整数组中数字的顺序,调整后若刚好处在n/2处则找到;小于n/2则中位数处在左边,大于n/2则位于左边 |
o(n0 |
出现次数超过数组长度一半,说明其出现的次数超过其余的总和,遍历数组,出现相同+1,不同-1;回到0则选择当前开始计数 |
o(n) |
//找出数后需要对数进行检查,有可能不是超过半数;超过半数的数只会有一个;
public static int MoreThanHalfNum(int[] nums){
if (nums == null || nums.length == 0){
return -1;
}
if (nums.length == 1){
return nums[0];
}
int count = 1;
int temp = nums[0];
for (int x = 1;x<nums.length;x++){
if (nums[x] == temp){
count++;
}else {
count--;
}
if (count == 0){
temp = nums[x];
count = 1;
}
}
//check temp
if (check(nums,temp) == true){
return temp;
}
return -1;
}
public static boolean check(int[] nums,int temp){
int count = 0;
for (int x : nums){
if (x == temp){
count++;
}
}
if (count > nums.length /2){
return true;
}else {
return false;
}
}
面试题40:最小的k个数
利用o(n)可得数组中第k大的数,且数组左边的数都小于第k位,则前k位为最小的k个数(不一定有序),需要变换数组顺序 |
o(n) |
额外用k单位存储单元,遍历数则存入,在满了的情况下,判断新数与存下的数中最大的数,若小于,则替换,若大于则继续,可以选择最大堆或红黑树来进行存储(排序,更新效率o(logk),适用于大数据的情况; |
o(nlogk) |
//treemap底部用红黑树实现,且满足不能重复加入相同值得特征;
public static TreeMap getMaxK(int[] nums, int k){
if (nums == null || nums.length == 0 || nums.length < k || k<=0){
return null;
}
TreeMap<Integer,Object> treeMap = new TreeMap();
for (int x : nums){
if (treeMap.size() < k){
treeMap.put(x,null);
}else {
int temp = treeMap.lastEntry().getKey();
if (x < temp){
treeMap.pollLastEntry();
treeMap.put(x,null);
}
}
}
return treeMap;
}
面试题41:数据流中的中位数
数组存储,数组无序 |
插入o(1) ;得到中位数o(n) |
数组存储,数组保持有序 |
插入o(n);得到中位数o(1) |
链表存储,有序 |
插入o(n);得到中位数o(1) |
二叉搜索树 |
平均o(logn),最差o(n);平均o(logn),最差o(n); |
最大堆和最小堆 |
o(logn) ; o(1) |
建立一个最大堆一个最小堆,保持最大堆的最大值小于最小堆的最小值,且个数相差不大于1; |
public static void getMiddle(Scanner it){
if (it == null || !it.hasNextInt()){
return;
}
PriorityQueue<Integer> minheap = new PriorityQueue<>();
PriorityQueue<Integer> maxheap = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2-o1;
}
});
int count = 0;
while (it.hasNextInt()){
int temp = it.nextInt();
count ++;
if (count % 2 == 0){
if (maxheap.size()!=0 && temp < maxheap.peek()){
int delmax = maxheap.poll();
maxheap.add(temp);
minheap.add(delmax);
}else {
minheap.add(temp);
}
}else {
if (minheap.size()!= 0 && temp > minheap.peek()){
int delmin = minheap.poll();
minheap.add(temp);
maxheap.add(delmin);
}else {
maxheap.add(temp);
}
}
System.out.println(getMid(maxheap,minheap,count));
}
it.close();
}
public static float getMid(PriorityQueue<Integer> maxheap,PriorityQueue<Integer> minheap,int count){
if (maxheap.peek() == null){
return minheap.peek();
}
if (minheap.peek() == null){
return maxheap.peek();
}
if (count % 2 != 0){
return maxheap.size() > minheap.size() ? maxheap.peek():minheap.peek();
}else {
return ((float)maxheap.peek()+minheap.peek())/2;
}
}
面试题42:连续子数组的最大和
遍历数组,设置两个变量,一个记录当前累加和,一个记录最大累加和,当加入当前元素累加和变为负数时,则从下一个元素开始从头累加; |
//补充了当数组都为负数和0时,应取最大的负数作为输出;
public static int getMaxsum(int[] nums){
if (nums == null || nums.length == 0){
invaild = true;
return 0;
}
if (nums.length == 1){
return nums[0];
}
int maxsum = 0;
int count = 0;
for (int x :nums){
count = count+x;
if (count > maxsum){
maxsum = count;
}
if (count < 0){
count = 0;
}
}
if (maxsum == 0){
return getmax(nums);
}
return maxsum;
}
public static int getmax(int[] nums){
int max = nums[0];
for (int x:nums){
if (x>max){
max = x;
}
}
return max;
}
面试题43:1-n整数中1出现的次数
将数划分为2部分,例如21345化为0-1345与1346-21345。递归求解,其中先计算最高位出现1的次数,再计算其余位出现1的次数; |
public static int Count1(int num){
if (num <= 0){
return 0;
}
String st = num +"";
char[] st_num = st.toCharArray();
return Count1_core(st_num,0);
}
public static int Count1_core(char[] st_num,int high){
if ( high >st_num.length -1){
return 0;
}
int first = st_num[high] - '0';
int lenth = st_num.length - high;
if (lenth == 1 && first!=0){
return 1;
}
if (lenth == 1 && first == 0){
return 0;
}
int numFirstDigit = 0;
if (first > 1){
numFirstDigit = (int)Math.pow(10,lenth-1);
}else if (first == 1){
String reback = "";
for (int x = high+1;x<st_num.length;x++){
reback = reback+st_num[x];
}
numFirstDigit = Integer.parseInt(reback)+1;
}
int numOtherDigit = first*(lenth-1)*(int)Math.pow(10,lenth-2);
int numRecursive = Count1_core(st_num,high+1);
return numFirstDigit+numOtherDigit+numRecursive;
}
面试题44:数字序列中某一位的数字
序列是有规律的,0,1,2...排列而成的,按位数可以分析其原本的数,再得到该位; |
//处理时将0位单独处理规则更统一;
public static int getDigit(int index){
if (index < 0){
return -1;
}
if (index == 0){
return 0;
}
index = index - 1;
int count = 1;
while (index >= 0){
if (index - count * 9 * (int)Math.pow(10,count-1) > 0){
index = index - count * 9 * (int)Math.pow(10,count-1);
count++;
}else {
int temp = index / count;
int remain = index % count;
int number = (int)Math.pow(10,count-1) + temp;
String number_st = number+"";
char[] number_char = number_st.toCharArray();
return number_char[remain]-'0';
}
}
return -1;
}
面试题45:把数组排成最小的数
求出全排列,再取全排列中最小的 |
o(n!) |
定义一种比较规则,m > n 当 mn > mn,对数组排序,再输出 |
o(nlogn) |
public static void getMinString(int[] nums){
if (nums == null || nums.length == 0){
return;
}
if (nums.length == 1){
System.out.println(nums[0]);
}
//用字符串表示数组元素避免拼接后出现溢出
PriorityQueue<String> stringPriorityQueue = new PriorityQueue<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
int String_ans = compareString(o1+o2,o2+o1);
if (String_ans == -1){
return -1;
}else if (String_ans == 1){
return 1;
}
else return 1;
}
});
for (int x : nums){
stringPriorityQueue.add(x+"");
}
while (stringPriorityQueue.size() != 0){
System.out.print(stringPriorityQueue.poll());
}
}
public static int compareString(String st1,String st2){
if (st1 == null || st2 == null){
return 0;
}
//长度相同
if (st1.length() == 0){
return 0;
}
char[] st1_char = st1.toCharArray();
char[] st2_char = st2.toCharArray();
for (int x = 0;x<st1.length();x++){
if (st1_char[x] < st2_char[x]){
return -1;
}
}
return 1;
}
面试题46:把数字翻译成字符串
动态规划:f(i) = f(i+1) + c * f(i+2) c当连起来的两位数在10-25范围时为0,其他为1;从后到前计算,将计算的结果存在数组中避免重复计算; |
public static int TurntoString(int num){
if (num<0){
return 0;
}
if (num <=9){
return 1;
}
String st_num = num+"";
char[] char_num = st_num.toCharArray();
int[] count = new int[char_num.length+1];
count[char_num.length] = 1;
count[char_num.length-1] = 1;
for (int x = char_num.length-2;x>=0;x--){
int twodigit = Integer.parseInt(char_num[x]+""+char_num[x+1]);
int cantranslate = 0;
if (twodigit>=10 && twodigit<=25){
cantranslate = 1;
}
count[x] = count[x+1]+cantranslate*count[x+2];
}
return count[0];
}
面试题47:礼物的最大价值
动态规划:f(i,j) = max{f(i-1,j),f(i,j-1)+gift[i,j] ,用循环来避免重复计算 使用一行来保存中间结果即可需要的值一个在 j -1位置,一个在j位置,计算完后替换位置j |
public static int getGift(int[][] gifts){
if (gifts == null || gifts.length == 0 || gifts[0].length == 0){
return 0;
}
int rows = gifts.length;
int cols = gifts[0].length;
int[] count = new int[cols];
for (int x = 0 ;x<rows;x++){
for (int y = 0;y<cols;y++){
int left = 0;
int up = 0;
if (x>0){
up = count[y];
}
if (y>0){
left = count[y-1];
}
count[y] = Math.max(up,left)+gifts[x][y];
}
}
return count[cols-1];
}
面试题48:最长不含重复字符的子字符串
f(i)为以i结尾的最长不含重复字符的子字符串;i若未出现过,f(i) = f(i-1)+1;i若出现过,计算与上次出现的距离,若大于f(i-1)则 f(i) = f(i-1)+1;若小于等于,则f(i) = d;每个字符出现的最后位置记录下来。 |
//需要保存的中间结果只有前面一个,故用一个变量即可
public static int getLongestSubString(char[] chars){
if (chars == null || chars.length == 0 ){
return 0;
}
if (chars.length == 1){
return 1;
}
int count = 1;
int[] distence = new int[26];
distence[chars[0]-'a'] = 1;
for (int x = 1; x<chars.length;x++){
char temp = chars[x];
int temp_int = temp - 'a';
if (temp_int < 0 || temp_int> 25){
return 0;
}
if (distence[temp_int] == 0){
count = count + 1;
}else {
int di_x = x - distence[temp_int]+1;
if (di_x <= count){
count = di_x;
}else {
count = count+1;
}
}
distence[temp_int] = x+1;
}
return count;
}
面试题51:数组中的逆序对
在归并排序的过程中完成计算,o(nlogn),归并时需要从后到前进行插入 |
//才发现原来归并的过程中,原数组与复制数组有一个替换的过程;
public static int InverseCouple(int[] nums){
if (nums == null||nums.length == 0 ||nums.length == 1){
return 0;
}
int[] copy = new int[nums.length];
return InverseCouple_core(nums,copy,0,nums.length-1);
}
public static int InverseCouple_core(int[] nums,int[] copy,int start,int end){
if (start == end){
copy[start] = nums[start];
return 0;
}
int middle = (end-start)/2;
int left = InverseCouple_core(copy,nums,start,start+middle);
int right = InverseCouple_core(copy,nums,start+middle+1,end);
//merge 只能使用从后向前,否则会出现无法判断的情况
int i = start+middle;
int j = end;
int temp = end;
int count = 0;
while (i>=start && j>=start+middle+1){
if (nums[i]<nums[j]){
copy[temp--] = nums[j--];
}else {
copy[temp--] = nums[i--];
count += j-start-middle;
}
}
//已计算过反序
while (i>=start){
copy[temp--] = nums[i--];
}
while (j>=start+middle+1){
copy[temp--] = nums[j--];
}
return left+right+count;
}
面试题52:两个链表的第一个公共节点
遍历第一个,每读一个就去第二个找有没有相同的 |
o(mn) |
从相同节点开始,后面的节点均相同,用两个栈将两个链表分别存储,再分别查看栈顶元素是否相同,找到第一个相同的 |
o(m+n),o(m+n) |
遍历两个数组得到长度,长的先走差值,再一起走,找到第一个相同的 |
o(m+n) |
public static Node TheFirstPublicNode(Node head1,Node head2){
if (head1 == null|| head2 == null||head1.next == null||head2.next == null){
return null;
}
int lenth1 = getlenth(head1);
int lenth2 = getlenth(head2);
int steps = lenth1 > lenth2 ? lenth1-lenth2:lenth2-lenth1;
Node index1 = head1;
Node index2 = head2;
if (lenth1>=lenth2){
for (int x = 1;x<=steps;x++){
index1 = index1.next;
}
}else {
for (int x =1;x<=steps;x++){
index2 = index2.next;
}
}
while (index1 != null){
if (index1.value == index2.value){
return index1;
}
index1 = index1.next;
index2 = index2.next;
}
return null;
}
public static int getlenth(Node head){
int lenth1 = 0;
Node temp = head;
while ( temp!=null){
lenth1++;
temp = temp.next;
}
return lenth1;
}