該方法的實現是:ios
通常的算法是第一次遍歷獲得長度,第二次遍歷取出中點。
而後就有人以爲這個方法比傳統的遍歷2次更優。 ????
首先O(0.5n)和O(1.5n)的這個說法就有問題。
但今天我只討論2個方法到底孰優孰劣。
廢話不說先上結果:c++
0.00027776 s : find_mid 傳統方法
0.000276798 s : find_mid_1
2.81981 s : find_mid 提升指針取得下一個元素的代價
2.81391 s : find_mid_1
#include <iostream> #include <thread> #include <sys/timeb.h> #include <time.h> #include <windows.h> using namespace std; class list { private: list *next = nullptr; public: int m_d; list* add(list* p) { next = p; return p; } list* get_next() { Sleep(delay); return next; } static int delay; }; int list::delay = 0; list* find_mid(list* ptr) { int len = 0; list *p_start = ptr; while (true) { if (!p_start) break; len++; p_start = p_start->get_next(); } len /= 2; p_start = ptr; while (len--) { p_start = p_start->get_next(); } return p_start; } list* find_mid_1(list* ptr) { list *p_s1 = ptr, *p_s2 = ptr; while (p_s2=p_s2->get_next()) { p_s2 = p_s2->get_next(); if (!p_s2) break; p_s1 = p_s1->get_next(); } return p_s1; } #define cal_time(fun,ptr) {\ QueryPerformanceCounter(&startCount);\ fun(ptr);\ QueryPerformanceCounter(&endCount);\ double elapsed = (double)(endCount.QuadPart - startCount.QuadPart) / freq.QuadPart;\ cout << elapsed<<" s : "<< #fun << endl;} int main() { int c = 1*1000; list * start = new list,*p; p = start; while (c--) { p = p->add(new list); p->m_d = c; } LARGE_INTEGER startCount; LARGE_INTEGER endCount; LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); _ASSERT(find_mid_1(start) == find_mid(start)); cal_time(find_mid, start); cal_time(find_mid_1, start); list::delay = 1; cal_time(find_mid, start); cal_time(find_mid_1, start); return 0; }
其實這2種方法都是O(n),使用大O表示法不夠精確,這種簡單的算法直接數一下next的調用次數就能夠知道都是1.5*len個get_next().find_mid_1 惟一的優點就是少用了一個 int len。因此性能提高實在少的可憐。算法
思路:其實也是用到了2個指針,其中一個走過的路程是另外一個的1/2。但千萬不要使用一個+1另外一個+2。而是在前一個指針遍歷的時候保存下來給另外一個指針。windows
list* find_mid_2(list* ptr) { list *p_s1 = ptr, *p_s2 = ptr, *tmp= ptr; int d = 1,t=1; int i = 0; while (true) { tmp = p_s2; for (int c=0; i < d; i++,c++) { p_s2 = p_s2->get_next(); if (!p_s2) { int len = c / 2; while (len--) { p_s1 = p_s1->get_next(); } return p_s1; } } d *= 2; p_s1 = tmp; } return p_s1; }
來看效果吧性能
最好狀況
0.608294 s : find_mid
0.612391 s : find_mid_1
0.404288 s : find_mid_2最差狀況
0.604105 s : find_mid
0.600299 s : find_mid_1
0.502341 s : find_mid_2spa
我估算須要(1,1.25)n個get_next()操做。指針