977. 有序数组的平方
题目:https://leetcode-cn.com/problems/squares-of-a-sorted-array/
答案参考:https://programmercarl.com/0977.%E6%9C%89%E5%BA%8F%E6%95%B0%E7%BB%84%E7%9A%84%E5%B9%B3%E6%96%B9.html#%E5%8F%8C%E6%8C%87%E9%92%88%E6%B3%95
暴力排序
最直观的相反,莫过于:每个数平方之后,排个序,美滋滋,代码如下:
C++ class Solution { public: vector<int> sortedSquares(vector<int>& A) { for (int i = 0; i < A.size(); i++) { A[i] *= A[i]; } sort(A.begin(), A.end()); // 快速排序 return A; } };
这个时间复杂度是 O(n + nlogn), 可以说是O(nlogn)的时间复杂度,但为了和下面双指针法算法时间复杂度有鲜明对比,记为 O(n + nlogn)。
双指针法
数组其实是有序的, 只不过负数平方之后可能成为最大数了。
那么数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间。
此时可以考虑双指针法了,i指向起始位置,j指向终止位置。
定义一个新数组result,和A数组一样的大小,让k指向result数组终止位置。
如果A[i] * A[i] < A[j] * A[j]
那么result[k--] = A[j] * A[j];
。
如果A[i] * A[i] >= A[j] * A[j]
那么result[k--] = A[i] * A[i];
。
C++ class Solution { public: vector<int> sortedSquares(vector<int>& A) { int k = A.size() - 1; vector<int> result(A.size(), 0); for (int i = 0, j = A.size() - 1; i <= j;) { // 注意这里要i <= j,因为最后要处理两个元素 if (A[i] * A[i] < A[j] * A[j]) { result[k--] = A[j] * A[j]; j--; } else { result[k--] = A[i] * A[i]; i++; } } return result; } };
此时的时间复杂度为O(n),相对于暴力排序的解法O(n + nlogn)还是提升不少的。
这里还是说一下,大家不必太在意leetcode上执行用时,打败多少多少用户,这个就是一个玩具,非常不准确。
做题的时候自己能分析出来时间复杂度就可以了,至于leetcode上执行用时,大概看一下就行,只要达到最优的时间复杂度就可以了,
一样的代码多提交几次可能就击败百分之百了.....
JAVA class Solution { public int[] sortedSquares(int[] nums) { int right = nums.length - 1; int left = 0; int[] result = new int[nums.length]; int index = result.length - 1; while (left <= right) { if (nums[left] * nums[left] > nums[right] * nums[right]) { result[index--] = nums[left] * nums[left]; ++left; } else { result[index--] = nums[right] * nums[right]; --right; } } return result; } }
官方:https://leetcode-cn.com/problems/squares-of-a-sorted-array/solution/you-xu-shu-zu-de-ping-fang-by-leetcode-solution/
方法一:直接排序
最简单的方法就是将数组nums 中的数平方后直接排序。
C++版 class Solution { public: vector<int> sortedSquares(vector<int>& nums) { vector<int> ans;//创建一个vector容器 for (int num: nums) { ans.push_back(num * num);//将结果推入vector的末尾 } sort(ans.begin(), ans.end());//排序 return ans; } }; JAVA版 class Solution { public int[] sortedSquares(int[] nums) { int[] ans = new int[nums.length]; for (int i = 0; i < nums.length; ++i) { ans[i] = nums[i] * nums[i]; } Arrays.sort(ans); return ans; } }
c++标准库里的排序函数的使用方法: https://www.cnblogs.com/epep/p/10959627.html
I)Sort函数包含在头文件为#include<algorithm>的c++标准库中,调用标准库里的排序方法可以不必知道其内部是如何实现的,只要出现我们想要的结果即可!
II)Sort函数有三个参数:
(1)第一个是要排序的数组的起始地址。
(2)第二个是结束的地址(最后一位要排序的地址)
(3)第三个参数是排序的方法,可以是从大到小也可是从小到大,还可以不写第三个参数,此时默认的排序方法是从小到大排序。
Sort函数使用模板: sort(start,end,排序方法)
从大到小的排序: 需要加入一个比较函数 complare(),此函数的实现过程是这样的
bool complare(int a,int b) { return a>b; }
结构体中的排序:
struct node { int a; int b; double c; }
有一个node类型的数组node arr[100],想对它进行排序:先按a值升序排列,如果a值相同,再按b值降序排列,如果b还相同,就按c降序排列。就可以写这样一个比较函数:
bool cmp(node x,node y) { if(x.a!=y.a) return x.a<y.a; if(x.b!=y.b) return x.b>y.b; return x.c>y.c; }
JAVA中的排序函数:https://www.cnblogs.com/zz22--/p/10705970.html
Arrays中的sort()方法主要是针对各种数据类型(基本数据类型和引用对象类型)的数组元素排序。
.......
关于引用对象类型数组的排序sort()方法要用到接口Comparator<T>,对其排序内部的比较函数compare()进行重写,以便于我们按照我们的排序要求对引用对象数组极性排序,默认是升序排序,但可以自己自定义成降序排序。关于Comparator<T>的介绍见官方文档
package test; import java.util.Arrays; import java.util.Comparator; //用Arrays中的sort()对数组进行排序 public class test4 { //年龄比较器 Comparator<student> comparatorAge =new Comparator <student>(){ public int compare(student p1,student p2){ if (p1.getAge()>p2.getAge()) return 1; else if (p1.getAge()<p2.getAge()) return -1; else return 0; } }; //成绩比较器 Comparator<student> comparatorGrade =new Comparator <student>(){ public int compare(student p1,student p2){ if (p1.getGrade()>p2.getGrade()) return 1; else if (p1.getGrade()<p2.getGrade()) return -1; else return 0; } }; public student [] ageSort(student[] s){ Arrays.sort(s,comparatorAge); return s; } public student [] gradeSort(student[] s){ Arrays.sort(s,comparatorGrade); return s; } public static void main(String[] args) { test4 tt=new test4(); student p1 = new student() ; p1.setAge( 10 ); p1.setName( "p1" ); p1.setGrade( 98 ); student p2 = new student() ; p2.setAge( 30 ); p2.setName( "p2" ); p2.setGrade( 70 ); student p3 = new student() ; p3.setAge( 20 ); p3.setName( "p3" ); p3.setGrade( 83 ); student p4 = new student() ; p4.setAge( 15 ); p4.setName( "p4" ); p4.setGrade( 80 ); student [] list = {p1,p2,p3,p4} ; student []agePrint= tt.ageSort(list); for (student ss : agePrint) { System.out.println("student age sort ," + ss.getName() + " " + ss.getAge() +" " +ss.getGrade()); }//按年龄排序 student []gradePrint= tt.gradeSort(list); for (student ss : gradePrint) { System.out.println("student grade sort ," + ss.getName() + " " + ss.getAge() +" " +ss.getGrade()); }//按成绩排序 } } //创建一个类型,用于比较的引用对象类型 class student{ private String name; private int age; private float grade; public void setName(String name){ this.name=name; } public void setAge(int age) { this.age = age; } public void setGrade(float grade) { this.grade = grade; } public String getName() { return name; } public int getAge() { return age; } public float getGrade() { return grade; } }
方法二:双指针
方法一没有利用「数组 nums 已经按照升序排序」这个条件。显然,如果数组nums 中的所有数都是非负数,那么将每个数平方后,数组仍然保持升序;如果数组nums 中的所有数都是负数,那么将每个数平方后,数组会保持降序。
这样一来,如果我们能够找到数组 nums 中负数与非负数的分界线,那么就可以用类似「归并排序」的方法了。具体地,我们设 neg 为数组nums 中负数与非负数的分界线,也就是说,nums[0] 到 nums[neg] 均为负数,而 nums[neg+1] 到 nums[n−1] 均为非负数。当我们将数组 nums 中的数平方后,那么 nums[0] 到 nums[neg] 单调递减,]nums[neg+1] 到 nums[n−1] 单调递增。
由于我们得到了两个已经有序的子数组,因此就可以使用归并的方法进行排序了。具体地,使用两个指针分别指向位置 neg 和neg+1,每次比较两个指针对应的数,选择较小的那个放入答案并移动指针。当某一指针移至边界时,将另一指针还未遍历到的数依次放入答案。
方法三:双指针(和代码随想录里面的一样)
同样地,我们可以使用两个指针分别指向位置 0 和 n−1,每次比较两个指针对应的数,选择较大的那个逆序放入答案并移动指针。这种方法无需处理某一指针移动至边界的情况,读者可以仔细思考其精髓所在。