算法合集(三)

合并两个有序数组

有序数组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;
	} 
	
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值