百度实习生笔试题 数组内Merge

支持英文数字 2012-05-07 12:21:24
题目:

算法和程序设计二:数组al[0...mid-1]和al[mid...num-1]两个部分都已经分别排好序。要求合并使得整个数组al有序。请给出合并merge的代码。要求空间复杂度为O(1)。

我的想法的时间复杂度可能到了o(n^2)了,所以想求教大家的思路。
...全文
849 26 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
26 条回复
切换为时间正序
请发表友善的回复…
发表回复
沧南 2012-10-20
  • 打赏
  • 举报
回复
思路:由于要求空间复杂度为O(1),故不能使用归并排序
1、遍历0~mid -1,将其与a[mid]相比较,若 a[mid] < a[i]; 则将其交换,进入2
2、循环遍历a[mid~length],如果1中交换后a[mid] > a[mid+1] 则进行交换,进行插入排序,将a[mid]插入到正确位置

程序
#include <iostream>

using namespace std;

void InsertSort(int a[], int index, int length){
int temp;
for(int i = index; i<length; i++){
if(a[i] >= a[i+1]){
temp=a[i];
a[i]=a[i+1];
a[i+1]=temp;
}
}
}

void sort(int a[], int mid, int length){
int temp;
for(int i=0; i<=mid-1; i++){
if(a[mid] < a[i]){
temp = a[i];
a[i] = a[mid];
a[mid] = temp;
InsertSort(a, mid, length - 1);
}
}
}

int main()
{
int a[11] ={1, 4, 6, 7, 10, 2, 3, 8, 9, 15, 16};
sort(a,5,11);
for(int i=0;i <11; i++)
cout << a[i]<<" ";
return 0;
}
http://blog.csdn.net/monkeyandy/article/details/7573918
hackbuteer1 2012-05-10
  • 打赏
  • 举报
回复
[Quote=引用 8 楼 的回复:]

To 5楼:
能说下循环右移的思路么??
还有就是在这情况下,希尔排序不知道可以否???
[/Quote]

/*
数组a[begin, mid] 和 a[mid+1, end]是各自有序的,对两个子段进行Merge得到a[begin , end]的有序数组。 要求空间复杂度为O(1)
方案:
1、两个有序段位A和B,A在前,B紧接在A后面,找到A的第一个大于B[0]的数A[i], A[0...i-1]相当于merge后的有效段,在B中找到第一个大于A[i]的数B[j],
对数组A[i...j-1]循环右移j-k位,使A[i...j-1]数组的前面部分有序
2、如此循环merge
3、循环右移通过先子段反转再整体反转的方式进行,复杂度是O(L), L是需要循环移动的子段的长度
*/
#include<iostream>
using namespace std;

void Reverse(int *a , int begin , int end ) //反转
{
for(; begin < end; begin++ , end--)
swap(a[begin] , a[end]);
}
void RotateRight(int *a , int begin , int end , int k) //循环右移
{
int len = end - begin + 1; //数组的长度
k %= len;
Reverse(a , begin , end - k);
Reverse(a , end - k + 1 , end);
Reverse(a , begin , end);
}

// 将有序数组a[begin...mid] 和 a[mid+1...end] 进行归并排序
void Merge(int *a , int begin , int end )
{
int i , j , k;
i = begin;
j = 1 + ((begin + end)>>1); //位运算的优先级比较低,外面需要加一个括号,刚开始忘记添加括号,导致错了很多次
while(i <= end && j <= end && i<j)
{
while(i <= end && a[i] < a[j])
i++;
k = j; //暂时保存指针j的位置
while(j <= end && a[j] < a[i])
j++;
if(j > k)
RotateRight(a , i , j-1 , j-k); //数组a[i...j-1]循环右移j-k次
i += (j-k+1); //第一个指针往后移动,因为循环右移后,数组a[i....i+j-k]是有序的
}
}

void MergeSort(int *a , int begin , int end )
{
if(begin == end)
return ;
int mid = (begin + end)>>1;
MergeSort(a , begin , mid); //递归地将a[begin...mid] 归并为有序的数组
MergeSort(a , mid+1 , end); //递归地将a[mid+1...end] 归并为有序的数组
Merge(a , begin , end); //将有序数组a[begin...mid] 和 a[mid+1...end] 进行归并排序
}

