查找算法

LeetCode 5192. 查找集群内的「关键连接」

匿名 (未验证) 提交于 2019-12-03 00:06:01
1. 问题 力扣数据中心有 n 台服务器,分别按从 0 到 n-1 的方式进行了编号。 它们之间以「服务器到服务器」点对点的形式相互连接组成了一个内部集群,其中连接 connections 是无向的。 从形式上讲, connections[i] = [a, b] 表示服务器 a 和 b 之间形成连接。任何服务器都可以直接或者间接地通过网络到达任何其他服务器。 「关键连接」是在该集群中的重要连接,也就是说,假如我们将它移除,便会导致某些服务器无法访问其他服务器。 请你以任意顺序返回该集群内的所有 「关键连接」。 示例 1: 输入:n = 4, connections = [[0,1],[1,2],[2,0],[1,3]] 输出:[[1,3]] 解释:[[3,1]] 也是正确的。 原题链接 ; 2. Tarjan算法 Tarjan 算法是在一个图中查找强连通分量的算法。强连通分量是指,每个顶点皆可由该图上的边抵达其他的每一个点的有向图。 算法的基本思想: 任选一个节点开始进行深度优先搜索(若深度优先搜索结束后仍有未访问的节点,则从中任选一点再次进行)。搜索过程中已访问的节点不再访问。 强连通分量的根: 指深度优先搜索是强连通分量重首个被访问的节点。 为找到根节点,我们给每个节点 v 一个深度优先搜索标号 v.index ,表示第几个被访问的节点。此外,每个节点有一个值 v

时间和空间复杂度

