时间效率与空间效率的平衡
- 丑数
- 第一个只出现一次的字符
- 数组中的逆序对
- 两个链表的第一个公共节点
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;
}
}