JAVA实现 剑指offer第二版 4

面试题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;
    }


猜你喜欢

转载自blog.csdn.net/u011010851/article/details/80218480