匿名 (未验证) 提交于 2019-12-02 23:59:01
复杂度分析是整个算法学习的精髓,只要掌握了它,数据结构和算法的内容基本上就掌握了一半了。 数据结构和算法解决是 “如何让计算机更快时间、更省空间的解决问题”。 因此需从执行时间和占用空间两个维度来评估数据结构和算法的性能。 分别用时间复杂度和空间复杂度两个概念来描述性能问题,二者统称为复杂度。 复杂度描述的是算法执行时间(或占用空间)与数据规模的增长关系。 和性能测试相比,复杂度分析有不依赖执行环境、成本低、效率高、易操作、指导性强的特点。 掌握复杂度分析,将能编写出性能更优的代码,有利于降低系统开发和维护成本。 算法的执行时间与每行代码的执行次数成正比,用 T(n) = O(f(n)) 表示,其中 T(n) 表示算法执行总时间,f(n) 表示每行代码执行总次数,而 n 往往表示数据的规模。这就是大 O 时间复杂度表示法。 1)定义 算法的时间复杂度,也就是算法的时间量度。 大 O 时间复杂度表示法 实际上并不具体表示代码真正的执行时间,而是表示 代码执行时间随数据规模增长的变化趋势 ,所以也叫 渐进时间复杂度 ,简称 时间复杂度 (asymptotic time complexity)。 例子1: function aFun() { console.log("Hello, World!"); // 需要执行 1 次 return 0; // 需要执行 1 次 }

skiplist 跳跃表

匿名 (未验证) 提交于 2019-12-02 23:57:01
什么是跳跃表?   SkipList在leveldb、redis以及lucence中都广为使用,是比较高效的数据结构。由于它的代码以及原理实现的简单性,更为人们所接受。我们首先看看SkipList的定义,为什么叫跳跃表? 跳跃表的理解  这里元素不多,体现不出优势,如果元素足够多,这种索引结构就能体现出优势来了。 璺宠〃  其中 -1 表示 INT_MIN, 链表的最小值,1 表示 INT_MAX,链表的最大值。  跳表具有如下性质:   (1) 由很多层结构组成   (2) 每一层都是一个有序的链表   (3) 最底层(Level 1)的链表包含所有元素   (4) 如果一个元素出现在 Level i 的链表中,则它在 Level i 之下的链表也都会出现。   (5) 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。  例子:查找元素 117   (1) 比较 21, 比 21 大,往后面找   (2) 比较 37, 比 37大,比链表最大值小,从 37 的下面一层开始找   (3) 比较 71, 比 71 大,比链表最大值小,从 71 的下面一层开始找   (4) 比较 85, 比 85 大,从后面找   (5) 比较 117, 等于 117, 找到了节点。 具体的搜索算法如下: /* 如果存在 x, 返回 x 所在的节点, * 否则返回 x

元素查找(移动有序数组)

匿名 (未验证) 提交于 2019-12-02 23:57:01
题目描述 有一个排过序的数组,包含n个整数,但是这个数组向左进行了一定长度的移位,例如,原数组为[1,2,3,4,5,6],向左移位5个位置即变成了[6,1,2,3,4,5],现在对于移位后的数组,需要查找某个元素的位置。请设计一个复杂度为log级别的算法完成这个任务。 给定一个int数组 A ,为移位后的数组,同时给定数组大小 n 和需要查找的元素的值 x ,请返回x的位置(位置从零开始)。保证数组中元素互异。 测试样例: [ 6 , 1 , 2 , 3 , 4 , 5 ], 6 , 6 返回: 0 思路: 还是二分查找没变。变得是每次调整end 和start 的时候,需要把可能的乱序情况考虑进去。 当 A[mid] < x 的时候, 乱序是X 在数组的前半段,因为数组中大的都被移到的前面。 那么一定需要条件 X > A[end] 并且 A[mid] < A[Start]。 当A[mid] > x 的时候, 乱序是X 在数组后面。也就是 小数在数组的最后面。 那么一定有 A[mid] > A[start] && x < A[start] 其他的情况就是正常的二分查找。 class Finder { public : int findElement ( vector <int> A , int n , int x ) { int left = 0 , right = n - 1 ;

查找算法二:哈希查找(散列查找)

匿名 (未验证) 提交于 2019-12-02 23:49:02
1.原理简介 哈希查找是通过计算数据元素的存储地址进行查找的一种方法。⑴用给定的哈希函数构造哈希表;⑵根据选择的冲突处理方法解决地址冲突;⑶在哈希表的基础上执行哈希查找。构造哈希函数:直接定址法、数字分析法、平方取中法、折叠法、除留余数法、随机数法。冲突解决:开放定址法、链地址法。 2.代码实现 #define MaxSize 100 //定义最大哈希表长度 #define NULLKEY -1 //定义空关键字值 #define DELKEY -2 //定义被删关键字值 typedef int KeyType; //关键字类型 typedef char InfoType; //其他数据类型 typedef struct { KeyType key; //关键字域 InfoType data; //其他数据域 int count; //探查次数域 }HashTable[MaxSize]; //哈希表类型 void InsertHT(HashTable ha, int &n, KeyType k, int p) //将关键字k插入到哈希表中 { int i, adr; adr = k%p; if (ha[adr].key == NULLKEY || ha[adr].key == DELKEY) { //x[j]可以直接放在哈希表中 ha[adr].key = k; ha[adr]

Leetcode 题解 - 二分查找

匿名 (未验证) 提交于 2019-12-02 23:43:01
版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 ( Creative Commons ) 文章目录 1. 求开方 2. 大于给定元素的最小元素 3. 有序数组的 Single Element 4. 第一个错误的版本 5. 旋转数组的最小数字 6. 查找区间 微信公众号 正常实现 Input : [1,2,3,4,5] key : 3 return the index : 2 public int binarySearch ( int [ ] nums , int key ) { int l = 0 , h = nums . length - 1 ; while ( l <= h ) { int m = l + ( h - l ) / 2 ; if ( nums [ m ] == key ) { return m ; } else if ( nums [ m ] > key ) { h = m - 1 ; } else { l = m + 1 ; } } return - 1 ; } 时间复杂度 二分查找也称为折半查找,每次都能将查找区间减半,这种折半特性的算法时间复杂度为 O(logN)。 m 计算 有两种计算中值 m 的方式: m = (l + h) / 2 m = l + (h - l) / 2 l + h 可能出现加法溢出

数据结构之B+树

匿名 (未验证) 提交于 2019-12-02 22:56:40
title: 数据结构之B+树 date: 2018-11-04 20:39:00 tags: 数据结构与算法之美 一棵m阶B-树,或者是空树,或者是满足以下性质的m叉树 根结点至少有两个分支; 除根以外的非叶结点,每个结点包含分支数范围[[m/2],m],即关键字字数的范围是[[m/2]-1,m-1],其中[m/2]表示取大于等于m/2的最小整数 所有叶子结点都在树的同一层上,并且指针域为空; 所有的非终端结点应包含如下信息: (n,A0,K1,A1,K2,A2,… ,Kn,An),结点内关键字互不相等,且从小到大排列。 解释: m阶的意思是这颗树最多的分支是多少,每个节点可以包含很多关键字,范围是[[m/2]-1,m-1],比如m为 3,就说明是3阶的B-树, 那么它的树的节点的关键字最少2,最多4个。下面的B+树也是同样的理解。 设关键字的总数为 N ,求 m阶 B- 树的最大层次 L。 在实际的文件系统中,基本上不使用B_树,而是使用B_树的一种变体,称为m阶B+树。 它与B-树的主要不同是叶子结点中存储记录。在B+树中,所有的非叶子结点可以看成是索引,而其中的关键字是作为“分界关键 字”,用来界定某一关键字的记录所在的子树。一棵m阶B+树与m阶B-树的主要差异是: 1.若一个结点有n棵子树,则必含有n个关键字; 2

Netty源码―五、内存分配概述

匿名 (未验证) 提交于 2019-12-02 22:56:40
Netty中的内存管理应该是借鉴了FreeBSD内存管理的思想―― jemalloc 。Netty内存分配过程中总体遵循以下规则: 优先从缓存中分配 如果缓存中没有的话,从内存池看看有没有剩余可用的 如果已申请的没有的话,再真正申请内存 分段管理,每个内存大小范围使用不同的分配策略 我们先总体上看下Netty内存分配的策略,然后再结合对应的数据结构来看看每种策略的具体实现。 netty根据需要分配内存的大小使用不同的分配策略,主要分为以下几种情况(pageSize默认是8K, chunkSize默认是16m): tiny: allocateSize<512,allocateSubpage small: pageSize>=allocateSize >=512,allocateSubpage normal: chunkSize >= allocateSize > pageSize ,allocateRun huge: allocateSize > chunkSize 内存分配的调用堆栈 结合上面内存分配的调用堆栈看看内存分配的主要过程: new一个ByteBuf,如果是direct则new:PooledUnsafeDirectByteBuf 从缓存中查找,没有可用的缓存进行下一步 从内存池中查找可用的内存,查找的方式如上所述(tiny、small、normal)

查找Python包的依赖包(语句)

匿名 (未验证) 提交于 2019-12-02 22:54:36
Window 10家庭中文版,Python 3.6.4, 于是,就有了一段200来行的程序和本文了。 功能名称: 查找Python包的依赖包(语句) 功能介绍: 找到Python包(包括子目录)中所有Python语句中的from、import语句,from语句获取import前面的部分,import语句获取整行。 使用方法: 使用 包的绝对路径 建立类 ModuleYilai(模块依赖) 的实例,然后调用实例方法yilais就可以获得Python包的依赖包呢,以列表形式返回。 程序介绍: class ModuleYilai 查找包依赖类; def ispackage(dirpath) 检查文件夹是否是Python包,判断是否含有__init__.py文件; def get_all_dirs(dirpath, level=True) 获取给定dirpath目录及其子目录的绝对路径的列表,level为True时包含目录本身;用到了递归,内部使用时,level为False; def get_all_pyfiles(dirpath) 获取目录下的所有Python文件(*.py); def get_pyfile_yilais(pyfile) 获取Python文件中所有的from子句、import子句; 需要说明的是,在建立正则表达时是,会忽略了from子句、import子句位于文件开头的情况

如何理解设计思想与代码质量优化

守給你的承諾、 提交于 2019-12-02 22:42:53
本文将通过六大原则、设计模式、数据结构、算法来阐述设计思想与代码质量优化的结合 一、六大原则 1、单一职责原则 不要存在多于一个导致类变更的原因,也就是说每个类应该实现单一的职责,如若不然,就应该把类拆分。 2、里氏替换原则(Liskov Substitution Principle) 里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。—— From Baidu 百科 历史替换原则中,子类对父类的方法尽量不要重写和重载。因为父类代表了定义好的结构,通过这个规范的接口与外界交互,子类不应该随便破坏它。 3、依赖倒转原则(Dependence Inversion Principle) 这个是开闭原则的基础,具体内容:面向接口编程,依赖于抽象而不依赖于具体。写代码时用到具体类时,不与具体类交互,而与具体类的上层接口交互。 4、接口隔离原则(Interface