zoukankan      html  css  js  c++  java
  • 剑指offer编程题66道题 26-35

    26.二叉搜索树与双向链表

    题目描述

    输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。
     

    中序遍历思路:
    按照右中左的顺序,中序遍历对节点的访问顺序和转换完链表从左到右的顺序是一样的。所以在中序遍历时完成相邻两个节点的互指即可。

    具体做法是把前一个节点记录下来然后pre->right=cur;cur->left=pre。

    /**
    public class TreeNode {
        int val = 0;
        TreeNode left = null;
        TreeNode right = null;
    
        public TreeNode(int val) {
            this.val = val;
    
        }
    
    }
    */
    public class Solution {
        TreeNode pre = null;
        public TreeNode Convert(TreeNode pRootOfTree) {
            if(pRootOfTree == null) return pRootOfTree;
             
            Convert(pRootOfTree.right);
            if(pre == null){
                pre = pRootOfTree;
            } else {
                pre.left = pRootOfTree;
                pRootOfTree.right = pre;
                pre = pRootOfTree;
            }
            Convert(pRootOfTree.left);
             
            return pre;
        }
    }
    View Code

    最开始我采用的是这种解法,有两个问题:

    1.采用左中右的中序遍历,遍历完之后头结点还得从右到左挪回来

    2.更严重的问题是,我将pre设置为局部传递的变量,由于pre是引用的值传递,在递归回退的时候,pre的引用时上一次遍历pre的副本,而不是遍历后更改的值。解决方法就是讲pre设置为全局变量。

    切记!!!java只有值传递!只有值传递!只有值传递!

    对于基本类型,java都是传值。而对于引用类型,其实java也是通过值传递的,只是传递的值不是实例本身,而是实例的引用的副本。

    参考java的传值与传引用

    错误解法:

    /**
    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) {
            if(pRootOfTree == null)
                return null;
            TreeNode pre = null;
            ConvertHelper(pRootOfTree,pre);
            TreeNode res = pRootOfTree;
            while(res.left != null){
                res = res.left;
            }
            return res;
        }
        
        public void ConvertHelper(TreeNode cur, TreeNode pre){
            if(cur == null)
                return;
            ConvertHelper(cur.left,pre);
            cur.left = pre;
            if(pre!=null) pre.right = cur;
            pre = cur;
            ConvertHelper(cur.right,pre);
        }
    }
    View Code

    非递归实现:

    链接:https://www.nowcoder.com/questionTerminal/947f6eb80d944a84850b0538bf0ec3a5
    来源:牛客网
    
    import java.util.Stack;
    public class Solution {
        public TreeNode Convert(TreeNode pRootOfTree) {
            if(pRootOfTree == null) return pRootOfTree;
             
            TreeNode list = null;
            Stack<TreeNode> s = new Stack<>();
            while(pRootOfTree != null || !s.isEmpty()){
                if(pRootOfTree != null) {
                    s.push(pRootOfTree);
                    pRootOfTree = pRootOfTree.right;
                } else {
                    pRootOfTree = s.pop();
                    if(list == null)
                        list = pRootOfTree;
                    else {
                        list.left = pRootOfTree;
                        pRootOfTree.right = list;
                        list = pRootOfTree;
                    }
                    pRootOfTree = pRootOfTree.left;
                }
            }
             
            return list;
        }
    }
    View Code

    27.字符串的排列

    题目描述

    输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
    import java.util.ArrayList;
    import java.util.Collections;
    public class Solution {
        public ArrayList<String> Permutation(String str) {
           ArrayList<String> list = new ArrayList<String>();
           if(str !=null && str.length() > 0){
               PermutationHelper(str.toCharArray(),0,list);
               Collections.sort(list);
           }
           return list; 
        }
        
        public void  PermutationHelper(char[] cs, int i, ArrayList<String> list){
            if(cs.length-1 == i){
                String val = String.valueOf(cs);
                if(!list.contains(val)){
                    list.add(val);
                }
            }else{
                for(int j=i; j<cs.length;j++){
                    swap(i,j,cs);
                    PermutationHelper(cs,i+1,list);
                    swap(i,j,cs);
                }
            }
        }
        
        public void swap(int i,int j,char[] cs){
            char temp = cs[i];
            cs[i] = cs[j];
            cs[j] = temp;
        }
    }
    View Code

     最后一个循环是递归调用swap交换前后两个字符,在最后交换完成入List之后再交换回来,回到初始状态再进下一个循环

    28.数组中出现次数超过一半的数字

    题目描述

    数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。
    例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。
    第一种普通解法:耗时10ms
    import java.util.*;
    public class Solution {
        public int MoreThanHalfNum_Solution(int [] array) {
            Map<Integer,Integer> count = new HashMap<>();
            if(array.length == 1)
                return array[0];
            for(int i:array){
                if(!count.containsKey(i)){
                    count.put(i, 1);
                }else{
                    count.put(i,count.get(i)+1);
                    if(count.get(i) > array.length/2)
                        return i;
                }
            }
            return 0;
        }
    }
    View Code

    第二种解法:耗时10ms

    采用阵地攻守的思想:
    第一个数字作为第一个士兵,守阵地;count = 1;
    遇到相同元素,count++;
    遇到不相同元素,即为敌人,同归于尽,count--;当遇到count为0的情况,又以新的i值作为守阵地的士兵,继续下去,到最后还留在阵地上的士兵,有可能是主元素。
    再加一次循环,记录这个士兵的个数看是否大于数组一般即可。

    public class Solution {
        public int MoreThanHalfNum_Solution(int [] array) {
           if(array.length == 1)
                return array[0];
           int result = array[0];
           int times = 0;
           for(int i = 0;i<array.length; i++){
               if(times == 0){
                   result = array[i];
                   times = 1;
               }else if(result == array[i]){
                   times++;
               }else{
                   times--;
               }
           }
            
            times=0;
            for(int i=0;i<array.length;i++){
                if(result==array[i]){
                    times++;
                }
           }
           if(times*2<=array.length){
               result=0;
           }
           return result;
            
    }
    }
    View Code

    29.最小的K的个数

    题目描述

    输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
    1.偷懒解法(利用jdk自带的排序api):
    import java.util.*;
    public class Solution {
        public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
            ArrayList<Integer> list = new ArrayList<>();
            if(k>input.length)
                return list;
            Arrays.sort(input);
            for(int i:Arrays.copyOfRange(input, 0, k)){
                list.add(i);
            }
            return list;
        }
    }
    View Code

     2.创建一个大小为k的数据容器,如果容器还没有有了k个数字,直接放入这个数到容器当中;如果容器中有了k个数字了,找出这已有的k个数字中的最大值,然后拿待插的数和最大值进行比较,小就替换,大就抛弃。如果用二叉树来实现这个容器,那么我们可以在O(logk)实现查找替换操作,对于n个输入数字而言,总的时间效率为O(nlogk)。

    import java.util.ArrayList;
    import java.util.PriorityQueue;
    import java.util.Comparator;
    public class Solution {
       public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {
           ArrayList<Integer> result = new ArrayList<Integer>();
           int length = input.length;
           if(k > length || k == 0){
               return result;
           }
            PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
     
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o2.compareTo(o1);
                }
            });
            for (int i = 0; i < length; i++) {
                if (maxHeap.size() != k) {
                    maxHeap.offer(input[i]);
                } else if (maxHeap.peek() > input[i]) {
                    Integer temp = maxHeap.poll();
                    temp = null;
                    maxHeap.offer(input[i]);
                }
            }
            for (Integer integer : maxHeap) {
                result.add(integer);
            }
            return result;
        }
    }
    View Code

    最大堆直接只用了PriorityQueue实现,它的默认实现是最小堆,改变他的排序方式就可以实现最大堆。

    PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(k, new Comparator<Integer>() {
     
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o2.compareTo(o1);
                }
            });

    3.冒泡法

    这种方法比较简单,就是时间复杂度为O(n*k),稍高

    链接:https://www.nowcoder.com/questionTerminal/6a296eb82cf844ca8539b57c23e6e9bf
    来源:牛客网
    
    public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
            ArrayList<Integer> al = new ArrayList<Integer>();
            if (k > input.length) {
                return al;
            }
            for (int i = 0; i < k; i++) {
                for (int j = 0; j < input.length - i - 1; j++) {
                    if (input[j] < input[j + 1]) {
                        int temp = input[j];
                        input[j] = input[j + 1];
                        input[j + 1] = temp;
                    }
                }
                al.add(input[input.length - i - 1]);
            }
            return al;
        }
    View Code

     30.连续子数组的最大和

    HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)

    1.两个循环遍历,时间复杂度为O(n2

    public class Solution {
        public int FindGreatestSumOfSubArray(int[] array) {
             double max = -1.0/0.0;
            for(int i = 0;i<array.length;i++){
               double submax = -1.0/0.0;
                int sum = 0;
                for (int j=i;j<array.length;j++){
                    sum = sum + array[j];
                    if(sum > submax)
                        submax = sum;
                }
                if(submax>max)
                    max = submax;
        }
            return (int)max;
    }
    }
    View Code
    public class Solution {
        public int FindGreatestSumOfSubArray(int[] array) {
             int max = array[0];
            for(int i = 0;i<array.length;i++){
               int submax = array[i];
                int sum = array[i];
                for (int j=i+1;j<array.length;j++){
                    sum = sum + array[j];
                    if(sum > submax)
                        submax = sum;
                }
                if(submax>max)
                    max = submax;
        }
            return max;
    }
    }
    View Code

    2.采用动态规划法,时间复杂度为O(n)

    public class Solution {
        public int FindGreatestSumOfSubArray(int[] array) {
        if(array.length == 0) return 0;
            int sum = array[0];
            int tempsum = array[0];
            for(int i = 1;i<array.length;i++){
                tempsum = (tempsum<0)?array[i]:tempsum + array[i];
                sum = (tempsum > sum) ? tempsum : sum;
        }
        return sum;
        }
    }
    View Code

     31.1—n整数中1出现的次数

     时间复杂度为O(nlogn)

    public class Solution {
        public int NumberOf1Between1AndN_Solution(int n) {
            int number = 0;
            for(int i=1;i<=n;i++){
                number = number+numberOf1(i);
            }
            return number;
        }
        
        public int numberOf1(int i){
            int number = 0;
            while( i!= 0){
                if(i % 10 ==1)
                    number++;
                i = i/10;
            }
            return number;
        }
    }
    View Code

     时间复杂度为O(logn)

    //主要思路:设定整数点(如1、10、100等等)作为位置点i(对应n的各位、十位、百位等等),分别对每个数位上有多少包含1的点进行分析
        //根据设定的整数位置,对n进行分割,分为两部分,高位n/i,低位n%i
        //当i表示百位,且百位对应的数>=2,如n=31456,i=100,则a=314,b=56,此时百位为1的次数有a/10+1=32(最高两位0~31),每一次都包含100个连续的点,即共有(a%10+1)*100个点的百位为1
        //当i表示百位,且百位对应的数为1,如n=31156,i=100,则a=311,b=56,此时百位对应的就是1,则共有a%10(最高两位0-30)次是包含100个连续点,当最高两位为31(即a=311),本次只对应局部点00~56,共b+1次,所有点加起来共有(a%10*100)+(b+1),这些点百位对应为1
        //当i表示百位,且百位对应的数为0,如n=31056,i=100,则a=310,b=56,此时百位为1的次数有a/10=31(最高两位0~30)
        //综合以上三种情况,当百位对应0或>=2时,有(a+8)/10次包含所有100个点,还有当百位为1(a%10==1),需要增加局部点b+1
        //之所以补8,是因为当百位为0,则a/10==(a+8)/10,当百位>=2,补8会产生进位位,效果等同于(a/10+1)
    View Code
    public class Solution {
            public int NumberOf1Between1AndN_Solution(int n) {
             int count=0;
             int i=1;
            for(i=1;i<=n;i*=10)
            {
                //i表示当前分析的是哪一个数位
                int a = n/i,b = n%i;
                if(a%10==1)
                    count=count+(a+8)/10*i+a%10*(b+1);
                else 
                    count=count+(a+8)/10*i;
            }
            return count;
            }
    }
    View Code

     32.把数组排成最小的数

    题目描述

    输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。
    例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
     * 解题思路:
     * 先将整型数组转换成String数组,然后将String数组排序,最后将排好序的字符串数组拼接出来。关键就是制定排序规则。
     * 排序规则如下:
     * 若ab > ba 则 a > b,
     * 若ab < ba 则 a < b,
     * 若ab = ba 则 a = b;
     * 解释说明:
     * 比如 "3" < "31"但是 "331" > "313",所以要将二者拼接起来进行比较
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.Comparator;
    
    public class Solution {
       public String PrintMinNumber(int [] numbers) {
            /**
             * 1.用list装numbers数组中的数
             * 2.使用collection.sort进行排序,排序是将str1+""+str2和str2+""+str1的大小进行比较
             * 3.将排序后的数组进行拼接
             */
            if(numbers == null || numbers.length==0) return "";
            ArrayList<Integer> list = new ArrayList<Integer>();
            for(int i:numbers){
                list.add(i);
            }
            Collections.sort(list, new Comparator<Integer>(){
                public int compare(Integer str1,Integer str2){
                    String s1 = str1 + "" + str2;
                    String s2 = str2 + "" + str1;
                    return s1.compareTo(s2);
                }
            });
            
            StringBuffer sb= new StringBuffer();
            for(int i:list)
                sb.append(i);
            
            return sb.toString();
            
        }
    }
    View Code

     33.丑数

    题目描述

    把只包含因子2、3和5的数称作丑数(Ugly Number)。
    例如6、8都是丑数,但14不是,因为它包含因子7。 习惯上我们把1当做是第一个丑数。求按从小到大的顺序的第N个丑数。
    思路:
    所有的丑数分为三种类型 2*i,3*i,5*i     其中 i是数组中的元素,一开始只有1
    2*1  3*1  5*1
    2*2  3*1  5*1
    2*2  3*2  5*1
    2*3  3*2  5*1
    2*3  3*2  5*2
    2*4  3*3  5*2
    2*5  3*3  5*2
    2*5  3*4  5*2
    2*6  3*4  5*3
    2*8  3*5  5*3
    2*8  3*6  5*4
    public class Solution {
            public int GetUglyNumber_Solution(int index) {
                if(index<7)
                    return index;
                int[] ret = new int[index];
                ret[0] =1;
                int t2 = 0;int t3= 0; int t5=0;
                for(int i=1;i<index;i++){
                    ret[i]=Math.min(Math.min(ret[t2]*2, ret[t3]*3), ret[t5]*5);
                    if(ret[i] == ret[t2]*2) t2++;
                    if(ret[i] == ret[t3]*3) t3++;
                    if(ret[i] == ret[t5]*5) t5++;
                }
              return   ret[index-1];
         }
     
    }
    View Code

    34.第一次只出现一次的字符

    题目描述

    在一个字符串(1<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置
    import java.util.*;
    public class Solution {
    public int FirstNotRepeatingChar(String str) {
             if(str == null || str.length() == 0)
                   return -1;
            char[] strArray = str.toCharArray();
            LinkedHashMap<Character,Integer> map = new LinkedHashMap<Character,Integer>();
            for(char a:strArray){
                if(map.containsKey(a)){
                    map.put(a, map.get(a)+1);
                }else
                    map.put(a, 1);
            }
            
            for(char key:map.keySet()){
                if(map.get(key)==1)
                    return str.indexOf(key);
            }
         return -1;
        }
    }
    View Code

     35.数组中的逆序对

    题目描述

    在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。
    输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
    归并排序的改进
    public class Solution {
        public int InversePairs(int [] array) {
            if(array==null||array.length==0)
            {
                return 0;
            }
            int[] copy = new int[array.length];
            for(int i=0;i<array.length;i++)
            {
                copy[i] = array[i];
            }
            int count = InversePairsCore(array,copy,0,array.length-1);//数值过大求余
            return count;
             
        }
        private int InversePairsCore(int[] array,int[] copy,int low,int high)
        {
            if(low==high)
            {
                return 0;
            }
            int mid = (low+high)>>1;
            int leftCount = InversePairsCore(array,copy,low,mid)%1000000007;
            int rightCount = InversePairsCore(array,copy,mid+1,high)%1000000007;
            int count = 0;
            int i=mid;
            int j=high;
            int locCopy = high;
            while(i>=low&&j>mid)
            {
                if(array[i]>array[j])
                {
                    count += j-mid;
                    copy[locCopy--] = array[i--];
                    if(count>=1000000007)//数值过大求余
                    {
                        count%=1000000007;
                    }
                }
                else
                {
                    copy[locCopy--] = array[j--];
                }
            }
            for(;i>=low;i--)
            {
                copy[locCopy--]=array[i];
            }
            for(;j>mid;j--)
            {
                copy[locCopy--]=array[j];
            }
            for(int s=low;s<=high;s++)
            {
                array[s] = copy[s];
            }
            return (leftCount+rightCount+count)%1000000007;
        }
    }
    View Code

     没看懂。。。

  • 相关阅读:
    iOS开发UI篇—xib的简单使用
    iOS开发UI篇—字典转模型
    iOS开发UI篇—九宫格坐标计算
    iOS开发UI篇—懒加载
    2020121301-01
    2020120501-01
    2020113001-梦断代码-3
    2020112801-01
    2020112401
    2020112201-1
  • 原文地址:https://www.cnblogs.com/xiangkejin/p/9140134.html
Copyright © 2011-2022 走看看