LeetCode专场:专题一 数组排序

有序数组的平方

【问题描述】
给定一个按非递减顺序排序的整数数组 A,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。

示例 1:

输入:[-4,-1,0,3,10]
输出:[0,1,9,16,100]

示例 2:

输入:[-7,-3,2,3,11]
输出:[4,9,9,49,121]

提示:

1 <= A.length <= 10000
-10000 <= A[i] <= 10000
A 已按非递减顺序排序。

【来源】
977. 有序数组的平方

【思路】

首先想到的是找到最后一个负数的下标,以此为分界分别从左和从右扫描比较。

package 数组_排序;

import java.util.Arrays;

/**
* @author JohnnyLin
* @version Creation Time:2020年10月6日 下午9:48:45
* 
*/
public class _977_有序数组的平方 {
	
	/**
	 * @param A
	 * @return
	 * [-7,-3,2,3,11]
	 * 
	 * [7,3,2,3,11]
	 */
	public static int[] sortedSquares(int[] A) {
		if(A==null||A.length==0) return null;
		int i=1,n=A.length;
		int square[]=new int[n];
		if(A[0]<0) {//出现负数才查找最后一个负数的位置 否则正常执行
			while(i<n) {
				if(A[i-1]*A[i]>0) {
					A[i-1]=Math.abs(A[i-1]);
					i++;
				}else {
					A[i-1]=Math.abs(A[i-1]);
					break;
				}
			}
		}
		//System.out.println(Arrays.toString(A));
		//System.out.println(i);
		int p=i-1,k=0;
		while(p>=0&i<n) {
			if(A[p]<=A[i]) {
				square[k++]=A[p]*A[p];
				p--;
			}else {
				square[k++]=A[i]*A[i];
				i++;
			}
			//System.out.println(Arrays.toString(square));
		}
		//-7,-3,2,3,11 
		// 7, 3,2,3,11
		while(p>=0) {
			square[k++]=A[p]*A[p];
			p--;
		}
		while(i<n) {
			square[k++]=A[i]*A[i];
			i++;
		}
		//System.out.println(Arrays.toString(square));
		return square;

	 }

	public static void main(String[] args) {
		//int a[]={-7,-3,2,3,11};
		//int a[]={1,2};
		int a[]={-1,0,0};
		sortedSquares(a);
	}

}

但看了LeetCode上的最优解之后发现没必要,直接两个指针一前一后,比较绝对值的大小,然后直接将较大一方的平方值赋值到新开辟数组的尾部。

public static int[] sortedSquares(int[] A) {
		/*
		 * 数组头尾的元素的绝对值应该是较大的
		 * 头尾指针扫描 比较绝对值大小
		 * 
		 */
		int i=0,j=A.length-1,k=j;
		int res[]=new int[j+1];
		while(i<=j) {
			if(Math.abs(A[i])>=A[j]) {
				res[k--]=A[i]*A[i++];
			}else {
				res[k--]=A[j]*A[j--];
			}
		}
		System.out.println(Arrays.toString(res));
		return res;
	}

合并排序的数组

【题目描述】
给定两个排序后的数组 A 和 B,其中 A 的末端有足够的缓冲空间容纳 B。 编写一个方法,将 B 合并入 A 并排序。

初始化 A 和 B 的元素数量分别为 m 和 n。

示例:

输入:
A = [1,2,3,0,0,0], m = 3
B = [2,5,6], n = 3

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

说明:

A.length == n + m

【来源】

面试题 10.01. 合并排序的数组

package 分治思想及快排双指针分区的应用;

import java.util.Arrays;

/**
* @author JohnnyLin
* @version Creation Time:2020年10月6日 下午3:55:11
*/
public class 合并排序的数组_逆向双指针 {
	//两个指针a,b分别从后往前扫描两个数组 p指针填充A数组
	 public static void merge(int[] A, int m, int[] B, int n) {
		if(m==0&&n!=0){//A中没有元素 B中有元素
			A=Arrays.copyOf(B,n);
			return;
		}
		 if(A==null) {
			 return;
		 }
		 
		 int a=m-1,b=n-1,p=m+n-1;
		 while(a>=0&&b>=0) {//同时不为0
			 A[p--]=A[a]>=B[b]?A[a--]:B[b--];
			// System.out.println(p+" "+a+" "+b);
		 }
		 //a,b
		 while(a>=0)
			 A[p--]=A[a--];
		 while(b>=0)
			 A[p--]=B[b--];
		 System.out.println(Arrays.toString(A));
		 
		 
	   }

