【问题描述】
给定一个按非递减顺序排序的整数数组 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位置元素进行交换和调整。
因此涉及到数组排序,需考虑指针扫描方向、双指针、三指针、