【剑指offer第二版】JAVA刷题总结-ch5

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

思路:count==0,时,val=num[i],count=1;相同count++,不同count--;最后判断是否存在超过一半的数字(传递参数【超过一半的那个数或者最后一个数】)

时间复杂度o(n),空间复杂度O(1)

class Solution {
    public int moreThanHalfNum_Solution(int[] nums) {
        int count=0,val=-1;
        for(int i=0; i<nums.length; i++){
            if(count==0){
                val=nums[i];
                count=1;
            } 
            else{
                if(nums[i]==val) count++;
                else count--;
            }
        }
        if(count>0){
            if(!moreThanHalf(nums, val)){//判断是否存在超过一半的数字
                return 0;
            }
            else
             return val;
        }
        return val;
    }
    public boolean moreThanHalf(int[] nums, int result){
        int count=0;
        for(int i=0; i<nums.length; i++){
            if(nums[i]==result) count++;
        }
        if(count>(nums.length>>1)) return true;
        else return false;
    }
}

40. 最小的k个数

思路:维护容量为k的最大堆。

class Solution {
    public List<Integer> getLeastNumbers_Solution(int [] input, int k) {
        PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>((o1, o2) -> o2 - o1);//大根堆
        if(k>input.length || input.length==0) return new ArrayList<>();
        for(int i=0;i<input.length;i++){
            maxHeap.add(input[i]);
            if(maxHeap.size()>k){
                maxHeap.poll();
            } 
        }
        List<Integer> ans = new ArrayList<Integer>();
        while(!maxHeap.isEmpty()){
            ans.add(maxHeap.poll());//
        }
        Collections.reverse(ans);//翻转链表
        return ans;
    }
}

41.数据流的中位数

思路:用最大堆和最小堆维护中位数左边和中位数右边。

class Solution {
    public PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1,o2)->o2-o1);
    public PriorityQueue<Integer> minHeap = new PriorityQueue<>();
    int index = 0;
    public void insert(Integer num) {
        if((index&1)==0){//第偶数个数,加入右边(最小堆)
            maxHeap.add(num);
            minHeap.add(maxHeap.poll());
        }
        else{
            minHeap.add(num);//第奇数个数,加入左边(最大堆)
            maxHeap.add(minHeap.poll());
        }
        index++;
    }

    public Double getMedian() {
        if(maxHeap.size()-minHeap.size()==1){
            return (double)maxHeap.peek();
        }
        else if(maxHeap.size()-minHeap.size()==-1){
            return (double)minHeap.peek();
        }
        else{
            return ((maxHeap.peek() + minHeap.peek()) / 2.0);
        }
    }
}

42.连续子数组的最大和

思路:dp[i] = dp[i - 1] >= 0 ? dp[i - 1] + nums[i] : nums[i]

class Solution {
    public int maxSubArray(int[] nums) {
        int[] dp = new int[nums.length];
        dp[0] = nums[0];
        int ans = nums[0];
        for(int i=1; i<nums.length; i++){
            if(dp[i-1]<0) dp[i] = nums[i];
            else dp[i] = nums[i]+dp[i-1];
            ans = Math.max(ans, dp[i]);
        }
        return ans;
    }
}

43. 1-n整数中1出现的次数

思路:(1)遍历,查每个数中1的个数,累加。时间复杂度O(nlogn)。(2)从高位到低位查找该位为1时,可选择数字的个数。分为left和right。

class Solution {
    public int numberOf1Between1AndN_Solution(int n) {
        List<Integer> nums = new ArrayList<>();
        while(n!=0){//把每一位放到List里面
            nums.add(n%10);
            n = n/10;
        }
        int ans = 0;
        for(int i=nums.size()-1; i>=0; i--){
            int left=0, right=0, t=1;
            for(int j=nums.size()-1; j>i; j--) left = left*10 + nums.get(j);//左边的数字
            for(int j=i-1; j>=0; j--){//右边的数字
                right = right*10+nums.get(j); 
                t=t*10;
            }
            ans = ans + left*t;//答案加上左边的数组*t
            if(nums.get(i)==1) ans = ans + right+1;//nums[i]==1
            else if(nums.get(i)>1) ans = ans + t;//nums[i]!=1
        }
        return ans;
    }
}

44.数字序列中某一位的数字

思路:先找到多少位,在找到这个数字,最后找到它在这个数字中的位数。

class Solution {
    public int digitAtIndex(int n) {
        if(n==0) return 0; 
        int i=1,s=9,base=1;//i位数有s个,开始的数字是base
        while(n>i*s){
            n -= i*s; 
            i++;
            s*=10;
            base*=10;
        }
        int number = base + (n+i-1)/i-1;//n/i上取整。number为该位所属的数字
        int r;//该位属于数字的第r位
        if(n%i==0) r=i;//余数为0 return i;
        else r=n%i;//不为0 return n%i
        for(int k=0; k<i-r;k++) number/=10;
        return number%10;
    }
}

45.把数组排成最小的数

思路:重载排序。证明排序是有效的(1)反对称性(2)传递性

class Solution {
    public String printMinNumber(int[] nums) {
        if(nums==null) return null;
        String[] numString = new String[nums.length];//先变成字符串数组
        for(int i=0; i<nums.length; i++){
            numString[i] = nums[i]+"";
        }
        Arrays.sort(numString, (s1, s2) -> (s1 + s2).compareTo(s2 + s1));//自定义排序array可以这样Collection不可以
        String ans = "";
        for (String str : numString)
            ans += str;
        return ans;
    }
}

46.把数字翻译成字符串

思路:(1)状态表示(2)状态计算(3)边界定义