	public static void main(String[] args) {

		int nums1[] = {2,0};
		int nums2[] = {1};
		int m = 1, n = 1;
		merge(nums1,  m, nums2,  n) ;
		
		//int A[]={};//A==null false,A.length==0 true
		//int A[]={0};//A==null false,A.length==1 true


	}

}

部分排序

【问题描述】

给定一个整数数组,编写一个函数,找出索引m和n,只要将索引区间[m,n]的元素排好序,整个数组就是有序的。注意:n-m尽量最小,也就是说,找出符合条件的最短序列。函数返回值为[m,n],若不存在这样的m和n(例如整个数组是有序的),请返回[-1,-1]。

示例:

输入: [1,2,4,7,10,11,7,12,6,7,16,18,19]
输出: [3,9]

提示:

0 <= len(array) <= 1000000

【来源】
面试题 16.16. 部分排序

【思路】
本题实际上是找最大逆序区间
r指针从左往右扫描 逆序即左大右小 因此需要记录扫描过的区间的最大值
l指针从右往左扫描 逆序即左大右小 因此需要记录扫描过的区间的最小值
输入: [1,2,4, 7,10,11,7,12,6,7, 16,18,19]
输出: [3,9]
{1,3,5,7,9}

package 数组_排序;
/**
* @author JohnnyLin
* @version Creation Time:2020年10月6日 下午8:26:00.
* 
* https://leetcode-cn.com/problems/sub-sort-lcci/
*/
public class 面试题_16_16_部分排序 {
	/**
	 * @param a
	 * @return
	 */
	public static int[] subSort(int[] a) {
		if(a==null||a.length==0) return new int[]{0,1};
		if(a.length==2) return  (a[0]>a[1])?new int[]{0,1}:null;
		int m=-1,n=-1;
		int r=1,l=a.length-2;
		int max=a[0],min=a[l+1];
		for(;l>=0;r++,l--) {
			if(a[r]<max) {
				n=r;
			}
			if(a[l]>min) {
				m=l;
			}
			//System.out.println(m+" "+n+" "+max+" " +min);
			max=Math.max(max, a[r]);
			min=Math.min(min, a[l]);
			
		}
		return new int[]{m,n};

    }

	public static void main(String[] args) {
		//int a[]= {1,2,4, 7,10,11, 7,12,6,7, 16,18,19};
		//int a[]= {11,93,9};
		int a[]= {1,3,5,3,9};
		int res[]=subSort(a);
		System.out.println(res[0]+" "+res[1]);

	}

}

逆序对个数

颜色分类

[问题描述]

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

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

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

示例:

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

进阶:

一个直观的解决方案是使用计数排序的两趟扫描算法。
首先,迭代计算出0、1 和 2 元素的个数,然后按照0、1、2的排序,重写当前数组。
你能想出一个仅使用常数空间的一趟扫描算法吗?

来源

75. 颜色分类

【思路】

原地排序,计数范围0-2,可以使用计数排序,但是需用到O(n)的辅助数组空间。 若要求只能使用O(1)空间该怎么做呢?

package 数组_排序;

import java.util.Arrays;

/**
* @author JohnnyLin
* @version Creation Time:2020年10月6日 下午5:31:41
* 
* https://leetcode-cn.com/problems/sort-colors/
*
*/


	public class _75_颜色分类_三指针 {
		/**
		 * @param nums
		 * p指针扫描数组nums l和r一开始分别指向头部和尾部
		 * p指针会遇到三种情况:
		 *   遇到1则跳过,
		 *   遇到2则与r指针交换值,同时r--
		 *   遇到0则与l指针交换值,同时l++,p++
		 * 
		 */
		public static void sortColors(int[] nums) {
			if(nums==null||nums.length==0) return;
			int l=0,p=0,r=nums.length-1;
			while(p<=r) {//要等于 否则当nums={0, 1, 2}时排序结果为{1,0, 2}
				if(nums[p]==2) {
					swap(nums,p,r);
					r--;
				}else if(nums[p]==0) {
					swap(nums, p, l);
					l++;
					p++;
				}else {
					p++;
				}
			}
			System.out.println(Arrays.toString(nums));
			
		
		}
		private static void swap(int[] a, int p, int r) {
			int tmp=a[p];
			a[p]=a[r];
			a[r]=tmp;
			
		}
		public static void main(String[] args) {
			//int a[]= {2,0,2,1,1,0};
			//int a[]= {1,0,2,2,2,1,1};
			int a[]= {2,0,1};
			//{};
			sortColors(a);
		
		}

}

最大间距

【问题描述】
给定一个无序的数组,找出数组在排序之后,相邻元素之间最大的差值。

如果数组元素个数小于 2,则返回 0。

示例 1:

输入: [3,6,9,1]
输出: 3
解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。

示例 2:

