912. 排序数组
给你一个整数数组 nums,请你将该数组升序排列。
示例 1:
输入:nums = [5,2,3,1]
输出:[1,2,3,5]
示例 2:
输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]
提示:
1 <= nums.length <= 5 * 104
-5 * 104 <= nums[i] <= 5 * 104
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sort-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
分析
用该题练习一下归并排序。
归并排序是利用分治的思路,对各个子问题进行操作再合并,首先对数组中每两个数进行排序合并,然后对每四个进行排序合并,然后对八个。。。直到整个数组全部合并完,时间复杂度是O(nlogn),空间复杂度是O(n)。
src是存放合并前数字的数组,dst是保存合并之后数组的数组。在操作的过程中需要双指针,并且需要注意进行完一轮操作后,不能直接让src等于dst,因为这样会使src与dst的地址相同,在下一轮操作时会同时操作两个数组,通过创建一个新数组作为中间数组可以避免此问题。
方法一:迭代
方法二:递归
题解(Java)
方法一:
class Solution {
public int[] sortArray(int[] nums) {
int length = nums.length;
int[] src = nums, dst = new int[length];
//每次对2、4、8...个数进行操作,最后合并
for (int seg = 1; seg < length; seg += seg) {
//用start代表每次操作的数组索引的开头,步长是seg * 2
for (int start = 0; start < length; start += seg * 2) {
//固定一下上轮操作过的两个相邻子数组的 后一数组的开头位置,需要注意不要超过数组长度
int mid = Math.min(start + seg, length);
//固定一下上轮操作过的两个相邻子数组的 末尾位置 + 1,需要注意不要超过数组长度
int end = Math.min(start + seg * 2, length);
//i作为前一数组的指针,j作为后一数组的指针,k作为dst数组的索引用来存储数据
int i = start, j = mid, k = start;
//循环退出条件是i到达mid处即遍历完前一数组、j到达end处即遍历完后一数组
while (i < mid || j < end) {
//添加后一数组数据的条件是j == end || (i < mid && src[i] < src[j])
if (j == end || (i < mid && src[i] < src[j])) {
dst[k++] = src[i++];
} else {
dst[k++] = src[j++];
}
}
}
//不能直接src = dst,因为这样的话src跟dst的地址值是一样的,在操作时都会改变
int[] temp = src;
src = dst;
dst = temp;
}
return src;
}
}
方法二:
class Solution {
public int[] sortArray(int[] nums) {
int[] dst = new int[nums.length];
dst = Arrays.copyOf(nums, nums.length);
mergeSort(nums, dst, 0, nums.length);
return dst;
}
private void mergeSort(int[] src, int[] dst, int start, int end) {
if (start + 1 >= end) return;
int mid = (start + end) / 2;
mergeSort(dst, src, start, mid);
mergeSort(dst, src, mid, end);
int i = start, j = mid, k = start;
while (i < mid || j < end) {
if (j == end || (i < mid && src[i] < src[j])) {
dst[k++] = src[i++];
} else {
dst[k++] = src[j++];
}
}
}
}