第一题—两个数组的交集||
方法一:
用HashMap统计元素个数,然后对比个数大小,选择小的放入新数组
/* 方法一:较差的一种方法,时间复杂度为O(m+n); */ public static int[] intersect(int[] nums1, int[] nums2) { Map<Integer, Integer> map1 = new HashMap<>(); //统计nums1的数据 for (int num : nums1) { //如果存在值就+1 if (map1.containsKey(num)) { map1.replace(num, map1.get(num) + 1); } else { map1.put(num, 1); } } int[] fin = new int[Math.min(nums1.length, nums2.length)];//长度为短数组长度 int i = 0; //统计nums2的数据 for (int num : nums2) { if (map1.containsKey(num) && map1.get(num) != 0) { fin[i++] = num; map1.replace(num, map1.get(num) - 1); } } return Arrays.copyOfRange(fin, 0, i); }
方法二:
1.先使用快速排序算法进行排序,然后使用双指针进行遍历。
时间复杂度为O(nlogn),空间复杂度:O(min(m,n))
public static int[] intersect(int[] nums1, int[] nums2) { if (nums1.length > nums2.length) { return intersect(nums2, nums1);//将nums1作为数组较短的那一个 } Arrays.sort(nums1);//排序 Arrays.sort(nums2); int p = 0, d = 0, index = 0;//P:nums1的指针,d:nums的指针,index:result的下标 int[] result = new int[nums1.length]; while (p < nums1.length && d < nums2.length) { if (nums1[p] == nums2[d]) {//如果当前两个数相等就放进result中,指针后移 result[index++] = nums1[p]; d++; p++; } else if (nums1[p] > nums2[d]) { //如果p指向的数大于d指向的数,则让d向后移动 d++; } else if (nums1[p] < nums2[d]) { //如果p指向的数小于d指向的数,则让p向后移动 p++; } } return Arrays.copyOfRange(result, 0, index); }
进阶问题
- 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
如果已经排好序传入参数,则删去排序代码,采用双指针,时间复杂度降低为O(n)
- 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
采用Hash计数,时空间复杂度会低一些。
如果 nums2 的元素存储在磁盘上,内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
自我理解:
内存是有限的,无法有效的对较长数组进行排序,采用方法一(hash计数)更合适,方法一只需要在map中读取一部分数据,符合题目要求。LeetCode网友观点:
1.内存十分小,不足以将数组全部载入内存
2.哈希表十分耗费空间
3.选用空间复杂度最小的算法,即方法二。 解法:
1.改造方法二
2.常见排序算是都是用于数组内部属于内部排序,如果涉及磁盘属于外部排序
3.归并排序是天然适合外部排序的算法,可以将分割后的子数组写到单个文件中,归并时将小文件合并为更大的文件。
4.当两个数组均排序完成生成两个大文件后,即可使用双指针遍历两个文件,如此可以使空间复杂度最低。
代码为:
public static int[] intersect(int[] nums1, int[] nums2) {
> if (nums1.length > nums2.length) {
> return intersect(nums2, nums1);//将nums1作为数组较短的那一个
> }
> sort(nums1);//归并排序,在下方
> sort(nums2);
> int p = 0, d = 0, index = 0;//P:nums1的指针,d:nums的指针,index:result的下标
> int[] result = new int[nums1.length];
> while (p < nums1.length && d < nums2.length) {
> if (nums1[p] == nums2[d]) {//如果当前两个数相等就放进result中,指针后移
> result[index++] = nums1[p];
> d++;
> p++;
> } else if (nums1[p] > nums2[d]) {
> //如果p指向的数大于d指向的数,则让d向后移动
> d++;
> } else if (nums1[p] < nums2[d]) {
> //如果p指向的数小于d指向的数,则让p向后移动
> p++;
> }
>
> }
> return Arrays.copyOfRange(result, 0, index); }
归并排序
归并排序算法转载于:https://www.cnblogs.com/chengxiao/p/6194356.html
public static void sort(int[] arr) {
int[] temp = new int[arr.length];//在排序前,先建好一个长度等于原数组长度的临时数组,避免递归中频繁开辟空间
sort(arr, 0, arr.length - 1, temp);
}
private static void sort(int[] arr, int left, int right, int[] temp) {
if (left < right) {
int mid = (left + right) / 2; //寻找中位值
sort(arr, left, mid, temp); //对数组左边进行归并排序
sort(arr, mid + 1, right, temp);//对数组右部进行归并排序
merge(arr, left, mid, right, temp);//将两个有序的子数组合并
}
}
private static void merge(int[] arr, int left, int mid, int right, int[] temp) {
int i = left;//左序列起始指针
int j = mid + 1;//右序列起始指针
int t = 0;//临时数组temp的指针
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[t++] = arr[i++];
} else {
temp[t++] = arr[j++];
}
}
while (i <= mid) {//将左边剩余元素填充进temp
temp[t++] = arr[i++];
}
while (j <= right) {//将右序列剩余元素填充进temp中
temp[t++] = arr[j++];
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while (left <= right) {
arr[left++] = temp[t++];
}
}
买卖股票的最佳时机
假定每一天都是最低价格,寻找到历史最低价格min,在min后面的每一天都判定一下当日价格是否低于min,不是的话就计算利润最大值并前面的利润最大作比较。
时间复杂度是O(1)
public static int maxProfit(int[] prices) {
int max = 0;
int min = Integer.MAX_VALUE;
for (int price : prices) {
if (price < min) {
min = price;//寻找历史最低点
} else if (price - min > max) {
max = price - min;//在可卖出的天数中,比较收益,选择最大的那一个。
}
}
return max;
}
min = price;//寻找历史最低点
} else if (price - min > max) {
max = price - min;//在可卖出的天数中,比较收益,选择最大的那一个。
}
}
return max;
}