• 关于

    因子图是什么

    的搜索结果

回答

TensorFlow 什么是TensorFlow? 该库是由Google与Brain Team合作开发的。TensorFlow几乎在每个Google应用程序中用于机器学习。 TensorFlow的工作方式类似于编写涉及大量张量操作的新算法的计算库。由于神经网络可以很容易地表示为计算图形,因此它们可以使用TensorFlow作为Tensors上的一系列操作来实现。此外,张量是表示数据的N维矩阵。 TensorFlow的特点 TensorFlow针对速度进行了优化,它利用XLA等技术实现快速线性代数运算。 使用TensorFlow,我们可以轻松地可视化图形的每个部分,这在使用Numpy或SciKit时不是一个选项。 其中一个非常重要的Tensorflow功能是它的可操作性非常灵活,这意味着它具有模块化,并且对于您想要独立的部分,它为您提供了这一选择。 它可以在CPU和GPU上轻松训练,用于分布式计算。 TensorFlow提供流水线操作,从某种意义上说,您可以训练多个神经网络和多个GPU,这使得模型在大规模系统上非常高效。已经有一大批软件工程师不断致力于稳定性改进。而且它是开源的,所以只要有互联网连接,任何人都可以使用它。 NumPy 什么是Numpy? Numpy被认为是Python中最受欢迎的机器学习库之一。TensorFlow和其他库在内部使用Numpy在Tensors上执行多个操作。数组接口是Numpy的最佳和最重要的功能。 Numpy的特点 Numpy非常具有交互性且易于使用;使复杂的数学实现变得非常简单;使编码变得简单易懂并且理解概念很容易;广泛使用,因此有很多开源贡献。 Scikit-Learn 什么是Scikit-Learn? 它是一个与NumPy和SciPy相关联的Python库。它被认为是处理复杂数据的最佳库之一。 这个库中有很多变化。一种修改是交叉验证功能,可以使用多个指标。物流回归和最近邻居等许多培训方法都得到了一些改进。 Scikit-Learn的特点 有多种方法可以检查监督模型对看不见的数据的准确性;无监督学习算法:同样,在提供中有大量的算法 - 从聚类,因子分析和主成分分析到无监督神经网络;用于从图像和文本中提取特征。

剑曼红尘 2020-03-26 13:02:32 0 浏览量 回答数 0

问题

图解九大数据结构 6月13日 【今日算法】

游客ih62co2qqq5ww 2020-06-17 13:17:00 29 浏览量 回答数 1

问题

图解!24张图彻底弄懂九大常见数据结构! 7月22日 【今日算法】

游客ih62co2qqq5ww 2020-07-27 13:19:32 6 浏览量 回答数 1

阿里云试用中心,为您提供0门槛上云实践机会!

0元试用32+款产品,最高免费12个月!拨打95187-1,咨询专业上云建议!

回答

