合并两个有序数组
有序数组nums1和nums2,将nums2合并到nums1中,使得nums1为有序数组。
实现
具体实现思路:
(1)merege()直接调用API,先将nums2的数值复制在nums1后面,之后直接采用sort()进行排序。直接采用排序算法时间复杂度为O(n);
(2)双指针算法。merge2()先将nums1的数值复制到num中,p1指向num,p2指向nums2,p指向nums1。之后开始进行对比,如果num[p1]比nums[p2]的值小的话,则将num[p1]的值赋值给nums1,同时将p1和p后移,否则将nums[p2]的值赋值给nums1,同时将p2和p后移。如果p1先到末尾,则直接将nums2的剩余元素全部复制到nums1中即可,如果p2先到末尾,将num的剩余元素复制到nums1中即可。采用这种方法会增大空间复杂度,为O(m+n),时间复杂度为O(m+n);
(3)在第二种算法的基础上,merge3()和merge4()避免了使用额外的空间,直接将p1指向nums1的末尾,p2指向nums2的末尾,p指向m+n-1。当p1和p2都没有到数组头时,如果nums1[p1]的值小于nums2[p2],那么将nums2[p2]的值赋值给nums1[p],同时将p和p2指针进行前移,否则的话将nums1[p1]的值赋值给nums1[p],同时将p和p1指针前移。在这种情况下,只需要看看nums2是否有剩余数值,将其复制到nums1中即可。
package test;
import java.util.Arrays;
public class MergeSortArray {
public static void main(String[] args) {
int[] nums1 = new int[]{1,3,5,7,9,0,0,0,0};
int[] nums2 = new int[]{2,4,6,8};
//System.out.println(Arrays.toString(merge(nums1, 5, nums2, 4)));
System.out.println(Arrays.toString(merge2(nums1, 5, nums2, 4)));
}
//直接调用API实现 排序算法时间复杂度O(n*logn)
public static int[] merge(int[] nums1, int m, int[] nums2, int n) {
System.arraycopy(nums2, 0, nums1, m, n);
Arrays.sort(nums1);
return nums1;
}
//双指针 对比元素大小直接插入新数组中 时间复杂度O(m+n) 空间复杂度增加
public static int[] merge2(int[] nums1, int m, int[] nums2, int n) {
int[] num = new int[m];
System.arraycopy(nums1, 0, num, 0, m);
int p1 = 0; //指向num
int p2 = 0; //指向nums2
int p = 0; //指向nums1
while(p1 < m && p2 < n) {
nums1[p++] = num[p1] < nums2[p2] ? num[p1++]:nums2[p2++];
}
if(p1 < m) {
System.arraycopy(num, p1, nums1, p1+p2, m+n-p1-p2);
}
if(p2 < n) {
System.arraycopy(nums2, p2, nums1, p1+p2, m+n-p1-p2);
}
return nums1;
}
//不使用额外的空间
public static int[] merge3(int[] nums1, int m, int[] nums2, int n) {
int p1 = m - 1;
int p2 = n - 1;
int p = m + n - 1;
while(p1 >= 0 && p2 >= 0) {
nums1[p--] = nums1[p1] < nums2[p2] ? nums2[p2--]:nums1[p1--];
}
System.arraycopy(nums2, 0, nums1, 0, p2+1);
return nums1;
}
//test
public static int[] merge4(int[] nums1, int m, int[] nums2, int n) {
int i = m - 1, j = n - 1;
int p = m + n - 1;
while(i >= 0 && j >= 0) {
if(nums1[i] > nums2[j]) {
nums1[p] = nums1[i];
p--;i--;
}else {
nums1[p] = nums2[j];
p--;j--;
}
}
System.arraycopy(nums2, 0, nums1, 0, j+1);
return nums1;
}
}
拓展
如果数据本身的存储结构是链表(有序),那么同样采用双指针算法。
思路:i指向nums1的链表头部,j指向nums2的链表头部,之后对nums1[i].val和nums2[j].val进行对比,如果nums2[j].val小的话,i++,继续进行对比,当nums2[j].val比nums1[i].val大时,则找到了插入的位置,将其插入进行即可。同样的,如果i或者j到达链表末尾,则将剩余元素全部链接到nums1即可。
package test;
public class MergeSortList {
static class ListNode{
int val;
ListNode next;
public ListNode(int val, ListNode next) {
this.val = val;
this.next = next;
}
}
public static void main(String[] args) {
//l1
ListNode node5 = new ListNode(66,null);
ListNode node4 = new ListNode(43,node5);
ListNode node3 = new ListNode(33,node4);
ListNode node2 = new ListNode(23,node3);
ListNode node1 = new ListNode(13,node2);
//l2
ListNode node10 = new ListNode(26,null);
ListNode node9 = new ListNode(23,node10);
ListNode node8 = new ListNode(20,node9);
ListNode node7 = new ListNode(18,node8);
ListNode node6 = new ListNode(14,node7);
//System.out.println("test");
System.out.println(merge(node1,node6));
//System.out.println("hello");
}
public static ListNode merge(ListNode l1, ListNode l2) {
ListNode l3 = null;
while(l1 != null && l2 != null) {
if(l1.val < l2.val) {
//保存下l1之前的节点
l3 = l1;
l1 = l1.next;
}else if(l1.val >= l2.val){
//插入到l1前面
ListNode l4 = l2.next;
l3.next = l2;
l2.next = l1;
l3 = l2;
l2 = l4;
}
}
if(l1 == null){
l3.next = l2;
}
return l1;
}
}
上面代码存在一些问题,就是当l2的首节点val比l1小时,此时不能插入而导致bug,之后进行改进。
子数组最大平均数
给一个整数数组,找出平均数最大且长度为k的下标连续的子数组,并输出该最大平均数。
实现
(1)暴力实现。通过for循环找出所有长度为k的连续子数组,求其平均值进行比较,输出最大平均值。下面的代码在for循环内还是做了一定的优化,减少了i比较的次数。
(2)滑动窗口。两个指针i和j分别指向头和尾,相距为k。每次移动时,减去i所指向的值,加上j所指向的值,求出maxAvg。
package test;
public class MaxAvg {
public static void main(String[] args) {
int[] nums = new int[] {1,12,-5,-6,50,3};
System.out.println(maxAvg(nums, 4));
System.out.println(maxAvg2(nums, 4));
}
//暴力解法
public static double maxAvg(int [] nums, int k) {
double max = 0;
// 长度小于k则返回0
if(nums.length < k) {
return 0;
}
// 从0下标开始的k位平均值
for(int i = 0; i <= nums.length - k; i++) {
double sum = 0;
for(int j = i; j < i + k ;j++) {
sum += nums[j];
}
if(sum > max) {
max = sum;
}
}
return max/k;
}
//滑动窗口
public static double maxAvg2(int [] nums, int k) {
double sum = 0;
// 长度小于k则返回0
if(nums.length < k) {
return 0;
}
//求出第一个窗口的值
for(int i = 0; i < k; i++) {
sum += nums[i];
}
double max = sum;
//进行移动
for(int i = k; i < nums.length; i++) {
sum = sum - nums[i-k] + nums[i];
max = Math.max(sum,max);
}
return max/k;
}
}