两个int数组拼接c语言,88.合并两个有序数组(LeetCode )——C语言

合并两个有序数组,根据题目的条件不同,细节处理也不同。我想先单纯的讨论下合并两个有序数组,不做时间及空间复杂度的限制。

只合并两个有序数组这里,只合并两个有序数组,不是完全在LeetCode88题的条件上解题,我只是想挖掘多一点的思路。在这里我只合并两个有序数组为一个有序数组,没有其他条件限制,后面将讨论LeetCode88题的解法。高手可略过。

有两种思路,一种是先合并数组,而不管它是否有序,然后将合并之后的数组排序。另一种思路是,在合并数组的过程中,对元素进行排序,合并结束,排序也结束,这种思路借鉴了归并排序的归并过程,即它用到了数组有序这个条件。

一、先将两个数组合并,然后再排序。

开辟一个新数组来存储这两个数组中的元素,需要O(m+n)的空间复杂度。将两个数组合并,此处不考虑这两个数组有序,因此合并数组时间花费为O(m+n),然后排序数组(各种排序都可),这里考虑想要较好的时间复杂度,因此用快速排序,时间复杂度为O((m+n)lg(m+n)),综合起来,最终的时间复杂度为O((m+n)lg(m+n)),空间复杂度O(m+n)。#include

#include

void exchange(int* x, int* y)

{

int temp = *x;

*x = *y;

*y = temp;

}

void mergeArr(int *arr, int len, int *arr1, int len1, int *arr2, int len2)

{

for (int i = 0; i < len; i++)

{

if (i < len1)

{

arr[i] = arr1[i];

}

else

{

arr[i] = arr2[i - len1];

}

}

}

// 快速排序是一种原址排序,不需要分配新的数组空间

// 只需要分配常量空间来存储主元以及用于数组元素交换

// start和end为子数组下标的起始位置与终止位置

// 快速排序的分割数组步骤,这是快速排序的核心

int partition(int *arr, int start, int end)

{

// 获取主元

int key = arr[end];

// left不存在,故将i取值为start-1

int i = start - 1;

for (int j = start; j < end; j++)

{

// 如果arr[j]小于主元,则将i递增,那么左边小于主元的数组的

// 长度增加了1,同时将arr[i]与arr[j]交换。

// 因为在i递增之后,指向的是一个大于主元的值

// 而arr[j]则是一个小于主元的值,因此交换他们的值,交换完成

// 则i及i之前的元素都是小于主元的值,i+1到j之间则是大于主元的值

// j到end-1之间是不确定大小的值,end即主元

if (arr[j] <= key)

{

i = i + 1;

exchange(arr + i, arr + j);

}

}

exchange(arr + i + 1, arr + end);

return i + 1;

}

// 快排也用到了分治的思想,即将大问题分割为性质相同的小问题,

// 小问题继续分割为更小的问题,一直到不能再分割,

// 然后将小问题的解合并起来,就获得最终的结果

// 因此,快排是先分割,然后在子数组上递归调用自己

void quickSort(int *arr, int start, int end)

{

if (start < end)

{

int mid = partition(arr, start, end);

quickSort(arr, start, mid - 1);

quickSort(arr, mid + 1, end);

}

}

void mergeArrSorted(int *arr, int len, int *arr1, int len1, int *arr2, int len2)

{

mergeArr(arr, len, arr1, len1, arr2, len2);

printf("merge Array: %d\n", len);

quickSort(arr, 0, len - 1);

}

int main (void) {

int arr1[] = {1, 9, 3, 4, 79, 27};

int arr2[] = {2, 56, 35, 7, 19};

int len1 = sizeof(arr1) / sizeof(*arr1);

int len2 = sizeof(arr2) / sizeof(*arr2);

int len = len1 + len2;

printf("length %d length\n", len);

int* arr = (int*)malloc(len * sizeof(int));

mergeArrSorted(arr, len, arr1, len1, arr2, len2);

for (int k = 0; k < len; k++)

{

printf("%d ", arr[k]);

}

printf("\n");

}在把数组传递为函数的实参时,也要显式的传递函数长度。因为函数sizeof在编译时求值,传递给函数的实参所指向的数组长度确定,但是函数的形参获取到的数组的长度在编译时是不确定的,因此在函数内部使用sizeof获取数组长度会出现不确定的值。

因此,在C语言中,传递数组的同时,也要显式传递数组的长度。二、开辟一个新的数组arr,用来存储这两个有序数组中的元素。这里用到归并排序的merge方法,即将两个指针指向两个数组头部,然后比较指针所指的元素,将较小的元素保存到arr数组,并将其指针后移。重复上面的步骤,直到两个数组遍历完全。这种方法,需要循环m+n次,因此时间复杂度为O(m+n),但是因为需要开辟额外的空间来存储这两个数组的元素,因此空间复杂度为O(m+n)。void mergeArrSorted(int* arr, int len, int *arr1, int len1, int *arr2, int len2)

