数据结构与算法---数组、排序---合并两个有序数组、 颜色分类

本文介绍了一种将两个有序数组合并成一个有序数组的方法,探讨了不同的实现思路,包括使用额外数组的双指针法和直接在原数组上操作的逆向填充法,并分析了各自的时间复杂度。

合并两个有序数组

88. 合并两个有序数组

题意:

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3

输出:[1,2,2,3,5,6]

说明:
初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素


思路1
双指针

给个直观的,设置一个新的数组nums3,长度是nums1的长度 + nums2的长度,然后nums1和nums2遍历比较,将小的放入。如果某一个数组放完之后,还有一个数组没有放完,则将该数组赋值给nums3即可

public static void main(String[] args)
	{
		int[] array1 = {1, 2, 3};
		int[] array2 = {2, 4, 6, 10};

		mergeSortArray(array1, array2);
	}
	
public static void mergeSortArray(int[] array1, int[] array2)
	{
		int li = 0;
		int ri = 0;
		int ai = 0;
		
		int[] array = new int[array1.length + array2.length];
		
		while ((li < array1.length) && (ri < array2.length)) {
			if (array1[li] <= array2[ri]) {
				array[ai++] = array1[li++];
			}else {
				array[ai++] = array2[ri++];
			}
		}
		
		if (li == array1.length) {
			for (int i = ri; i < array2.length; i++) {
				array[ai++] = array2[ri++];
			}
		}
		
		if (ri == array2.length) {
			for (int i = li; i < array1.length; i++) {
				array[ai++] = array1[li++];
			}
		}
		

		for (int i = 0; i < array.length; i++) {
			System.out.print(array[i]+ " ");
		}
	}

打印结果:
1 2 2 3 4 6 10 

其时间复杂度:O(n)

类似写法:
iOS课程观看笔记(十一)—算法:有序数组合并
数据结构与算法—排序—归并排序:归并排序的组合部分

这道题算是做(写)了两遍了,这是第三遍


思路:面试时临时想出的方法

在面试的时候被问到这个题,然后临场想出的解法是这样的:

暴力法
package com.yz01;

public class test4 {
	public static void main(String[] args) {
		int[] array1 = { 1, 2, 3 };
		int[] array2 = { 2, 4, 6, 10 };

		mergeSortArray(array1, array2);
	}

	public static void mergeSortArray(int[] array1, int[] array2) {
		// 边界值
		if (array1 == null || array2 == null)
			return;
		
		//创建一个数组,其大小是两个数组之合
		int[] array3 = new int[array1.length + array2.length];

		//从0开始遍历
		int array2Index = 0;
		int array3Index = 0;

		for (int i = 0; i < array1.length; i++) {
			for (int j = array2Index; j < array2.length; j++) {
				if (array1[i] > array2[j]) {
					array3[array3Index++] = array2[j];
					array2Index = j + 1;
				} else {
					array3[array3Index++] = array1[i];
					break;
				}

			}
		}

		for (int j = array2Index; j < array2.length; j++) {
			array3[array3Index++] = array2[j];
		}
		
		//结果打印
		for (int i = 0; i < array3.length; i++) {
			System.out.print(array3[i] + " ");
		}
	}

}

当时写的时候存在两个错误

  • 一个是array2Index = j + 1,写成了array2Index = j,这样的后果是j被使用了两次

  • 两外一个是:没有写

for (int j = array2Index; j < array2.length; j++) {
	nums3[nums3Index++] = array2[j];
}

这样的后果是,当第一个数组遍历完后,第二个数组还有剩余的时候,第二个数组是进不去的,因此,需要在外面将剩余的第二个数组元素挨个插入到新数组中去

其时间复杂度:O(n2)


但是,如果要求空间复杂度为O(1)呢?
也就是不准建立新的数组

思路2 待验证

那就只能将比较的结果一个一个放入nums1中。
从前一个一个比较?还是从后一个一个比较?

