【算法】(遇到的问题)给定一个数组,找出不在数组中的最小的那个数字
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; } }
相关文章推荐
- 给定一个数组,找出不在数组中的最小的那个数字
- 给定一个数组,找出不在数组中的最小的那个数字
- 给定一个数组,找出不在数组中的最小的那个数字
- 给定一个数组,找出不在数组中的最小的那个数字
- 算法题:从数组找数字(网易2017校园招聘) 2017-10-10 算法爱好者 (点击上方公众号,可快速关注) 给定一个数组,除了一个数出现 1 次之外,其余数都出现 3 次。找出出现一次的数。
- 写一个算法实现在一个整数数组中,找出第二大的那个数字。
- 问题描述如下: 有2.5亿个整数(这2.5亿个整数存储在一个数组里面,至于数组是放在外存还是内存,没有进一步具体说明); 要求找出这2.5亿个数字里面,不重复的数字的个数; 另外,可用的内存限定为600M; 要求算法尽量高效,最优;
- 程序员面试金典——解题总结: 9.18高难度题 18.6设计一个算法,给定10亿数字,找出最小的100万个数字。假定计算机内存足以容纳全部10亿个数字。
- 给定一个数组,按序排列,从数组找出若干个数,使得这若干个数字的和与M最为接近,(背包问题)
- 算法:找出数组中未出现的那个数字
- 算法:找出数组中未出现的那个数字
- 关于面试常见的一个数组中找出出现一次的几个数字的问题
- 一个简单的实现找出数组中一个数字出现次数最多的数字的算法
- 整型数组处理算法(十)给定数组a[n],其中有超过一半的数为一个定值,找出这个数。[2014人人网笔试题]
- 一个简单的算法---实现找出数组中一个数字出现次数最多的数字
- 算法:找出数组中未出现的那个数字
- 每天学习一算法系列(14) (输入一个已经按升序排序过的数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字)
- 整型数组处理算法(九)给定任意一个正整数,求比这个数大且最小的“不重复数”(性能优化)[2014百度笔试题]
- 计数排序——有一个数组,里面是从1到1,000,000的整数,其中有一个数字出现了两次,你怎么找出那个重复的数字?
- 软件开发者面试百问-----有一个数组,里面是从1到1,000,000的整数,其中有一个数字出现了两次,你怎么找出那个重复的数字?