合并两个有序数组 LeetCode 88
题目链接:[LeetCode 88 - 简单]
思路
首先,该题需要时间复杂度O(m+n),因此该题不能使用两个for循环来遍历。 其次该题没有返回值,最终的结果是放到num1中。
由提示已知num1.length==m+n,因此不用担心num1的长度问题。
一开始的设想是进行一个从0到m+n-1的循环,通过两个下标,比较num1和num2中的大小,将小的那个值放入到num1中。但是由此衍生出来一个问题,如果num2中存在比num1小的数,会导致num1中的数被覆盖。
由此可以采用两种方式进行解决:①创建一个新的数组,其大小与num1一致,最后将结果返回到num1②将转化为从后往前遍历
双指针:
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
if(nums1.length==0||nums2.length==0)return;
int p1=m-1,p2=n-1;
for(int i=m+n-1;i>=0;i--){
if(p1>=0&&p2>=0){
if(nums1[p1]>nums2[p2]){
nums1[i]=nums1[p1--];
}else{
nums1[i]=nums2[p2--];
}
}else{
if(p1>=0){
nums1[i]=nums1[p1--];
}else if(p2>=0){
nums1[i]=nums2[p2--];
}
}
}
}
}
方法①的代码:
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int p1 = 0, p2 = 0;
int[] sorted = new int[m + n];
int cur;
while (p1 < m || p2 < n) {
if (p1 == m) {
cur = nums2[p2++];
} else if (p2 == n) {
cur = nums1[p1++];
} else if (nums1[p1] < nums2[p2]) {
cur = nums1[p1++];
} else {
cur = nums2[p2++];
}
sorted[p1 + p2 - 1] = cur;
}
for (int i = 0; i != m + n; ++i) {
nums1[i] = sorted[i];
}
}
}
还有一种很简单的做法,但是不能体现其代码的精髓。 做的时候想到过,但是没有用此方法。
class Solution {
public void merge(int[] nums1, int m, int[] nums2, int n) {
for (int i = 0; i != n; ++i) {
nums1[m + i] = nums2[i];
}
Arrays.sort(nums1);
}
}
排序数组 LeetCode 912
题目链接:[LeetCode 812 - 中等]
思路
首先使用了冒泡,在10/21时超时。 选择了选择排序,在13/21时超时。 因此选择快排or堆排序进行解题:
首先介绍堆排序
维护堆的性质:
主要就是判断根节点的值是否比左右子树小,如果小于左右子树,进行交换,并且继续递归判断其下一个节点的左右子树大小问题。
/**
*@param arr 存储堆的数组
*@param n 数组的长度
*@param i 待维护节点的下标
**/
private void heapify(int[] arr,int n,int i){
int largest = i;
int lson = 2 * i + 1;
int rson = 2 * i + 2;
if(lson < n && arr[largest] < arr[lson])
largest = lson;
if(rson < n && arr[largest] < arr[rson])
largest = rson;
if(largest!=i){
int temp = arr[largest];
arr[largest] = arr[i];
arr[i] = temp;
heapify(arr,n,largest);
}
}
堆排序分为两步走:①建堆②排序
//建堆
for(int i = n / 2 - 1; i >= 0 ; i--){
heapify(arr,n,i);
}
//排序
for(int i = n - 1 ; i > 0 ; i--){
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
heapify(arr,i,0);
}
堆排序:
class Solution {
public int[] sortArray(int[] nums) {
return sort(nums);
}
private int[] sort(int[] arr){
int n = arr.length;
//建堆
for(int i = n / 2 - 1; i >=0 ; i--){
heapify(arr,n,i);
}
//排序
for(int i = n - 1 ; i > 0 ; i--){
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
heapify(arr,i,0);
}
return arr;
}
private void heapify(int[] arr,int n,int i){
int largest = i;
int lson = i * 2 + 1;
int rson = i * 2 + 2;
if(lson < n && arr[largest] < arr[lson])
largest = lson;
if(rson < n && arr[largest] < arr[rson])
largest = rson;
if(largest!=i){
int temp = arr[i];
arr[i] = arr[largest];
arr[largest]=temp;
heapify(arr,n,largest);
}
}
}
第二天使用了快排,当出现很多个相同的数的时候会发生超时的情况。
主要是由于在快速排序中,选取基准值的选择对算法的效率有很大影响。如果每次选取的基准值都是数组的最左边或最右边的元素,且数组近乎有序时,可能会导致快速排序的时间复杂度退化为 O(n^2)。为了避免这种情况,可以随机选择数组中的一个元素作为基准值。 以下就是采用了随机基准值之后的代码,有一部分还没有能够完全理解。
class Solution {
public int[] sortArray(int[] nums) {
randomizedQuicksort(nums, 0, nums.length - 1);
return nums;
}
public void randomizedQuicksort(int[] nums, int l, int r) {
if (l < r) {
int pos = randomizedPartition(nums, l, r);
randomizedQuicksort(nums, l, pos - 1);
randomizedQuicksort(nums, pos + 1, r);
}
}
public int randomizedPartition(int[] nums, int l, int r) {
int i = new Random().nextInt(r - l + 1) + l; // 随机选一个作为我们的主元
swap(nums, r, i);
return partition(nums, l, r);
}
public int partition(int[] nums, int l, int r) {
int pivot = nums[r];
int i = l - 1;
for (int j = l; j <= r - 1; ++j) {
if (nums[j] <= pivot) {
i = i + 1;
swap(nums, i, j);
}
}
swap(nums, i + 1, r);
return i + 1;
}
private void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}