辟谣!单向链表查找中点【附正确方法】
来源:互联网 发布:ubuntu安装pychom 编辑:程序博客网 时间:2024/05/21 22:54
听说查找中点可以达到O(0.5n)
该方法的实现是:
- 2个指针从链表的头开始。
- 一个指针每步+1,一个指针每步+2
- 然后跑的快的指针到链表尾部的时候,那个慢一点的指针就是中点了
一般的算法是第一次遍历得到长度,第二次遍历取出中点。
然后就有人觉得这个方法比传统的遍历2次更优。 ????
首先O(0.5n)和O(1.5n)的这个说法就有问题。
但今天我只讨论2个方法到底孰优孰劣。
废话不说先上结果:
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。而是在前一个指针遍历的时候保存下来给另一个指针。
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_2
我估算需要(1,1.25)n个get_next()操作。
0 0
- 辟谣!单向链表查找中点【附正确方法】
- 链表的快慢指针:查找中点
- 链表的快慢指针:查找中点
- 单向链表的插入、查找和遍历方法 (c)
- 【LeetCode】Middle of Linked List 查找链表中点
- 查找单向链表的中间元素
- 单向链表的查找问题
- 查找两个单向链表的交汇点
- C++单向链表-查找某个节点
- 单向链表-遍历(查找)
- 单向链表-查找和删除
- 数据结构学习之单向链表[附Java实现代码]
- 查找单链表的中点
- 找到链表的中点
- 单向链表中循环的查找方法总结
- 两种方法实现单向链表的创建、遍历、删除、查找、逆序输出(循环法和递归法)
- 查找单向链表倒数第m个
- 单向链表的操作:创建,删除,插入,销毁,查找
- 在Android中查看和管理sqlite数据库及产生的-journal同名数据库
- Linux 中的零拷贝技术,第 1 部分
- oracle学习 第二章 限制性查询和数据的排序 ——03
- 使用Log4j2打印Mybatis SQL语句以及结果集
- ios开发之Protocol
- 辟谣!单向链表查找中点【附正确方法】
- 使用 VisualVM 进行性能分析及调优
- Linux 中的零拷贝技术,第 2 部分
- FIFO结构与实现
- LeetCode(60)-ZigZag Conversion
- 正则表达式-取最大相同的字符
- TCP和UDP的区别
- Java并发编程:Callable、Future和FutureTask
- iTerm 2 && Oh My Zsh