int main(void)
{
int n , i , a[20];
while(cin>>n)
{
for(i = 0 ; i < n ; ++i)
cin>>a[i];
MergeSort(a , 0 , n - 1);
for(i = 0 ; i < n ; ++i)
cout<<a[i]<<" ";
cout<<endl;
}
return 0;
}
  • 打赏
  • 举报
回复
空间换时间,时间换空间,不可能空间少了,时间不增加!
支持英文数字 2012-05-10
  • 打赏
  • 举报
回复
我估计他们的目的想考察一个合并排序(概念上的考察),一个二分查找(最主要的)。
[Quote=引用 22 楼 的回复:]
0(N) 无解.
[/Quote]
iamnobody 2012-05-09
  • 打赏
  • 举报
回复
0(N) 无解.

支持英文数字 2012-05-09
  • 打赏
  • 举报
回复
感谢解答。
[Quote=引用 18 楼 的回复:]
C/C++ code
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int lower_bound(int *arr, int begin, int end, int target) {
while (begin <= end) {
int middle = begin + (end -……
[/Quote]
支持英文数字 2012-05-09
  • 打赏
  • 举报
回复
归并排序的解法是通过空间换时间的方法,使用的时间复杂度是o(n),而现在要求空间复杂度是o(1)
[Quote=引用 14 楼 的回复:]
引用 11 楼 的回复:

引用楼主 的回复:
题目:

算法和程序设计二:数组al[0...mid-1]和al[mid...num-1]两个部分都已经分别排好序。要求合并使得整个数组al有序。请给出合并merge的代码。要求空间复杂度为O(1)。

我的想法的时间复杂度可能到了o(n^2)了,所以想求教大家的思路。

这样的:
从第二个数组的al[mid]开始逐个和al[0.……
[/Quote]
支持英文数字 2012-05-09
  • 打赏
  • 举报
回复
既然说最低是o(n),就应该解释为什么是o(n).现在我想不到o(n)的算法。
[Quote=引用 13 楼 的回复:]
算法复杂度最低应该是O(n)
[/Quote]
qq120848369 2012-05-08
  • 打赏
  • 举报
回复
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int lower_bound(int *arr, int begin, int end, int target) {
while (begin <= end) {
int middle = begin + (end - begin) / 2;
if (target <= arr[middle]) {
end = middle - 1;
} else {
begin = middle + 1;
}
}

return end + 1;
}

int merge(int *arr, int num) {
if (arr == NULL) {
return -1;
}

if (num <= 1) {
return 0;
}

int l_begin = 0, l_end = (num / 2) - 1;
int r_begin = l_end + 1, r_end = num -1;

while (l_begin <= l_end) {
if (arr[l_begin] > arr[r_begin]) {
int temp = arr[l_begin];
arr[l_begin] = arr[r_begin];
/* 慢速版
int ndx = r_begin + 1;
while (ndx <= r_end && temp > arr[ndx]) {
arr[ndx - 1] = arr[ndx];
++ ndx;
}

arr[ndx - 1] = temp;
*/

/* 二分版 */
if (r_end - r_begin + 1 > 1) {
int ndx = lower_bound(arr, r_begin + 1, num - 1, temp);
if (ndx == r_begin + 1) {
arr[r_begin] = temp;
} else {
memmove(arr + r_begin, arr + r_begin + 1,
sizeof(int) * (ndx - r_begin - 1));
arr[ndx - 1] = temp;
}
} else {
arr[r_begin] = temp;
}
}

++ l_begin;
}

return 0;
}

int main(int argc, char* const argv[]) {
int arr[] = {1, 4, 4, 5, 6, 6, 1, 2, 2, 3, 3, 4, 5};
int ret = merge(arr, sizeof(arr) / sizeof(arr[0]));

if (!ret) {
int i = 0;

for ( ; i < sizeof(arr) / sizeof(arr[0]); ++ i ) {
printf("%d ", arr[i]);
}
printf("\n");
}

return 0;
}


写了个二分的和顺序的, 效率不知道了
nice_cxf 2012-05-08
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 的回复:]

算法复杂度最低应该是O(n)
[/Quote]
O(n)大概不行把
每次折半查找,是n/2*(log(n/2)+memmove的时间),最坏情况memmove是从n/2到n,还是o(n^2)
qq120848369 2012-05-08
  • 打赏
  • 举报
回复
[Quote=引用 15 楼 的回复:]

思考了一下过程, 认为当第一段的游标到达中央之后, 第二段仍然有序,而且第一段已经成为最小的n/2个有序序列,接下来应该对第二段调用同样的算法。

复杂度是log(n/2)*n/2 + log(n/4)*n/4 + log(n/8)*n/8 ....
[/Quote]

错,第二段也已经完成了。
qq120848369 2012-05-08
  • 打赏
  • 举报
回复
思考了一下过程, 认为当第一段的游标到达中央之后, 第二段仍然有序,而且第一段已经成为最小的n/2个有序序列,接下来应该对第二段调用同样的算法。

复杂度是log(n/2)*n/2 + log(n/4)*n/4 + log(n/8)*n/8 ....

喜马拉雅星星 2012-05-08
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 的回复:]

引用楼主 的回复:
题目:

算法和程序设计二:数组al[0...mid-1]和al[mid...num-1]两个部分都已经分别排好序。要求合并使得整个数组al有序。请给出合并merge的代码。要求空间复杂度为O(1)。

我的想法的时间复杂度可能到了o(n^2)了,所以想求教大家的思路。

这样的:
从第二个数组的al[mid]开始逐个和al[0...mid-1]比较,比较得……
[/Quote]
这个是并归排序的原理,可以看一下原始的解释去
caoyiwen19830726 2012-05-08
  • 打赏
  • 举报
回复
算法复杂度最低应该是O(n)
qq120848369 2012-05-08
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 的回复:]

很简单,前半段的小那么不用操作,后半段的小,就把后半段和前半段交换,然后后半段做一个插入排序(因为交换前后半段<前半段,所以交换后需向后做插入排序)。
[/Quote]

突然想到,其实插入排序那里是可以二分查找的,之后可以memmove挪内存,应该比逐个字节去扫会高效不少,尤其在数组很长的情况下。

喜马拉雅星星 2012-05-08
  • 打赏
  • 举报
回复
[Quote=引用楼主 的回复:]
题目:

算法和程序设计二:数组al[0...mid-1]和al[mid...num-1]两个部分都已经分别排好序。要求合并使得整个数组al有序。请给出合并merge的代码。要求空间复杂度为O(1)。

我的想法的时间复杂度可能到了o(n^2)了,所以想求教大家的思路。
[/Quote]
这样的:
从第二个数组的al[mid]开始逐个和al[0...mid-1]比较,比较得顺序不是从前至后而是从后至前。如果al[mid]比所比较的前面的数要小则将所比较得数向后移,逐个插入直到后边的数组全部插入位置。这样只需要一个游标,复杂度应该比O(n)小一些。
关键是不从前插,而是从后插。
qq120848369 2012-05-08
  • 打赏
  • 举报
回复
很简单,前半段的小那么不用操作,后半段的小,就把后半段和前半段交换,然后后半段做一个插入排序(因为交换前后半段<前半段,所以交换后需向后做插入排序)。
xxx_abbc_zz 2012-05-08
  • 打赏
  • 举报
回复

void mergeArray(int a[], int first, int mid ,int last)
{
int i, j = mid +1;
int k, flag = 0;

while ( a[j] < a[j-1] && j <= last )
{
k = flag;
for ( i = j-1; i >= k ; i--)
if ( a[i] > a[i+1] )
{
Swap( a[i], a[i+1] );
flag = i;
}
j ++;
}
}
支持英文数字 2012-05-07
  • 打赏
  • 举报
回复
两个游标是肯定的,甚至需要第三个游标。
当后半部分有连续的数据插入到前半部分时,我觉得可能需要将前半部分的数据后移,这个后移时间复杂度就很高了。
[Quote=引用 1 楼 的回复:]

我的想法是:定义两个游标,分别指向两段的未处理的数据,然后根据游标来插入数据,并且将第二段之前的数据进行后移一格。估计时间复杂度也很高。
[/Quote]
W170532934 2012-05-07
  • 打赏
  • 举报
回复
我的想法是:定义两个游标,分别指向两段的未处理的数据,然后根据游标来插入数据,并且将第二段之前的数据进行后移一格。估计时间复杂度也很高。
加载更多回复(6)

65,189

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