在Java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结构,但是在jdk1.8里 加入了红黑树的实现,当链表的长度大于8时,转换为红黑树的结构。这里写图片描述从上图中可以看出,Java中HashMap采用了链地址法。链地址法,简单来说,就是数组加链表的结合。在每个数组元素上都一个链表结构,当数据被Hash后,得到数组下标,把数据放在对应下标元素的链表上。 */ static class Node<K,V> implements Map.Entry<K,V> { final int hash;//用于定位数组索引的位置 final K key; V value; Node<K,V> next;//链表的下一个Node Node(int hash, K key, V value, Node<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } Node是HashMap的一个内部类,实现了Map.Entry接口,本质是就是一个映射(键值对)。有时两个key会定位到相同的位置,表示发生了Hash碰撞。当然Hash算法计算结果越分散均匀,Hash碰撞的概率就越小,map的存取效率就会越高。HashMap类中有一个非常重要的字段,就是 Node[] table,即哈希桶数组,明显它是一个Node的数组。如果哈希桶数组很大,即使较差的Hash算法也会比较分散,如果哈希桶数组数组很小,即使好的Hash算法也会出现较多碰撞,所以就需要在空间成本和时间成本之间权衡,其实就是在根据实际情况确定哈希桶数组的大小,并在此基础上设计好的hash算法减少Hash碰撞。那么通过什么方式来控制map使得Hash碰撞的概率又小,哈希桶数组(Node[] table)占用空间又少呢?答案就是好的Hash算法和扩容机制。如果哈希桶数组很大,即使较差的Hash算法也会比较分散,如果哈希桶数组数组很小,即使好的Hash算法也会出现较多碰撞,所以就需要在空间成本和时间成本之间权衡,其实就是在根据实际情况确定哈希桶数组的大小,并在此基础上设计好的hash算法减少Hash碰撞。这里存在一个问题,即使负载因子和Hash算法设计的再合理,也免不了会出现拉链过长的情况,一旦出现拉链过长,则会严重影响HashMap的性能。于是,在JDK1.8版本中,对数据结构做了进一步的优化,引入了红黑树。而当链表长度太长(默认超过8)时,链表就转换为红黑树,利用红黑树快速增删改查的特点提高HashMap的性能,其中会用到红黑树的插入、删除、查找等算法

wangccsy 2019-12-02 01:47:22 0 浏览量 回答数 0

回答

HashMap 和 HashSet 内部是如何工作的?散列函数(hashing function)是什么? HashMap 不仅是一个常用的数据结构,在面试中也是热门话题。 Q1. HashMap 如何存储数据?A1. 以键/值对(key/value)形式存储。你可以使用键(key)来存、取值。 Q2. HashMap 查询时间的复杂度是怎样的?A2. 是O(n) = O(k * n)。如果 hashCode() 方法能向下面讨论的那样把数据分散到桶(bucket)中,那么平均是O(1)。 Q3. HashMap 内部是如何存储数据的?A3. HashMap 使用后台数组(backing array)作为桶,并使用链表(linked list)存储键/值对。 桶的后台数组:如下所示 hashCode() 返回 1, 45等 1)使用键(key)和值(value)将一个对象放入 map 中时,会隐式调用 hashCode() 方法,返回哈希值(hash code value),比如 123。两个不同的键能够返回一样的哈希值。良好的哈希算法(hashing algorithm)能够将数值分散开。在上面的例子中,我们假设 (“John”,01/01/1956) 的键和 (“Peter”, 01/01/1995) 的键返回相同的哈希值,都是 123。 Java equals vs hashCode 2)当返回一个 hashCode,例如是 123,初始的 HashMap 容量为 10,它如何知道存储到后台数组(backing array)的哪个索引(index)呢?HashMap 内部会调用 hash(int ) 和 indexFor(int h, int length) 方法。这被称为哈希函数(hashing function)。简要解释下这个函数:1234 hashCode() % capacity 123 % 10 = 3456 % 10 = 6 这表示,“hashCode = 123”存储在备份数组的索引3上。容量为 10 的情况下,你可能得到的数字在 0 到 9 之间。一旦 HashMap 达到容量的 75%,也就是哈希因子(hash factor)默认值 0.75,后台数组(backing array)的容量就会加倍,发生重散列(rehashing)为新的 20 的容量重新分配桶。1234 hashCode() % capacity 123 % 20 = 3456 % 20 = 16 上面重散列的取模方法有一个缺陷。如果 hashCode 是负数会怎样?负索引可不是你想要的。因此,一个改进的哈希公式会移出符号位,然后再用取模(即 %)运算符计算剩余部分。12 (123 & 0x7FFFFFFF) % 20 = 3(456 & 0x7FFFFFFF) % 20 = 16 这确保你得到的索引值为正数。如果你查看 Java 8 的 HashMap 源码,它的实现使用以下方法: a). 通过只抽取重要的低位,来防止不良离散值(poorer hashes)。1234567 static int hash(int h) { // This function ensures that hashCodes that differ only by // constant multiples at each bit position have a bounded // number of collisions (approximately 8 at default load factor). h ^= (h >>> 20) ^ (h >>> 12); return h ^ (h >>> 7) ^ (h >>> 4); } b). 根据哈希码(hashCode)和容量(capacity),来决定索引(index)。123 static int indexFor(int h, int length) { return h & (length-1); } 实际的名称值对(name value pairs)作为一个键/值对存储在 LinkedList 中。 如上图所示,键/值对以链表形式存储。两个不同的键可以产生一样的 hashCode,例如123,并存储在同一个 bucket 中,理解这点至关重要。例如,上面例子中的 “John, 01/01/1956” 和 “Peter, 01/01/1995“ 。你如何只检索 “John, 01/01/1956” 呢?此时你的 key 所属类的 equals() 方法会被调用。它遍历 bucket 为 “123” 的 LinkedList 中的每个条目,使用 equals() 方法找到并检索出键为 “John, 01/01/1956” 的条目。这就是在你的类中实现 hashCode() 和 equals() 方法重要性的原因。如果你使用一个现有的包装类,如 Integer 或 String 作为键,它们已经实现了这两个方法。如果你使用自己写的类作为键,如 “John, 01/01/1956” 这样含有名字和出生日期属性的“MyKey”,你有责任正确地实现这些方法。 Q5. 为什么恰当地设置 HashMap 的初始容量(initial capacity)是最佳实践?A5. 这样可以减少重散列的发生。 Q6. HashSet 内部如何存储数据?A6. HashSet 内部使用 HashMap 。它将元素存储为键和值。(译者注:HashSet 把存储的值作为 key) Q7. 为 Object 实现了一个糟糕的 hashcode() 会有什么影响?A7. 不同的对象调用 hashCode() 方法应该返回不同的值。如果不同的对象返回相同的值,会导致更多的键/值对存储在同一个 bucket 中。这会降低 HashMap 和 HashSet 的性能。

wangccsy 2019-12-02 01:48:57 0 浏览量 回答数 0

问题

什么是B+树 6月1日【今日算法】

游客ih62co2qqq5ww 2020-06-01 14:50:52 1 浏览量 回答数 1

回答

请参考个人博客:https://blog.csdn.net/u010870518/article/details/79450295 在进一步分析为什么MySQL数据库索引选择使用B+树之前,我相信很多小伙伴对数据结构中的树还是有些许模糊的,因此我们由浅入深一步步探讨树的演进过程,在一步步引出B树以及为什么MySQL数据库索引选择使用B+树! 学过数据结构的一般对最基础的树都有所认识,因此我们就从与我们主题更为相近的二叉查找树开始。 一、二叉查找树 (1)二叉树简介: 二叉查找树也称为有序二叉查找树,满足二叉查找树的一般性质,是指一棵空树具有如下性质: 1、任意节点左子树不为空,则左子树的值均小于根节点的值; 2、任意节点右子树不为空,则右子树的值均大于于根节点的值; 3、任意节点的左右子树也分别是二叉查找树; 4、没有键值相等的节点; 上图为一个普通的二叉查找树,按照中序遍历的方式可以从小到大的顺序排序输出:2、3、5、6、7、8。 对上述二叉树进行查找,如查键值为5的记录,先找到根,其键值是6,6大于5,因此查找6的左子树,找到3;而5大于3,再找其右子树;一共找了3次。如果按2、3、5、6、7、8的顺序来找同样需求3次。用同样的方法在查找键值为8的这个记录,这次用了3次查找,而顺序查找需要6次。计算平均查找次数得:顺序查找的平均查找次数为(1+2+3+4+5+6)/ 6 = 3.3次,二叉查找树的平均查找次数为(3+3+3+2+2+1)/6=2.3次。二叉查找树的平均查找速度比顺序查找来得更快。 (2)局限性及应用 一个二叉查找树是由n个节点随机构成,所以,对于某些情况,二叉查找树会退化成一个有n个节点的线性链。如下图: 大家看上图,如果我们的根节点选择是最小或者最大的数,那么二叉查找树就完全退化成了线性结构。上图中的平均查找次数为(1+2+3+4+5+5)/6=3.16次,和顺序查找差不多。显然这个二叉树的查询效率就很低,因此若想最大性能的构造一个二叉查找树,需要这个二叉树是平衡的(这里的平衡从一个显著的特点可以看出这一棵树的高度比上一个输的高度要大,在相同节点的情况下也就是不平衡),从而引出了一个新的定义-平衡二叉树AVL。 二、AVL树 (1)简介 AVL树是带有平衡条件的二叉查找树,一般是用平衡因子差值判断是否平衡并通过旋转来实现平衡,左右子树树高不超过1,和红黑树相比,它是严格的平衡二叉树,平衡条件必须满足(所有节点的左右子树高度差不超过1)。不管我们是执行插入还是删除操作,只要不满足上面的条件,就要通过旋转来保持平衡,而旋转是非常耗时的,由此我们可以知道AVL树适合用于插入删除次数比较少,但查找多的情况。 从上面是一个普通的平衡二叉树,这张图我们可以看出,任意节点的左右子树的平衡因子差值都不会大于1。 (2)局限性 由于维护这种高度平衡所付出的代价比从中获得的效率收益还大,故而实际的应用不多,更多的地方是用追求局部而不是非常严格整体平衡的红黑树。当然,如果应用场景中对插入删除不频繁,只是对查找要求较高,那么AVL还是较优于红黑树。 (3)应用 1、Windows NT内核中广泛存在; 三、红黑树 (1)简介 一种二叉查找树,但在每个节点增加一个存储位表示节点的颜色,可以是red或black。通过对任何一条从根到叶子的路径上各个节点着色的方式的限制,红黑树确保没有一条路径会比其它路径长出两倍。它是一种弱平衡二叉树(由于是若平衡,可以推出,相同的节点情况下,AVL树的高度低于红黑树),相对于要求严格的AVL树来说,它的旋转次数变少,所以对于搜索、插入、删除操作多的情况下,我们就用红黑树。 (2)性质 1、每个节点非红即黑; 2、根节点是黑的; 3、每个叶节点(叶节点即树尾端NULL指针或NULL节点)都是黑的; 4、如果一个节点是红的,那么它的两儿子都是黑的; 5、对于任意节点而言,其到叶子点树NULL指针的每条路径都包含相同数目的黑节点; 6、每条路径都包含相同的黑节点; (3)应用 1、广泛用于C++的STL中,Map和Set都是用红黑树实现的; 2、著名的Linux进程调度Completely Fair Scheduler,用红黑树管理进程控制块,进程的虚拟内存区域都存储在一颗红黑树上,每个虚拟地址区域都对应红黑树的一个节点,左指针指向相邻的地址虚拟存储区域,右指针指向相邻的高地址虚拟地址空间; 3、IO多路复用epoll的实现采用红黑树组织管理sockfd,以支持快速的增删改查; 4、Nginx中用红黑树管理timer,因为红黑树是有序的,可以很快的得到距离当前最小的定时器; 5、Java中TreeMap的实现; 四、B/B+树 说了上述的三种树:二叉查找树、AVL和红黑树,似乎我们还没有摸到MySQL为什么要使用B+树作为索引的实现,不要急,接下来我们就先探讨一下什么是B树。 (1)简介 我们在MySQL中的数据一般是放在磁盘中的,读取数据的时候肯定会有访问磁盘的操作,磁盘中有两个机械运动的部分,分别是盘片旋转和磁臂移动。盘片旋转就是我们市面上所提到的多少转每分钟,而磁盘移动则是在盘片旋转到指定位置以后,移动磁臂后开始进行数据的读写。那么这就存在一个定位到磁盘中的块的过程,而定位是磁盘的存取中花费时间比较大的一块,毕竟机械运动花费的时候要远远大于电子运动的时间。当大规模数据存储到磁盘中的时候,显然定位是一个非常花费时间的过程,但是我们可以通过B树进行优化,提高磁盘读取时定位的效率。 为什么B类树可以进行优化呢?我们可以根据B类树的特点,构造一个多阶的B类树,然后在尽量多的在结点上存储相关的信息,保证层数尽量的少,以便后面我们可以更快的找到信息,磁盘的I/O操作也少一些,而且B类树是平衡树,每个结点到叶子结点的高度都是相同,这也保证了每个查询是稳定的。 总的来说,B/B+树是为了磁盘或其它存储设备而设计的一种平衡多路查找树(相对于二叉,B树每个内节点有多个分支),与红黑树相比,在相同的的节点的情况下,一颗B/B+树的高度远远小于红黑树的高度(在下面B/B+树的性能分析中会提到)。B/B+树上操作的时间通常由存取磁盘的时间和CPU计算时间这两部分构成,而CPU的速度非常快,所以B树的操作效率取决于访问磁盘的次数,关键字总数相同的情况下B树的高度越小,磁盘I/O所花的时间越少。 注意B-树就是B树,-只是一个符号。 (2)B树的性质 1、定义任意非叶子结点最多只有M个儿子,且M>2; 2、根结点的儿子数为[2, M]; 3、除根结点以外的非叶子结点的儿子数为[M/2, M]; 4、每个结点存放至少M/2-1(取上整)和至多M-1个关键字;(至少2个关键字) 5、非叶子结点的关键字个数=指向儿子的指针个数-1; 6、非叶子结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1]; 7、非叶子结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字小于K[1]的子树,P[M]指向关键字大于K[M-1]的子树,其它P[i]指向关键字属于(K[i-1], K[i])的子树; 8、所有叶子结点位于同一层; 这里只是一个简单的B树,在实际中B树节点中关键字很多的,上面的图中比如35节点,35代表一个key(索引),而小黑块代表的是这个key所指向的内容在内存中实际的存储位置,是一个指针。 五、B+树 (1)简介 B+树是应文件系统所需而产生的一种B树的变形树(文件的目录一级一级索引,只有最底层的叶子节点(文件)保存数据)非叶子节点只保存索引,不保存实际的数据,数据都保存在叶子节点中,这不就是文件系统文件的查找吗? 我们就举个文件查找的例子:有3个文件夹a、b、c, a包含b,b包含c,一个文件yang.c,a、b、c就是索引(存储在非叶子节点), a、b、c只是要找到的yang.c的key,而实际的数据yang.c存储在叶子节点上。 所有的非叶子节点都可以看成索引部分! (2)B+树的性质(下面提到的都是和B树不相同的性质) 1、非叶子节点的子树指针与关键字个数相同; 2、非叶子节点的子树指针p[i],指向关键字值属于[k[i],k[i+1]]的子树.(B树是开区间,也就是说B树不允许关键字重复,B+树允许重复); 3、为所有叶子节点增加一个链指针; 4、所有关键字都在叶子节点出现(稠密索引). (且链表中的关键字恰好是有序的); 5、非叶子节点相当于是叶子节点的索引(稀疏索引),叶子节点相当于是存储(关键字)数据的数据层; 6、更适合于文件系统; 非叶子节点(比如5,28,65)只是一个key(索引),实际的数据存在叶子节点上(5,8,9)才是真正的数据或指向真实数据的指针。 (3)应用 1、B和B+树主要用在文件系统以及数据库做索引,比如MySQL; 六、B/B+树性能分析 n个节点的平衡二叉树的高度为H(即logn),而n个节点的B/B+树的高度为logt((n+1)/2)+1;   若要作为内存中的查找表,B树却不一定比平衡二叉树好,尤其当m较大时更是如此。因为查找操作CPU的时间在B-树上是O(mlogtn)=O(lgn(m/lgt)),而m/lgt>1;所以m较大时O(mlogtn)比平衡二叉树的操作时间大得多。因此在内存中使用B树必须取较小的m。(通常取最小值m=3,此时B-树中每个内部结点可以有2或3个孩子,这种3阶的B-树称为2-3树)。 七、为什么说B+树比B树更适合数据库索引? 1、 B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。 2、B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。 3、由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。 PS:我在知乎上看到有人是这样说的,我感觉说的也挺有道理的: 他们认为数据库索引采用B+树的主要原因是:B树在提高了IO性能的同时并没有解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低。 ———————————————— 版权声明:本文为CSDN博主「徐刘根」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/u010870518/java/article/details/79450295

AA大大官 2020-03-31 14:54:01 0 浏览量 回答数 0

问题

【精品问答】110+数据挖掘面试题集合

珍宝珠 2019-12-01 21:56:45 2713 浏览量 回答数 3

问题

【精品问答】python技术1000问(2)

问问小秘 2019-12-01 22:03:02 68 浏览量 回答数 0

问题

搜索引擎背后的经典数据结构和算法 6月10日 【今日算法】

游客ih62co2qqq5ww 2020-06-15 07:32:11 0 浏览量 回答数 0

问题

网站优化之提升访问速度的影响因素

chenchuan 2019-12-01 21:01:05 7177 浏览量 回答数 0

问题

网站优化之提升访问速度的影响因素

chenchuan 2019-12-01 21:37:28 3707 浏览量 回答数 0

回答

    using System;     using System.Collections.Generic;     using System.Linq;     using System.Text;    namespace test{    class QuickSort    {        static void Main(string[] args)        {            int[] array = { 49, 38, 65, 97, 76, 13, 27 };            sort(array, 0, array.Length - 1);            Console.ReadLine();        }        /**一次排序单元,完成此方法,key左边都比key小,key右边都比key大。         **@param array排序数组          **@param low排序起始位置          **@param high排序结束位置         **@return单元排序后的数组 */        private static int sortUnit(int[] array, int low, int high)        {            int key = array[low];            while (low < high)            {                /*从后向前搜索比key小的值*/                while (array[high] >= key && high > low)                    --high;                 /*比key小的放左边*/                array[low] = array[high];                   /*从前向后搜索比key大的值,比key大的放右边*/                while (array[low] <= key && high > low)                    ++low;                 /*比key大的放右边*/                array[high] = array[low];            }            /*左边都比key小,右边都比key大。//将key放在游标当前位置。//此时low等于high */            array[low] = key;            foreach (int i in array)            {                Console.Write({0}\t, i);            }            Console.WriteLine();            return high;        }            /**快速排序 *@paramarry *@return */        public static void sort(int[] array, int low, int high)        {            if (low >= high)                return;             /*完成一次单元排序*/            int index = sortUnit(array, low, high);             /*对左边单元进行排序*/            sort(array, low, index - 1);            /*对右边单元进行排序*/            sort(array, index + 1, high);        }    }} 运行结果:27 38 13 49 76 97 6513 27 38 49 76 97 65  13 27 38 49 65 76 97快速排序就是递归调用此过程——在以49为中点分割这个数据序列,分别对前面一部分和后面一部分进行类似的快速排序,从而完成全部数据序列的快速排序,最后把此数据序列变成一个有序的序列,根据这种思想对于上述数组A的快速排序的全过程如图6所示:初始状态 {49 38 65 97 76 13 27} 进行一次快速排序之后划分为 {27 38 13} 49 {76 97 65} 分别对前后两部分进行快速排序{27 38 13} 经第三步和第四步交换后变成 {13 27 38} 完成排序。{76 97 65} 经第三步和第四步交换后变成 {65 76 97} 完成排序。图示 快速排序的最坏情况基于每次划分对主元的选择。基本的快速排序选取第一个元素作为主元。这样在数组已经有序的情况下,每次划分将得到最坏的结果。一种比较常见的优化方法是随机化算法,即随机选取一个元素作为主元。这种情况下虽然最坏情况仍然是O(n^2),但最坏情况不再依赖于输入数据,而是由于随机函数取值不佳。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。一位前辈做出了一个精辟的总结:“随机化快速排序可以满足一个人一辈子的人品需求。”随机化快速排序的唯一缺点在于,一旦输入数据中有很多的相同数据,随机化的效果将直接减弱。对于极限情况,即对于n个相同的数排序,随机化快速排序的时间复杂度将毫无疑问的降低到O(n^2)。解决方法是用一种方法进行扫描,使没有交换的情况下主元保留在原位置。 QUICKSORT(A,p,r)1 if p<r2 then q ←PARTITION(A,p,r)3 QUICKSORT(A,p,q-1)4 QUICKSORT(A,q+1,r)为排序一个完整的数组A,最初的调用是QUICKSORT(A,1,length[A])。快速排序算法的关键是PARTITION过程,它对子数组A[p..r]进行就地重排:PARTITION(A,p,r)1 x←A[r]2 i←p-13 for j←p to r-14 do if A[j]≤x5 then i←i+16 exchange A[i]←→A[j]7 exchange A[i+1]←→A[r]8 return i+1 对PARTITION和QUICKSORT所作的改动比较小。在新的划分过程中,我们在真正进行划分之前实现交换:(其中PARTITION过程同快速排序伪代码(非随机))RANDOMIZED-PARTITION(A,p,r)1 i← RANDOM(p,r)2 exchange A[r]←→A[i]3 return PARTITION(A,p,r)新的快速排序过程不再调用PARTITION,而是调用RANDOMIZED-PARTITION。RANDOMIZED-QUICKSORT(A,p,r)1 if p<r2 then q← RANDOMIZED-PARTITION(A,p,r)3 RANDOMIZED-QUICKSORT(A,p,q-1)4 RANDOMIZED-QUICKSORT(A,q+1,r) 这里为方便起见,我们假设算法Quick_Sort的范围阈值为1(即一直将线性表分解到只剩一个元素),这对该算法复杂性的分析没有本质的影响。我们先分析函数partition的性能,该函数对于确定的输入复杂性是确定的。观察该函数,我们发现,对于有n个元素的确定输入L[p..r],该函数运行时间显然为θ(n)。最坏情况无论适用哪一种方法来选择pivot,由于我们不知道各个元素间的相对大小关系(若知道就已经排好序了),所以我们无法确定pivot的选择对划分造成的影响。因此对各种pivot选择法而言,最坏情况和最好情况都是相同的。我们从直觉上可以判断出最坏情况发生在每次划分过程产生的两个区间分别包含n-1个元素和1个元素的时候(设输入的表有n个元素)。下面我们暂时认为该猜测正确,在后文我们再详细证明该猜测。对于有n个元素的表L[p..r],由于函数Partition的计算时间为θ(n),所以快速排序在序坏情况下的复杂性有递归式如下:T(1)=θ(1),T(n)=T(n-1)+T(1)+θ(n) (1)用迭代法可以解出上式的解为T(n)=θ(n2)。这个最坏情况运行时间与插入排序是一样的。下面我们来证明这种每次划分过程产生的两个区间分别包含n-1个元素和1个元素的情况就是最坏情况。设T(n)是过程Quick_Sort作用于规模为n的输入上的最坏情况的时间,则T(n)=max(T(q)+T(n-q))+θ(n),其中1≤q≤n-1 (2)我们假设对于任何k<n,总有T(k)≤ck,其中c为常数;显然当k=1时是成立的。将归纳假设代入(2),得到:T(n)≤max(cq2+c(n-q)2)+θ(n)=c*max(q2+(n-q)2)+θ(n)因为在[1,n-1]上q2+(n-q)2关于q递减,所以当q=1时q2+(n-q)2有最大值n2-2(n-1)。于是有:T(n)≤cn2-2c(n-1)+θ(n)≤cn2只要c足够大,上面的第二个小于等于号就可以成立。于是对于所有的n都有T(n)≤cn。这样,排序算法的最坏情况运行时间为θ(n2),且最坏情况发生在每次划分过程产生的两个区间分别包含n-1个元素和1个元素的时候。时间复杂度为o(n2)。最好情况如果每次划分过程产生的区间大小都为n/2,则快速排序法运行就快得多了。这时有:T(n)=2T(n/2)+θ(n),T(1)=θ(1) (3)解得:T(n)=θ(nlogn)快速排序法最佳情况下执行过程的递归树如下图所示,图中lgn表示以10为底的对数,而本文中用logn表示以2为底的对数.由于快速排序法也是基于比较的排序法,其运行时间为Ω(nlogn),所以如果每次划分过程产生的区间大小都为n/2,则运行时间θ(nlogn)就是最好情况运行时间。但是,是否一定要每次平均划分才能达到最好情况呢。要理解这一点就必须理解对称性是如何在描述运行时间的递归式中反映的。我们假设每次划分过程都产生9:1的划分,乍一看该划分很不对称。我们可以得到递归式:T(n)=T(n/10)+T(9n/10)+θ(n),T(1)=θ(1) (4)请注意树的每一层都有代价n,直到在深度log10n=θ(logn)处达到边界条件,以后各层代价至多为n。递归于深度log10/9n=θ(logn)处结束。这样,快速排序的总时间代价为T(n)=θ(nlogn),从渐进意义上看就和划分是在中间进行的一样。事实上,即使是99:1的划分时间代价也为θ(nlogn)。其原因在于,任何一种按常数比例进行划分所产生的递归树的深度都为θ(nlogn),其中每一层的代价为O(n),因而不管常数比例是什么,总的运行时间都为θ(nlogn),只不过其中隐含的常数因子有所不同。(关于算法复杂性的渐进阶,请参阅算法的复杂性)平均情况快速排序的平均运行时间为θ(nlogn)。我们对平均情况下的性能作直觉上的分析。要想对快速排序的平均情况有个较为清楚的概念,我们就要对遇到的各种输入作个假设。通常都假设输入数据的所有排列都是等可能的。后文中我们要讨论这个假设。当我们对一个随机的输入数组应用快速排序时,要想在每一层上都有同样的划分是不太可能的。我们所能期望的是某些划分较对称,另一些则很不对称。事实上,我们可以证明,如果选择L[p..r]的第一个元素作为支点元素,Partition所产生的划分80%以上都比9:1更对称,而另20%则比9:1差,这里证明从略。平均情况下,Partition产生的划分中既有“好的”,又有“差的”。这时,与Partition执行过程对应的递归树中,好、差划分是随机地分布在树的各层上的。为与我们的直觉相一致,假设好、差划分交替出现在树的各层上,且好的划分是最佳情况划分,而差的划分是最坏情况下的划分。在根节点处,划分的代价为n,划分出来的两个子表的大小为n-1和1,即最坏情况。在根的下一层,大小为n-1的子表按最佳情况划分成大小各为(n-1)/2的两个子表。这儿我们假设含1个元素的子表的边界条件代价为1。在一个差的划分后接一个好的划分后,产生出三个子表,大小各为1,(n-1)/2和(n-1)/2,代价共为2n-1=θ(n)。一层划分就产生出大小为(n-1)/2+1和(n-1)/2的两个子表,代价为n=θ(n)。这种划分差不多是完全对称的,比9:1的划分要好。从直觉上看,差的划分的代价θ(n)可被吸收到好的划分的代价θ(n)中去,结果是一个好的划分。这样,当好、差划分交替分布划分都是好的一样:仍是θ(nlogn),但θ记号中隐含的常数因子要略大一些。关于平均情况的严格分析将在后文给出。在前文从直觉上探讨快速排序的平均性态过程中,我们已假定输入数据的所有排列都是等可能的。如果输入的分布满足这个假设时,快速排序是对足够大的输入的理想选择。但在实际应用中,这个假设就不会总是成立。解决的方法是,利用随机化策略,能够克服分布的等可能性假设所带来的问题。一种随机化策略是:与对输入的分布作“假设”不同的是对输入的分布作“规定”。具体地说,在排序输入的线性表前,对其元素加以随机排列,以强制的方法使每种排列满足等可能性。事实上,我们可以找到一个能在O(n)时间内对含n个元素的数组加以随机排列的算法。这种修改不改变算法的最坏情况运行时间,但它却使得运行时间能够独立于输入数据已排序的情况。另一种随机化策略是:利用前文介绍的选择支点元素pivot的第四种方法,即随机地在L[p..r]中选择一个元素作为支点元素pivot。实际应用中通常采用这种方法。快速排序的随机化版本有一个和其他随机化算法一样的有趣性质:没有一个特别的输入会导致最坏情况性态。这种算法的最坏情况性态是由随机数产生器决定的。你即使有意给出一个坏的输入也没用,因为随机化排列会使得输入数据的次序对算法不产生影响。只有在随机数产生器给出了一个很不巧的排列时,随机化算法的最坏情况性态才会出现。事实上可以证明几乎所有的排列都可使快速排序接近平均情况性态,只有非常少的几个排列才会导致算法的近最坏情况性态。一般来说,当一个算法可按多条路子做下去,但又很难决定哪一条保证是好的选择时,随机化策略是很有用的。如果大部分选择都是好的,则随机地选一个就行了。通常,一个算法在其执行过程中要做很多选择。如果一个好的选择的获益大于坏的选择的代价,那么随机地做一个选择就能得到一个很有效的算法。我们在前文已经了解到,对快速排序来说,一组好坏相杂的划分仍能产生很好的运行时间 。因此我们可以认为该算法的随机化版本也能具有较好的性态。

liujae 2019-12-02 01:18:45 0 浏览量 回答数 0

回答

SpringBoot整合ES 创建SpringBoot项目,导入 ES 6.2.1 的 RestClient 依赖和 ES 依赖。在项目中直接引用 es-starter 的话会报容器初始化异常错误,导致项目无法启动。如果有读者解决了这个问题,欢迎留言交流 org.elasticsearch.client elasticsearch-rest-high-level-client ${elasticsearch.version} org.elasticsearch elasticsearch ${elasticsearch.version} 为容器定义 RestClient 对象 /** * 在Spring容器中定义 RestClient 对象 * @Author: keats_coder * @Date: 2019/8/9 * @Version 1.0 * */ @Configuration public class ESConfig { @Value("${yunshangxue.elasticsearch.hostlist}") private String hostlist; // 127.0.0.1:9200 @Bean // 高版本客户端 public RestHighLevelClient restHighLevelClient() { // 解析 hostlist 配置信息。假如以后有多个,则需要用 , 分开 String[] split = hostlist.split(","); // 创建 HttpHost 数组,其中存放es主机和端口的配置信息 HttpHost[] httpHostArray = new HttpHost[split.length]; for (int i = 0; i < split.length; i++) { String item = split[i]; httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")[1]), "http"); } // 创建RestHighLevelClient客户端 return new RestHighLevelClient(RestClient.builder(httpHostArray)); } // 项目主要使用 RestHighLevelClient,对于低级的客户端暂时不用 @Bean public RestClient restClient() { // 解析hostlist配置信息 String[] split = hostlist.split(","); // 创建HttpHost数组,其中存放es主机和端口的配置信息 HttpHost[] httpHostArray = new HttpHost[split.length]; for (int i = 0; i < split.length; i++) { String item = split[i]; httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")[1]), "http"); } return RestClient.builder(httpHostArray).build(); } } 在 yml 文件中配置 eshost yunshangxue: elasticsearch: hostlist: ${eshostlist:127.0.0.1:9200} 调用相关 API 执行操作 创建操作索引的对象 构建操作索引的请求 调用对象的相关API发送请求 获取响应消息 /** * 删除索引库 */ @Test public void testDelIndex() throws IOException { // 操作索引的对象 IndicesClient indices = client.indices(); // 删除索引的请求 DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest("ysx_course"); // 删除索引 DeleteIndexResponse response = indices.delete(deleteIndexRequest); // 得到响应 boolean b = response.isAcknowledged(); System.out.println(b); } 创建索引, 步骤和删除类似,需要注意的是删除的时候需要指定 ES 库分片的数量和副本的数量,并且在创建索引的时候可以将映射一起指定了。代码如下 public void testAddIndex() throws IOException { // 操作索引的对象 IndicesClient indices = client.indices(); // 创建索引的请求 CreateIndexRequest request = new CreateIndexRequest("ysx_course"); request.settings(Settings.builder().put("number_of_shards", "1").put("number_of_replicas", "0")); // 创建映射 request.mapping("doc", "{\n" + " \"properties\": {\n" + " \"description\": {\n" + " \"type\": \"text\",\n" + " \"analyzer\": \"ik_max_word\",\n" + " \"search_analyzer\": \"ik_smart\"\n" + " },\n" + " \"name\": {\n" + " \"type\": \"text\",\n" + " \"analyzer\": \"ik_max_word\",\n" + " \"search_analyzer\": \"ik_smart\"\n" + " },\n" + "\"pic\":{ \n" + "\"type\":\"text\", \n" + "\"index\":false \n" + "}, \n" + " \"price\": {\n" + " \"type\": \"float\"\n" + " },\n" + " \"studymodel\": {\n" + " \"type\": \"keyword\"\n" + " },\n" + " \"timestamp\": {\n" + " \"type\": \"date\",\n" + " \"format\": \"yyyy-MM‐dd HH:mm:ss||yyyy‐MM‐dd||epoch_millis\"\n" + " }\n" + " }\n" + " }", XContentType.JSON); // 执行创建操作 CreateIndexResponse response = indices.create(request); // 得到响应 boolean b = response.isAcknowledged(); System.out.println(b); } Java API操作ES 准备数据环境 创建索引:ysx_course 创建映射: PUT http://localhost:9200/ysx_course/doc/_mapping { "properties": { "description": { // 课程描述 "type": "text", // String text 类型 "analyzer": "ik_max_word", // 存入的分词模式:细粒度 "search_analyzer": "ik_smart" // 查询的分词模式:粗粒度 }, "name": { // 课程名称 "type": "text", "analyzer": "ik_max_word", "search_analyzer": "ik_smart" }, "pic":{ // 图片地址 "type":"text", "index":false // 地址不用来搜索,因此不为它构建索引 }, "price": { // 价格 "type": "scaled_float", // 有比例浮点 "scaling_factor": 100 // 比例因子 100 }, "studymodel": { "type": "keyword" // 不分词,全关键字匹配 }, "timestamp": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis" } } } 加入原始数据: POST http://localhost:9200/ysx_course/doc/1 { "name": "Bootstrap开发", "description": "Bootstrap是由Twitter推出的一个前台页面开发框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长页面开发的程序人员)轻松的实现一个不受浏览器限制的精美界面效果。", "studymodel": "201002", "price":38.6, "timestamp":"2018-04-25 19:11:35", "pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg" } DSL搜索 DSL(Domain Specific Language)是ES提出的基于json的搜索方式,在搜索时传入特定的json格式的数据来完成不 同的搜索需求。DSL比URI搜索方式功能强大,在项目中建议使用DSL方式来完成搜索。 查询全部 原本我们想要查询全部的话,需要使用 GET 请求发送 _search 命令,如今使用 DSL 方式搜索,可以使用 POST 请求,并在请求体中设置 JSON 字符串来构建查询条件 POST http://localhost:9200/ysx_course/doc/_search 请求体 JSON { "query": { "match_all": {} // 查询全部 }, "_source" : ["name","studymodel"] // 查询结果包括 课程名 + 学习模式两个映射 } 具体的测试方法如下:过程比较繁琐,好在条理还比较清晰 // 搜索全部记录 @Test public void testSearchAll() throws IOException, ParseException { // 搜索请求对象 SearchRequest searchRequest = new SearchRequest("ysx_course"); // 指定类型 searchRequest.types("doc"); // 搜索源构建对象 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); // 搜索方式 // matchAllQuery搜索全部 searchSourceBuilder.query(QueryBuilders.matchAllQuery()); // 设置源字段过虑,第一个参数结果集包括哪些字段,第二个参数表示结果集不包括哪些字段 searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{}); // 向搜索请求对象中设置搜索源 searchRequest.source(searchSourceBuilder); // 执行搜索,向ES发起http请求 SearchResponse searchResponse = client.search(searchRequest); // 搜索结果 SearchHits hits = searchResponse.getHits(); // 匹配到的总记录数 long totalHits = hits.getTotalHits(); // 得到匹配度高的文档 SearchHit[] searchHits = hits.getHits(); // 日期格式化对象 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for(SearchHit hit:searchHits){ // 文档的主键 String id = hit.getId(); // 源文档内容 Map<String, Object> sourceAsMap = hit.getSourceAsMap(); String name = (String) sourceAsMap.get("name"); // 由于前边设置了源文档字段过虑,这时description是取不到的 String description = (String) sourceAsMap.get("description"); // 学习模式 String studymodel = (String) sourceAsMap.get("studymodel"); // 价格 Double price = (Double) sourceAsMap.get("price"); // 日期 Date timestamp = dateFormat.parse((String) sourceAsMap.get("timestamp")); System.out.println(name); System.out.println(studymodel); System.out.println("你看不见我,看不见我~" + description); System.out.println(price); } } 坑:red> 执行过程中遇到的问题:不能对这个值进行初始化,导致 Spring 容器无法初始化 Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'yunshangxue.elasticsearch.hostlist' in value "${yunshangxue.elasticsearch.hostlist}" 通过检查 target 目录发现,生成的 target 文件包中没有将 yml 配置文件带过来... 仔细对比发现,我的项目竟然变成了一个不是 Maven 的项目。重新使用 IDEA 导入 Mavaen 工程之后便能正常运行了 分页查询 我们来 look 一下 ES 的分页查询参数: { // from 起始索引 // size 每页显示的条数 "from" : 0, "size" : 1, "query": { "match_all": {} }, "_source" : ["name","studymodel"] } 1565524349684 通过查询结果可以发现,我们设置了分页参数之后, hits.total 仍然是 3,表示它找到了 3 条数据,而按照分页规则,它只会返回一条数据,因此 hits.hits 里面只有一条数据。这也符合我们的业务规则,在查询前端页面显示总共的条数和当前的数据。 由此,我们就可以通过 Java API 来构建查询条件了:对上面查询全部的代码进行如下改造: // 搜索源构建对象 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); int page = 2; // 页码 int size = 1; // 每页显示的条数 int index = (page - 1) * size; searchSourceBuilder.from(index); searchSourceBuilder.size(1); // 搜索方式 // matchAllQuery搜索全部 searchSourceBuilder.query(QueryBuilders.matchAllQuery()); 精确查询 TermQuery Term Query为精确查询,在搜索时会整体匹配关键字,不再将关键字分词 例如: { "query": { "term": { // 查询的方式为 term 精确查询 "name": "spring" // 查询的字段为 name 关键字是 spring } }, "_source": [ "name", "studymodel" ] } 此时查询的结果是: "hits": [ { "_index": "ysx_course", "_type": "doc", "_id": "3", "_score": 0.9331132, "_source": { "studymodel": "201001", "name": "spring开发基础" } } ] 查询到了上面这条数据,因为 spring开发基础 分完词后是 spring 开发 基础 ,而查询关键字是 spring 不分词,这样当然可以匹配到这条记录,但是当我们修改关键字为 spring开发,按照往常的查询方法,也是可以查询到的。但是 term 不一样,它不会对关键字分词。结果可想而知是查询不到的 JavaAPI如下: // 搜索方式 // termQuery 精确查询 searchSourceBuilder.query(QueryBuilders.termQuery("studymodel", "201002")); 根据 ID 查询: 根据 ID 精确查询和根据其他条件精确查询是一样的,不同的是 id 字段前面有一个下划线注意写上 searchSourceBuilder.query(QueryBuilders.termQuery("_id", "1")); 但是,当一次查询多个 ID 时,相应的 API 也应该改变,使用 termsQuery 而不是 termQuery。多了一个 s 全文检索 MatchQuery MatchQuery 即全文检索,会对关键字进行分词后匹配词条。 query:搜索的关键字,对于英文关键字如果有多个单词则中间要用半角逗号分隔,而对于中文关键字中间可以用 逗号分隔也可以不用 operator:设置查询的结果取交集还是并集,并集用 or, 交集用 and { "query": { "match": { "description": { "query": "spring开发", "operator": "or" } } } } 有时,我们需要设定一个量化的表达方式,例如查询 spring开发基础,这三个词条。我们需求是至少匹配两个词条,这时 operator 属性就不能满足要求了,ES 还提供了另外一个属性:minimum_should_match 用一个百分数来设定应该有多少个词条满足要求。例如查询: “spring开发框架”会被分为三个词:spring、开发、框架 设置"minimum_should_match": "80%"表示,三个词在文档的匹配占比为80%,即3*0.8=2.4,向下取整得2,表 示至少有两个词在文档中要匹配成功。 JavaAPI 通过 matchQuery.minimumShouldMatch 的方式来设置条件 // matchQuery全文检索 searchSourceBuilder.query(QueryBuilders.matchQuery("description", "Spring开发框架").minimumShouldMatch("70%")); 多字段联合搜索 MultiQuery 上面的 MatchQuery 有一个短板,假如用户输入了某关键字,我们在查找的时候并不知道他输入的是 name 还是 description,这时我们用什么都不合适,而 MultiQuery 的出现解决了这个问题,他可以通过 fields 属性来设置多个域联合查找:具体用法如下 { "query": { "multi_match": { "query": "Spring开发", "minimum_should_match": "70%", "fields": ["name", "description"] } } } JavaAPI searchSourceBuilder.query(QueryBuilders.multiMatchQuery("Spring开发框架", "name", "description").minimumShouldMatch("70%")); 提升 boost 在多域联合查询的时候,可以通过 boost 来设置某个域在计算得分时候的比重,比重越高的域当他符合条件时计算的得分越高,相应的该记录也更靠前。通过在 fields 中给相应的字段用 ^权重倍数来实现 "fields": ["name^10", "description"] 上面的代码表示给 name 字段提升十倍权重,查询到的结果: { "_index": "ysx_course", "_type": "doc", "_id": "3", "_score": 13.802518, // 可以清楚的发现,得分竟然是 13 了 "_source": { "name": "spring开发基础", "description": "spring 在java领域非常流行,java程序员都在用。", "studymodel": "201001", "price": 88.6, "timestamp": "2018-02-24 19:11:35", "pic": "group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg" } }, 而在 Java 中,仍然可以通过链式编程来实现 searchSourceBuilder.query(QueryBuilders.multiMatchQuery("Spring开发框架", "name", "description").field("name", 10)); // 设置 name 10倍权重 布尔查询 BoolQuery 如果我们既要对一些字段进行分词查询,同时要对另一些字段进行精确查询,就需要使用布尔查询来实现了。布尔查询对应于Lucene的BooleanQuery查询,实现将多个查询组合起来,有三个可选的参数: must:文档必须匹配must所包括的查询条件,相当于 “AND” should:文档应该匹配should所包括的查询条件其中的一个或多个,相当于 "OR" must_not:文档不能匹配must_not所包括的该查询条件,相当于“NOT” { "query": { "bool": { // 布尔查询 "must": [ // 查询条件 must 表示数组中的查询方式所规定的条件都必须满足 { "multi_match": { "query": "spring框架", "minimum_should_match": "50%", "fields": [ "name^10", "description" ] } }, { "term": { "studymodel": "201001" } } ] } } } JavaAPI // 搜索方式 // 首先构造多关键字查询条件 MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("Spring开发框架", "name", "description").field("name", 10); // 然后构造精确匹配查询条件 TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("studymodel", "201002"); // 组合两个条件,组合方式为 must 全满足 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); boolQueryBuilder.must(matchQueryBuilder); boolQueryBuilder.must(termQueryBuilder); // 将查询条件封装给查询对象 searchSourceBuilder.query(boolQueryBuilder); 过滤器 定义过滤器查询,是在原本查询结果的基础上对数据进行筛选,因此省略了重新计算的分的步骤,效率更高。并且方便缓存。推荐尽量使用过虑器去实现查询或者过虑器和查询共同使用,过滤器在布尔查询中使用,下边是在搜索结果的基础上进行过滤: { "query": { "bool": { "must": [ { "multi_match": { "query": "spring框架", "minimum_should_match": "50%", "fields": [ "name^10", "description" ] } } ], "filter": [ { // 过滤条件:studymodel 必须是 201001 "term": {"studymodel": "201001"} }, { // 过滤条件:价格 >=60 <=100 "range": {"price": {"gte": 60,"lte": 100}} } ] } } } 注意:range和term一次只能对一个Field设置范围过虑。 JavaAPI // 首先构造多关键字查询条件 MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("Spring框架", "name", "description").field("name", 10); // 添加条件到布尔查询 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); boolQueryBuilder.must(matchQueryBuilder); // 通过布尔查询来构造过滤查询 boolQueryBuilder.filter(QueryBuilders.termQuery("studymodel", "201001")); boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(60).lte(100)); // 将查询条件封装给查询对象 searchSourceBuilder.query(boolQueryBuilder); 排序 我们可以在查询的结果上进行二次排序,支持对 keyword、date、float 等类型添加排序,text类型的字段不允许排序。排序使用的 JSON 格式如下: { "query": { "bool": { "filter": [ { "range": { "price": { "gte": 0, "lte": 100 } } } ] } }, "sort": [ // 注意这里排序是写在 query key 的外面的。这就表示它的API也不是布尔查询提供 { "studymodel": "desc" // 对 studymodel(keyword)降序 }, { "price": "asc" // 对 price(double)升序 } ] } 由上面的 JSON 数据可以发现,排序所属的 API 是和 query 评级的,因此在调用 API 时也应该选择对应的 SearchSourceBuilder 对象 // 排序查询 @Test public void testSort() throws IOException, ParseException { // 搜索请求对象 SearchRequest searchRequest = new SearchRequest("ysx_course"); // 指定类型 searchRequest.types("doc"); // 搜索源构建对象 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); // 搜索方式 // 添加条件到布尔查询 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); // 通过布尔查询来构造过滤查询 boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(0).lte(100)); // 将查询条件封装给查询对象 searchSourceBuilder.query(boolQueryBuilder); // 向搜索请求对象中设置搜索源 searchRequest.source(searchSourceBuilder); // 设置排序规则 searchSourceBuilder.sort("studymodel", SortOrder.DESC); // 第一排序规则 searchSourceBuilder.sort("price", SortOrder.ASC); // 第二排序规则 // 执行搜索,向ES发起http请求 SearchResponse searchResponse = client.search(searchRequest); // 搜索结果 SearchHits hits = searchResponse.getHits(); // 匹配到的总记录数 long totalHits = hits.getTotalHits(); // 得到匹配度高的文档 SearchHit[] searchHits = hits.getHits(); // 日期格式化对象 soutData(searchHits); } 高亮显示 高亮显示可以将搜索结果一个或多个字突出显示,以便向用户展示匹配关键字的位置。 高亮三要素:高亮关键字、高亮前缀、高亮后缀 { "query": { "bool": { "must": [ { "multi_match": { "query": "开发框架", "minimum_should_match": "50%", "fields": [ "name^10", "description" ], "type": "best_fields" } } ] } }, "sort": [ { "price": "asc" } ], "highlight": { "pre_tags": [ "" ], "post_tags": [ "" ], "fields": { "name": {}, "description": {} } } } 查询结果的数据如下: 1565585272091 Java 代码如下,注意到上面的 JSON 数据, highlight 和 sort 和 query 依然是同级的,所以也需要用 SearchSourceBuilder 对象来设置到搜索条件中 // 高亮查询 @Test public void testHighLight() throws IOException, ParseException { // 搜索请求对象 SearchRequest searchRequest = new SearchRequest("ysx_course"); // 指定类型 searchRequest.types("doc"); // 搜索源构建对象 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); // 搜索方式 // 首先构造多关键字查询条件 MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery("Spring框架", "name", "description").field("name", 10); // 添加条件到布尔查询 BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); boolQueryBuilder.must(matchQueryBuilder); // 通过布尔查询来构造过滤查询 boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(60).lte(100)); // 将查询条件封装给查询对象 searchSourceBuilder.query(boolQueryBuilder); // *********************** // 高亮查询 HighlightBuilder highlightBuilder = new HighlightBuilder(); highlightBuilder.preTags("<em>"); // 高亮前缀 highlightBuilder.postTags("</em>"); // 高亮后缀 highlightBuilder.fields().add(new HighlightBuilder.Field("name")); // 高亮字段 // 添加高亮查询条件到搜索源 searchSourceBuilder.highlighter(highlightBuilder); // *********************** // 设置源字段过虑,第一个参数结果集包括哪些字段,第二个参数表示结果集不包括哪些字段 searchSourceBuilder.fetchSource(new String[]{"name","studymodel","price","timestamp"},new String[]{}); // 向搜索请求对象中设置搜索源 searchRequest.source(searchSourceBuilder); // 执行搜索,向ES发起http请求 SearchResponse searchResponse = client.search(searchRequest); // 搜索结果 SearchHits hits = searchResponse.getHits(); // 匹配到的总记录数 long totalHits = hits.getTotalHits(); // 得到匹配度高的文档 SearchHit[] searchHits = hits.getHits(); // 日期格式化对象 soutData(searchHits); } 根据查询结果的数据结构来获取高亮的数据,替换原有的数据: private void soutData(SearchHit[] searchHits) throws ParseException { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); for (SearchHit hit : searchHits) { // 文档的主键 String id = hit.getId(); // 源文档内容 Map<String, Object> sourceAsMap = hit.getSourceAsMap(); String name = (String) sourceAsMap.get("name"); // 获取高亮查询的内容。如果存在,则替换原来的name Map<String, HighlightField> highlightFields = hit.getHighlightFields(); if( highlightFields != null ){ HighlightField nameField = highlightFields.get("name"); if(nameField!=null){ Text[] fragments = nameField.getFragments(); StringBuffer stringBuffer = new StringBuffer(); for (Text str : fragments) { stringBuffer.append(str.string()); } name = stringBuffer.toString(); } } // 由于前边设置了源文档字段过虑,这时description是取不到的 String description = (String) sourceAsMap.get("description"); // 学习模式 String studymodel = (String) sourceAsMap.get("studymodel"); // 价格 Double price = (Double) sourceAsMap.get("price"); // 日期 Date timestamp = dateFormat.parse((String) sourceAsMap.get("timestamp")); System.out.println(name); System.out.println(id); System.out.println(studymodel); System.out.println("你看不见我,看不见我~" + description); System.out.println(price); } }

剑曼红尘 2020-04-15 19:21:40 0 浏览量 回答数 0
阿里云大学 云服务器ECS com域名 网站域名whois查询 开发者平台 小程序定制 小程序开发 国内短信套餐包 开发者技术与产品 云数据库 图像识别 开发者问答 阿里云建站 阿里云备案 云市场 万网 阿里云帮助文档 免费套餐 开发者工具 企业信息查询 小程序开发制作 视频内容分析 企业网站制作 视频集锦 代理记账服务 2020阿里巴巴研发效能峰会 企业建站模板 云效成长地图 高端建站