假设是从前往后比,那么,找出的比较大的元素放置在哪?
明确的是不应该放置在0位置上
那就是,如果nums1[i] > nums2[j] ,两者交换位置,i++;
如果nums1[i] <= nums2[j],j++;

貌似也行

如果是从后往前比较呢?

思路3

设置nums1需要比较的位置为nums1Compare
设置nums1需要插入的位置为nums1Insert
设置nums2需要比较的位置为nums2Compare

在这里插入图片描述
如果nums2提前结束,则结束比较,nums1不需要动

在这里插入图片描述
如果上面的nums1提前结束,则将nums2赋值到nums1上即可


package 排序_数组;

public class _88_合并两个有序数组 {
	
	public static void main(String[] args)
	{
		int[] nums1 = {2, 0};
		int[] nums2 = {1};
		merge(nums1, 1, nums2, 1);
		
		for (int i = 0; i < nums1.length; i++) {
			System.out.print(nums1[i] + " ");
		}
	}
	
	public static void merge(int[] nums1, int m, int[] nums2, int n) {
		//从后往前走
		//需要三个指针
		
		//指向要插入的地方
		int nums1Insert = nums1.length - 1;
		//指向要比较的nums1元素
		int nums1Compare = m - 1;
		//指向要比较的nums2元素
		int nums2Compare = n - 1;
		
		//不需要nums1Compare >= 0,因为nums2Compare小于0,说明nums2比较完了,剩余的nums1不用动了
		//而nums1Compare < 0,还得将nums2的数据赋值
		while(nums2Compare >= 0)
		{
			if (nums1Compare < 0 || nums1[nums1Compare] < nums2[nums2Compare]) {
				nums1[nums1Insert] = nums2[nums2Compare];
				nums1Insert--;
				nums2Compare--;
			}else {
				nums1[nums1Insert] = nums1[nums1Compare];
				nums1Insert--;
				nums1Compare--;
			}
		}
    }
}


颜色分类

75.颜色分类
题目:给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。

此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

注意:
不能使用代码库中的排序函数来解决这道题。

示例:

输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]

思路

注意,原地,指的是空间复杂度为O(1)

一般来说,扫描一遍数组就可以解题的,需要借助多个指针。
双指针、三指针

由于题目只有0、1、2三种数字类型
那么,顺序扫描:
如果是0,则放在最前面;
如果是2,则放在最后面;
如果是1,则不动;

假设有一个扫描指针,currentPoint
如果扫描的是0,则需要放在前面的位置,也就是还需要有一个指针指向0需要插入的位置leftPoint
如果扫描的是2,则需要放在后面的位置,也就是还需要有一个指针指向2需要插入的位置rightPoint

如果是0,则currentPoint指向的数值 与 leftPoint指向的数值交换,然后currentPoint++、leftPoint++;
如果是2,则currentPoint指向的数值 与 rightPoint指向的数值交换,rightPoint–,然后再次对currentPoint指向的值进行判断
如果是1,则currentPoint++;

什么时候截止呢?
rightPoint < currentPoint的时候

class Solution {
    public void sortColors(int[] nums) {

        /**
        如果是0,则currentPoint指向的数值 与 leftPoint指向的数值交换,然后currentPoint++、leftPoint++;
        如果是2,则currentPoint指向的数值 与 rightPoint指向的数值交换,rightPoint--,然后再次对currentPoint指向的值进行判断
        如果是1,则currentPoint++;
        */
        int leftPoint = 0;
        int rightPoint = nums.length - 1;
        int currentPoint = 0;
        while(rightPoint >= currentPoint){
            if(nums[currentPoint] == 0){
                int temp = nums[currentPoint];
                nums[currentPoint] = nums[leftPoint];
                nums[leftPoint] = temp;
                currentPoint++;
                leftPoint++;
            }else if(nums[currentPoint] == 2){
                int temp = nums[currentPoint];
                nums[currentPoint] = nums[rightPoint];
                nums[rightPoint] = temp;
                rightPoint--;
            }else if(nums[currentPoint] == 1){
                currentPoint++;
            }
        }
    }
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值