输入: [10]
输出: 0
解释: 数组元素个数小于 2,因此返回 0。

说明:

你可以假设数组中所有元素都是非负整数,且数值在 32 位有符号整数范围内。
请尝试在线性时间复杂度和空间复杂度的条件下解决此问题。

来源

164. 最大间距

【思路】
第一想法是先排序再扫一遍算最大间距,这样时间复杂度是O(n*logn)。
若考虑非负整数,线性时间复杂度和空间复杂度的条件下该怎么解决呢?一时间毫无思路,看了题解发现用的是桶排序。

在理想情况下,原数据如果是均等间距的,如2,4,6,8那么最大间距也就是每个区间长度,因为每两个数据对区间的贡献值一样,平均下来自然也就是一样的。但在数据分配不平均的情况下,最大间距>每个区间长度。因此最大间距 >= 每个区间长度= (max-min)/(numSize-1) >桶内间距。由于最大间距只可能发生在桶与桶之间,因此只需要使用Bucket类记录桶内最大值和最小值情况即可。

下面以{3,6,9,15,1}对桶排序进行说明。
每个区间长度(bucketSize)为=区间总长度/区间个数,而区间个数为元素个数-1。注意1,0,1,1时,区间分为: [0,1) [1,2),因此最小长度为1,否则后面求桶的个数会出现除数为0的情况。

因此每个区间长度为:(15-1)/4=14/4=3
桶的个数为=区间总长度/每个区间长度+1,为了包括边界值max,桶的个数要比区间数多一个。
因此桶的个数为:14/3+1=5

区间nums[i]
[1,4)1 和3
[4,7)6
[7,10)9
[10,13)
[13,16)15

package 数组_排序;

import java.util.Iterator;

/**
* @author JohnnyLin
* @version Creation Time:2020年10月7日 下午2:42:50
*/

	//由于最大间距只可能发生在桶与桶之间 
	//因此只需要记录桶内最大值和最小值情况即可
class Bucket{

	 int max=Integer.MIN_VALUE;
	 int min=Integer.MAX_VALUE;
}
public class _164_最大间距 {

	public static int maximumGap(int[] nums) {
		 if(nums==null||nums.length<2) return 0;
		 //遍历数组找到最大值和最小值
		 int max=Integer.MIN_VALUE,min=Integer.MAX_VALUE;
		 for(int x:nums) {
			 max=Math.max(max, x);
			 min=Math.min(min, x);
		 }
		 int n=nums.length;
		 //每个区间的长度也就是桶的大小 最小为1
		 int bucketSize=Math.max( (max-min)/(n-1),1);
		 //1,0,1,1算出来的d为0
		 Bucket[] buckets=new Bucket[(max-min)/bucketSize+1];
		 /*将原数据装入桶中(也就是相应区间范围)
		 [1,4.5) [4.5,8) [8,11.5) [11.5,15) [15,18.5)
		  1 3		6			9				15
		  14/4=3.0
		 */
		 System.out.println(max+" "+min+" "+" "+bucketSize);
		 for (int i : nums) {
			int index= (i-min)/bucketSize;
			if(buckets[index]==null)buckets[index]=new Bucket();
			buckets[index].min=Math.min(i, buckets[index].min);
			buckets[index].max=Math.max(i, buckets[index].max);
			//System.out.println(buckets[index].min+" "+buckets[index].max);
		 }
		 int res=0;
		 //后一个桶的最小值与前一个桶的最大值做差 但要注意有的桶可能没有装数据指向为null
		 //所得的最大值即为相邻元素最大间距 
		 int preMax=-1;
		 for(int i=0;i<buckets.length;i++) {
			 if(buckets[i]!=null&&preMax!=-1) {
				 res=Math.max(res, buckets[i].min-preMax);
			 }
			 if(buckets[i]!=null)
				 preMax=buckets[i].max;
		 }
		 return res;
	    }
	public static void main(String[] args) {
		//int a[]= {3,6,9,1};
		//1 3 6 9 15
		//int a[]= {3,6,9,15,1};
		//int a[]= {1,1,1,1};
		int a[]= {1,0,1,1};
		System.out.println(maximumGap(a));

	}

}

数组部分小小结

  • 有序数组的平方,用到了双指针,一前一后扫描并比较绝对值大小,由于这两处地方可能是绝对值较大的位置,因此在存入辅助数组时,有个地方比较奇特,指向辅助数组的指针是从后往前扫的。与合并排序的数组类似。

  • 颜色分类使用到了三指针,p指针扫描数组,l和r分指向头部和尾部,用于与p位置元素进行交换和调整。
    因此涉及到数组排序,需考虑指针扫描方向、双指针、三指针、

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值