class Solution {
    public int getTranslationCount(String s) {
        if(s=="") return 1;
        int[] f = new int[s.length()];//dp问题
        f[0] = 1;//
        for(int i=1; i<s.length(); i++){
            f[i]=f[i-1];//一位
            if(i>1){//解决越界问题
            //两位
                if((s.charAt(i-2)-'0')*10+(s.charAt(i-1)-'0')>=10 && (s.charAt(i-2)-'0')*10+(s.charAt(i-1)-'0')<=25) f[i] += f[i-2];
            }
        }
        return f[s.length()-1];
    }
}

60. 礼物的最大价值

思路:(动态规划) O(n2)
dp[i][j] 代表走到(i,j)这个位置的最大价值
由于只能向下向右走,所以(i,j)这个位置只能从左边和上边过来,所以dp[i][j] = max(dp[i-1][j],dp[i][j-1]) + grid[i][j],即从左边和上边这两个位置选一个较大的加上本位置的价值,即可。

class Solution {
    public int getMaxValue(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        int[][] fn = new int[m+1][n+1];
        for(int i=1; i<m+1; i++){
            for(int j=1; j<n+1; j++){
                fn[i][j] = Math.max(fn[i-1][j], fn[i][j-1]) + grid[i-1][j-1];//注意fn偏移了一位
            }
        }
        return fn[m][n];
    }
}

48.最长不含重复字符的子字符串

思路:

49.丑数

思路:每次计算的结果中,有一部分是小于等于U,另一部分是大于U,那么大于U里面最小的M就是下一个丑数了。选定三个数U2/U3/U5,那么下一个U就=min(2U2,3U3,5*U5)。我们定义U2乘以2后是下一个可能的丑数,U3乘以3后是下一个可能的丑数,U5乘以5后是下一个可能的丑数。所以每次求完丑数,都要将这三个“候选人”更新。

class Solution {
    public int getUglyNumber(int n) {
        if(n<=0) return 0;
        int[] ans = new int[n];
        int i2=0, i3=0, i5=0;
        ans[0]=1;//第一个丑数为1
        for(int i=1;i<n;i++){
            int min = Math.min(ans[i2]*2,Math.min(ans[i3]*3, ans[i5]*5));//找到下一个最小的丑数
            ans[i] = min;//加入数组
            while(ans[i2]*2<=min) i2++;//i2指向下一个可能的下一个丑数(*2比当前丑数大)
            while(ans[i3]*3<=min) i3++;//...
            while(ans[i5]*5<=min) i5++;//...
            
        }
        return ans[n-1];
    }
}

50.第一个只出现一次的字符

50-1 字符串中第一个只出现一次的字符

思路:哈希表,时间复杂度O(n),空间复杂度O(1)。

class Solution {
    public char firstNotRepeatingChar(String s) {
        int[] count = new int[256];
        for(int i=0; i<s.length(); i++){
            count[s.charAt(i)]++;
        }
        for(int i=0; i<s.length(); i++){
            if(count[s.charAt(i)]==1)
                return s.charAt(i);
        }
        return '#';
    }
}

50-2 字符流中第一个只出现一次的字符

思路:哈希表,时间复杂度O(n),空间复杂度O(n)。哈希表存储出现过的字符,队列里存储只出现过1次的字符。

class Solution {    
    Map<Character, Integer> charCount = new HashMap<>();//存储出现过的字符
    Queue<Character> char1 = new LinkedList<>();//存储只出现过一次的字符
    //Insert one char from stringstream   
    public void insert(char ch){
        if(charCount.containsKey(ch)){
            if(char1.contains(ch)) {
                char1.remove(ch);
            }
            return;
        }
        charCount.put(ch, 1);
        char1.offer(ch);
    }
    //return the first appearence once char in current stringstream
    public char firstAppearingOnce(){
        if(char1.isEmpty()==true) return '#';
        return char1.peek();
    }
}

51. 数组中的逆序对

思路:(1)暴力,时间复杂度O(n²);(2)归并排序,时间复杂度O(nlogn),需要辅助空间O(n)。

class Solution {
    int count=0;
    public int inversePairs(int[] nums) {
        int l=0, r=nums.length-1, m=(l+r)>>1;
        int[] tmp = new int[nums.length];
        MergeSort(nums, tmp, l, r);
        return count;
    }
    public void MergeSort(int[] nums, int[] tmp, int l, int r){
        if(l>=r) return;
        int m = (l+r)>>1;
        MergeSort(nums, tmp, l, m);
        MergeSort(nums, tmp, m+1, r);
        int i=l, j=m+1, k=0;
        while(i<=m && j<=r){
            if(nums[i]>nums[j]){
                count = count + m-i+1;
                tmp[k++] = nums[j++];
            }
            else tmp[k++] = nums[i++];
        }
        while(i<=m) tmp[k++] = nums[i++];
        while(j<=r) tmp[k++] = nums[j++];
        k=0;
        for(i=l;i<=r;i++){
            nums[i] = tmp[k];
            k++;
        }
    }
}

52. 两个链表的第一个公共节点

思路:设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。

当访问链表 A 的指针访问到链表尾部时,令它从链表 B 的头部重新开始访问链表 B;同样地,当访问链表 B 的指针访问到链表尾部时,令它从链表 A 的头部重新开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。

class Solution {
    public ListNode findFirstCommonNode(ListNode headA, ListNode headB) {
        if(headA==null || headB==null) return null;
        ListNode p=headA, q=headB;
        while(p!=q){
            if(p!=null) p = p.next;
            else p=headB;
            if(q!=null) q=q.next;
            else q=headA;
        }
        return p;
    }
}
发布了16 篇原创文章 · 获赞 0 · 访问量 3120

猜你喜欢

转载自blog.csdn.net/Calliope1997/article/details/104250686
今日推荐