二分查找的二分法和黄金分割点比较

笔记和代码的思路来源:

好大学慕课浙江大学陈越、何钦铭的《数据结构》

讨论3.1 黄金分割查找?

老师参与

在二分查找中,我们是取mid等于left和right的中间值,即用等分的方法进行查找。

那为什么一定要等分呐?能不能进行“黄金分割”?也就是mid=left+0.618(right-left),当然mid要取整数。如果这样查找,时间复杂性是多少?也许你还可以编程做个试验,比较一下二分法和“黄金分割”法的执行效率。

 
证明来自好白的小白。

二分法每次能有100%的概率能只剩50%的数据,每次剩下的期望为50%,即每次除以2。所以时间复杂度是

而黄金分割的话每次都有0.618的概率剩0.618,0.382的概率剩0.382,每次剩下的期望为0.528,即每次除以1.894。所以时间复杂度是

 

虽然两者都是O(logN)类,但是系数不同,黄金分割法所需时间约为二分法的1.085倍(此处没考虑取整……)。

 

假设分割点距左侧的距离除以全长等于p,那么每次剩下的期望为个全长。要让这个期望最小,我们知道p要等于1-p。所以p=0.5。所以二分法分在正中。

 
下面是是自己的代码和测试过程:
 
  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<time.h>
  4 #include<math.h>
  5 
  6 #define MAXSIZE 1000000
  7 
  8 typedef int dataType;
  9 
 10 typedef struct node{
 11     dataType data[MAXSIZE];
 12     int length;
 13 }lis,*pList;
 14 
 15 
 16 pList createEmptyList(){
 17     pList list = (pList)malloc(sizeof(lis));
 18     if(list){
 19         list->length=-1;
 20     }
 21     return list;
 22 }
 23 
 24 int isFull(pList list){
 25     return (list->length==MAXSIZE-1);
 26 }
 27 
 28 int isEmpty(pList list){
 29     return (list->length==-1);
 30 }
 31 
 32 void insert(pList list,dataType element){
 33     if(isFull(list)){
 34         printf("the list has full");
 35         return;
 36     }
 37     list->data[++(list->length)]=element;
 38 
 39 }
 40 
 41 dataType findIndex(pList list,int index){
 42     if(isEmpty(list)){
 43         printf("the list is empty");
 44         return;
 45     }
 46     return list->data[index];
 47 }
 48 
 49 void toString(pList list){
 50     int length=0;
 51     printf("\ntoString:");
 52     while(length<=list->length){
 53         printf("%d ",list->data[length]);
 54         length++;
 55     }
 56 }
 57 
 58 
 59 /*
 60 不使用哨兵是算法就会导致每次循环进行一次i<=list->length的判断,
 61 为了演示方便,我们这里的数组角标从1开始,0角标存放的是数组的长度+1
 62 */
 63 int orderSearch(pList list,int element){
 64     int i;
 65     for(i=0;i<=list->length && list->data[i]!=element;i++);
 66     if(i>list->length){
 67         return -1;
 68     }else{
 69         return i;
 70     }
 71 }
 72 
 73 /*
 74 使用顺序查找,使用哨兵算法,
 75 如果使用哨兵算法,那么数组的第一个元素就只能存放数组的长度
 76 数组的真正的角标是从1开始的。0号角标存放的需要查找的元素element,
 77 当循环退出是,要么是找到了,返回找到元素的角标i
 78 要么是i=0;没找到
 79 */
 80 int orderSearch2(pList list,int element){
 81     int i;
 82     list->data[0]=element;
 83     for(i=list->length;list->data[i]!=element;i--);
 84     return i;//0表示没有找到
 85 }
 86 
 87 /*二分查找,去终点作为分割点*/
 88 int binarySearch(pList list,int element){
 89     int left=1;
 90     int right=list->length;
 91     while(left<=right){
 92         int mid=(right+left)/2;
 93         if(element>list->data[mid]){
 94             left=mid+1;
 95         }else if(element<list->data[mid]){
 96             right = mid - 1;
 97         }else{
 98             return mid;
 99         }
100     }
101     return -1;//没找到,返回-1
102 }
103 
104 /*
105 二分查找,取黄金分割点作为中点
106 */
107 int binarySearch2(pList list,int element){
108     int left=1;
109     int right=list->length;
110     while(left<=right){
111         int mid=left+0.618*(right-left);
112         if(element>list->data[mid]){
113             left=mid+1;
114         }else if(element<list->data[mid]){
115             right = mid - 1;
116         }else{
117             return mid;
118         }
119     }
120     return -1;//没找到,返回-1
121 }
122 
123 
124 void main(){
125     int i=0,j=0;
126     clock_t start,end;
127     double duration;//used to stored top - end
128 
129     int count =10;
130     pList list = createEmptyList();
131 
132     insert(list,MAXSIZE);
133     for(i=1;i<MAXSIZE;i++){
134         insert(list,i);
135     }
136 
137 
138 
139     start=clock();
140     start=clock();
141     for(j=0;j<count;j++){
142         for(i=0;i<MAXSIZE;i++){
143             binarySearch(list,i);//使用中点分割
144         }
145     }
146     end=clock();
147     duration=((double)(end-start))/CLK_TCK/MAXSIZE/count;
148     printf("use middle as the split point:%f\n",duration);
149 
150 
151     start=clock();
152     for(j=0;j<count;j++){
153         for(i=0;i<MAXSIZE;i++){
154             binarySearch2(list,i);//使用黄金分割点分割
155         }
156     }
157     end=clock();
158     duration=((double)(end-start))/CLK_TCK/MAXSIZE/count;
159     printf("use huangJinFenGeDian as the split point:%f\n",duration);
160 
161 
162     //toString(list);
163 
164 /*
165     insert(list,10);
166     for(i=1;i<=10;i++){
167         insert(list,i);
168     }
169     printf("%d ",orderSearch2(list,7));
170     printf("%d ",orderSearch2(list,11));
171     printf("%d ",binarySearch(list,9));
172     printf("%d ",binarySearch(list,11));
173     toString(list);
174 */
175 }
View Code

 

进行累计计时就求平均的思想,但是当count=100时,PC机运算时间太慢了,就没有测试。

下面是count=10的测试结果,发现基本上查不了多少。但是上面的逻辑证明更加准确。

 

 
 
posted @ 2017-04-06 10:56  浪漫逆风  阅读(3641)  评论(0编辑  收藏  举报