您的位置:首页 > 其它

【算法】(遇到的问题)给定一个数组,找出不在数组中的最小的那个数字

2017-06-12 16:04 288 查看

算法题:找出不在数组中的最小的那个数字所遇到的问题

算法题找出不在数组中的最小的那个数字所遇到的问题
一切的源头

初次探索

二次探索

三次探索

最终探索

主要代码

一切的源头

一切都源于一个题目:给定一个数组,找出不在数组中的最小的那个数字。这是原文链接

同学问我,说这篇文章里的第5种解法,他实现了可是却得不到正确的结果,于是我去阅读了该文章,这篇文章的思路真的很巧妙,文章也写的很明白,然后我也去试着实现了第5种算法,过程中也遇到了很多问题,不过最后也都解决了,在这里做一下记录。

初次探索

首先要注意两点:

1. 数组的下标是从1开始的

2. 数组中没有重复的元素

先描述一下原文作者的思路:

首先取1~n的中间值m=(1+n)/2,然后利用快拍中的partition函数的思想将整个数组分成2部分。

A1中都是小于或等于m的数字,A2中都是大于m的数字。

如果A1的长度小于m,那么说明缺少的数字应该出现在A1中,否则在A2中,具体推导过程作者说的很详细,这里就不在赘述了。

以下是作者的代码:

MIN-AVAILABLE-NUM(A, low, up)
  if(low == up) return low
  m = (low + up) / 2
  split = partition(A, low, up, m)
  if a[split] == m
     then return MIN-AVAILABLE-NUM(A, low, split)
else return MIN-AVAILABLE-NUM(A, split+1, up)


这个思路是没有问题的,但是都存在一点小问题,一会在讨论,因为当时我看后面有一个非递归实现,所以就去实现非递归实现的代码。以下就是非递归实现的代码:

MIN-AVAILABLE-NUM(A, low, up)
  while low != up
    m = (low + up) / 2
    split = partition(low, up, m)
    if A[split] == m
      then low = split + 1
    else up = split - 1
   return low


然后我用C语言实现了这个算法,一样得不出结果。

开始的时候一直觉得自己的程序写错了,检查了很多遍没有发现问题,和作者的思路一样,很气很奇怪啊。

后来发现了partition函数写的有问题,经过修改后,终于得到了正确的结果。当我感觉如释重负的时候,换了一个例子,然后得到了错误的结果。然后又尝试了一个例子还是错误的,所以我认为自己的程序有了问题,第一个例子能得出正常的结果纯属巧合。

二次探索

于是我再次修改了程序,使得第二和第三个例子都得到了正确答案,我以为终于解决了,但是事情并不是这么简单,当我再次尝试第一个例子的时候,又无法得到正确的答案,于是我决定去debug一下,看看到底是怎么回事,经过一番努力我发现了问题所在,其实这一切都是巧合。

作者的思路很好,但是有些细节没有注意到。接下来举个简单的例子来说明问题的所在:

假设有两个数组1,2,3,4和数组1,2,4,5,下面是模拟算法的计算过程:



通过结果我们发现,这个算法并不能得到正确的结果。

再举一个简单的例子说明这个问题,假设有一个很长的数组,它缺少的数字是下图红色的3,那么最终的结果只可能有两种情况:



这个怎么来证明呢,我们可以假设数组是有序的,那么这个算法的过程就和二分查找类似,由于使用的是/2,而且和数组长度的奇偶性相关,所以会存在两种情况:

1. 指向最终结果之前的一个数

2. 指向最终的结果

至于为什么不会指向最终结果的后一个数,这还是由于/2,整数除法的原因。比如,10/2=5,11/2=5,整数的除法会吧小数部分直接忽略掉,也叫向下取整,所以最终不可能指向最终结果的后一个数的。

三次探索

所以我再次修正了算法,果然得到了正确的结果,当我以为这次是终于解决了,然后又找了一组测试数据,并没有得到正确的结果。这次我debug了一下,很快发现了错误。再举一个简单的例子说明这个问题:



对于数组1 2 4 5 6 很明显得到了一个错误的答案。这是因为第一次split之后,split进行了减一,刚好跳过了最终结果。这个错误完全是我的原因,因为我没有看作者的递归实现,他递归是正确的,但是非递归实现的时候,当最终结果出现在数组的前半部分时,也就是需要修改up的值时,对split进行了减一,就是因为这个减一,所以会跳过正确的结果。

最终探索

最后修正了我的程序,这次是真的解决了吧?

我目前是没有遇到其他问题,如果有人能看到这篇文章,然后发现我的程序还有问题的话一定要告诉我:事情并不是这么简单,还没有结束呢!

主要代码

以下就是我的程序主要代码:

自己测试的时候一定要注意:

1. 数组的下标是从1开始的,也就是说数组的0号位置不要存放有效数据

2. 数组中没有重复的元素

//两个数字交换
void swap(int *a, int *b){
int temp = *a;
*a = *b;
*b = temp;
}

//分组函数
int partition(int data[], int length, int start, int end, int key){
if(length<=0||start<0||end>=length) return -1;
int index;
int small = start-1;
for(index=start; index<=end; ++index){
if(data[index]<=key){
++small;
if(small!=index){
swap(&data[index], &data[small]);
}
}
}
return small;
}

//主要函数
int solution(int A[], int low, int up){
while(low < up){//注意:主要修改,原来的条件会出现死循环
int m = (low + up)/2;
int split = partition(A, N, low, up, m);
if (split == m) { //这里不能使用A[split]
low = split +1;
}else {
up = split;
}
}
if(low==A[low]){
return low+1;
}
if(A[low]>low){
return low;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法
相关文章推荐