《剑指Offer》笔记5.3

时间效率与空间效率的平衡

  1. 丑数
  2. 第一个只出现一次的字符
  3. 数组中的逆序对
  4. 两个链表的第一个公共节点

49:丑数

题目:我们把只包含因子2、3和5的数称作丑数(Ugly Number)。求按从小到大的顺序的第1500个丑数。例如,6、8都是丑数,但 14 不是,因为它包含因子 7。习惯上我们把 1 当作第一个丑数。
思路:新的丑数肯定是前面某个丑数乘以2、3或5的结果。动态规划,dp[i] = min(dp[i2]*2, dp[i3]*3, dp[i5]*5)

class Solution {
    public int nthUglyNumber(int n) {
        int a = 0, b = 0, c = 0;
        int[] dp = new int[n];
        dp[0] = 1;
        for(int i = 1; i < n; i++) {
            int n2 = dp[a] * 2, n3 = dp[b] * 3, n5 = dp[c] * 5;
            dp[i] = Math.min(Math.min(n2, n3), n5);
            if(dp[i] == n2) a++;
            if(dp[i] == n3) b++;
            if(dp[i] == n5) c++;
        }
        return dp[n - 1];
    }
}

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

题目:字符串中第一个只出现一次的字符。
思路:遍历字符串 s ,使用哈希表统计“各字符数量是否 > 1;再遍历字符串 s ,在哈希表中找到首个“数量为 1 的字符”,并返回。

class Solution {
    public char firstUniqChar(String s) {
        HashMap<Character, Boolean> dic = new HashMap<>();
        for(int i=0;i<s.length();i++){
            dic.put(s.charAt(i), !dic.containsKey(s.charAt(i)));
        }
        for(int i=0;i<s.length();i++){
            if(dic.get(s.charAt(i))) return s.charAt(i);
        }
        return ' ';
    }
}

51:数组中的逆序对

题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。例如,在数组{7,5,6,4} 中,一共存在 5 个逆序对,分别是(7,6)、(7,5)、(7,4)、(6,4)、(6,5)。
思路:归并排序,在归并的过程中,右边的值较小时,count += 左边剩余数

class Solution {
    public int reversePairs(int[] nums) {
        if(nums.length < 2 || nums == null) return 0;
        int[] copy = nums.clone();
        int[] temp = new int[nums.length];
        return mergeAndCount(copy, 0, nums.length - 1, temp);
    }
    private int mergeAndCount(int[] nums, int left, int right, int[] temp){
        if(left == right) return 0;
        int mid = left + (right - left) / 2;
        int leftCount = mergeAndCount(nums, left, mid, temp);
        int rightCount = mergeAndCount(nums, mid+1, right, temp);
        
        if(nums[mid] < nums[mid+1]) {
            return leftCount + rightCount;
        }
        
        int crossCount = mergeAndCountHelper(nums, left, mid, right, temp);
        return leftCount + rightCount + crossCount;
    }
    private int mergeAndCountHelper(int[] nums, int left, int mid, int right, int[] temp){
        for (int i = left; i <= right; i++) {
            temp[i] = nums[i];
        }
        int i = left;
        int j = mid + 1;
        int count = 0;
        for (int k = left; k <= right; k++) {
            if (i == mid + 1) {
                nums[k] = temp[j];
                j++;
            } else if (j == right + 1) {
                nums[k] = temp[i];
                i++;
            } else if (temp[i] <= temp[j]) {
                nums[k] = temp[i];
                i++;
            } else {
                nums[k] = temp[j];
                j++;
                count += (mid - i + 1);
            }
        }
        return count;
    }
}

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

题目:输入两个链表,招呼它们的第一个公共节点。
思路:使用两个指针 nodeA,nodeB 分别指向两个链表 headA,headB 的头结点,然后同时分别逐结点遍历,当 nodeA 到达链表 headA 的末尾时,重新定位到链表 headB 的头结点;当 nodeB 到达链表 headB 的末尾时,重新定位到链表 headA 的头结点。这样,当它们相遇时,所指向的结点就是第一个公共结点。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode nodeA = headA;
        ListNode nodeB = headB;
        while(nodeA != nodeB){
            nodeA = nodeA==null?headB:nodeA.next;
            nodeB = nodeB==null?headA:nodeB.next;
        }
        return nodeA;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值