LeetCode专场:专题一 数组排序

    科技2024-12-27  42

    文章目录

    有序数组的平方合并排序的数组部分排序逆序对个数颜色分类最大间距数组部分小小结

    有序数组的平方

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

    Processed: 0.012, SQL: 8