筆記和代碼的思路來源:
好大學慕課浙江大學陳越、何欽銘的《數據結構》
討論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 }
進行累計計時就求平均的思想,但是當count=100時,PC機運算時間太慢了,就沒有測試。
下面是count=10的測試結果,發現基本上查不了多少。但是上面的邏輯證明更加准確。