{

int index1 = 0;

int index2 = 0;

for (int k = 0; k < len; k++)

{

// 如果是其他语言,可以在两个数组末尾各放一个Infiniti值,

// 这样判断比较到末尾时,刚好循环len次,下面的代码也可精简为

// if (arr1[index1] <= arr2[index2])

// {

// arr[k] = arr1[index1];

// index1++;

// }

// else

// {

// arr[k] = arr2[index2];

// index2++;

// }

// 因为这样就少了数组越界的判断

// 下面的代码的三个外层判断,就是为了防止数组访问越界

if (index1 < len1 && index1 < len2)

{

if (arr1[index1] <= arr2[index2])

{

arr[k] = arr1[index1];

index1++;

}

else

{

arr[k] = arr2[index2];

index2++;

}

}

else if (index1 < len1 && index1 >= len2)

{

arr[k] = arr1[index1];

index1++;

}

else if (index1 >= len1 && index1 >= len2)

{

arr[k] = arr2[index2];

index2++;

}

}

}

下面是测试代码:#include

#include

void mergeArrSorted(int* arr, int len, int *arr1, int len1, int *arr2, int len2)

{

int index1 = 0;

int index2 = 0;

for (int k = 0; k < len; k++)

{

if (index1 < len1 && index1 < len2)

{

if (arr1[index1] <= arr2[index2])

{

arr[k] = arr1[index1];

index1++;

}

else

{

arr[k] = arr2[index2];

index2++;

}

}

else if (index1 < len1 && index1 >= len2)

{

arr[k] = arr1[index1];

index1++;

}

else if (index1 >= len1 && index1 >= len2)

{

arr[k] = arr2[index2];

index2++;

}

}

}

int main (void) {

int arr1[] = {1, 3, 4, 5, 8};

int arr2[] = {2, 7, 10, 18, 21};

int len1 = sizeof(arr1) / sizeof(int);

int len2 = sizeof(arr2) / sizeof(int);

int len = len1 + len2;

int* arr = (int*)malloc(len * sizeof(int));

mergeArrSorted(arr, len, arr1, len1, arr2, len2);

for (int i = 0; i < len; i++) {

printf("%d ", arr[i]);

}

printf("\n");

}

LeetCode原题:

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

条件说明:初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。

你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

读完题目可以知道,题目要求将子数组nums2合并到nums1,因为nums1有足够的空间能够容纳两个数组的所有元素。跟上面合并数组比起来,有空间的要求,就是我们必须把num2合并到num1,此外区别不大。因此,上面讲过的两种方法都可以实现数组合并。

方法一:

不需要额外的空间保存数组,只需将num2保存到num1后面,然后对nums1进行快速排序即可。因此,时间复杂度不变,还是O((m+n)lg(m+n)),即快排的时间复杂度。空间复杂度为O(1),因为不需要开辟额外的空间。

方法二:

因为需要将nums1作为输出数组,因此,要先将nums1的元素存储到一个新的数组,然后使用双指针法,比较两个数组中的元素大小,并依次将其保存到nums1。因此需要额外开辟m的空间来存储nums1中的元素。因此空间复杂度O(m),时间复杂度不变,为O(m+n)。

方法三:

可以看到,第一种方法空间复杂度很优秀,但是时间复杂度逊色于第二种方法。我们需要找出时间复杂度如方法二一般优秀,空间复杂度如方法一一般优秀的算法,那就是双指针法--从后往前遍历。

双指针从后往前遍历,将较大的值存储到nums1尾部,如此循环,直到所有元素都有序存储到nums1中。时间复杂度O(m+n),空间复杂度O(1)。void merge(int *nums1, int nums1Size, int m, int *nums2, int nums2Size, int n)

{

int len = m + n;

m = m - 1;

n = n - 1;

for (int i = len - 1; i >= 0; i--)

{

if (n >= 0 && m >= 0)

{

if (nums2[n] > nums1[m])

{

nums1[i] = nums2[n];

n--;

}

else

{

nums1[i] = nums1[m];

m--;

}

}

else if (n >= 0 && m < 0)

{

nums1[i] = nums2[n];

n--;

}

else if (n < 0 && m >= 0)

{

nums1[i] = nums1[m];

m--;

}

}

}bVcLZcx

下面是测试代码,有兴趣的可以拿去编译运行一下:#include

#include

void merge(int *nums1, int nums1Size, int m, int *nums2, int nums2Size, int n)

{

int len = m + n;

m = m - 1;

n = n - 1;

for (int i = len - 1; i >= 0; i--)

{

if (n >= 0 && m >= 0)

{

if (nums2[n] > nums1[m])

{

nums1[i] = nums2[n];

n--;

}

else

{

nums1[i] = nums1[m];

m--;

}

}

else if (n >= 0 && m < 0)

{

nums1[i] = nums2[n];

n--;

}

else if (n < 0 && m >= 0)

{

nums1[i] = nums1[m];

m--;

}

}

}

int main (void) {

int arr1[10] = {1, 2, 3, 8, 20};

int arr2[] = {6, 9, 10};

int len1 = sizeof(arr1) / sizeof(*arr1);

int len2 = sizeof(arr2) / sizeof(*arr2);

int m = 5;

int n = len2;

int len = m + n;

merge(arr1, len1, m, arr2, len2, n);

for (int i = 0; i < len; i++)

{

printf("%d ", arr1[i]);

}

printf("\n");

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值