天天学算法——搜索热词关联(TopK)

58同城2016研发笔试题解析:数据结构与算法核心考点 数据结构与算法是研发工程师的核心内功,也是互联网大厂笔试面试中区分度最高的考察模块。从链表反转、二叉树重建到快排边界、二分查找变种,每一类题目都在检验工程师对底层原理的理解深度和代码实现的严谨性。无论技术栈如何变迁,这些基础知识的应用场景始终贯穿于海量数据处理、索引优化、并发控制等真实工程问题中。本文以58同城2016年研发工程师笔试题为样本,逐题拆解题目背后的考察意图与答题思路,并结合进程线程、TCP握手、SQL聚合等高频考点,帮助读者系统梳理知识盲区,为校招或跳槽打下扎实基础。 阅读详情

目录:

  • 《剑指offer》面试题-topk算法
  • 搜索热词关联算法
  • 代码实现以及java学习

写在前面

每次写博客都爱先扯点乱七八糟的东西,这是工作准备写的第2篇博客,之前写过一篇hadoop入门,那里还留下了一个搜索引擎的demo没有去完成,这次学习热词关联刚好也是和搜索引擎相关,所以借此机会把这篇记录下来,一方面花了3天来学习了这个内容,确实学到了不少东西,二来下次写搜索引擎的hadoop的demo时候可以把这个整合到一起,如果有空把关于搜索的东西整合到一起,添加一些爬虫相关的只是内容,就可以简单的搭建一个搜索引擎了,想想还是挺不错的。好啦,我们来开始学习吧!

topK算法

这里写图片描述
这个题目实现不难,在没有什么限制的情况下我们很快能得到答案。

解法1 排序

对数组排序,然后找到最小的k个数字,这个思路和粗暴,实际上我们把问题转化成了排序算法,那么合理的想法就是去找排序中时间复杂度最小的快排(O(nlgn)),这里对于此方法有一个问题就是在于需要改变原数组,如果题目中存在此种限制,自然是需要考虑其他算法。

解法2 partition算法

parition算法,说起这个算法可能对于算法不太熟悉的同学真没什么印象,但是如果说快排大家肯定都知道了。我先贴一段java实现的代码给大家看一看。

//快速排序 虽然快排思想比较简单,但是有些=还是需要注意一下勒,网上不少博客的代码都有点小问题,所以自己写了跑了下才贴出来。
    public static void qsort(int[] arr,int begin,int end) {

        int index = partition(arr, begin,end);

        if(begin >= end -1) {
            return; 
        }

        qsort(arr,begin,index-1);
        qsort(arr,index+1,end);

    }
//用一个哨兵来分割大于小于的两部分
    private static int partition(int[] arr,int begin,int end) {
        if(begin >= end) {
            return -1;
        }

        int pos = arr[begin];
        while(begin < end) {
            while(arr[end] >= pos && begin < end) {

                end --;
            }
            if(begin < end) {
                arr[begin] = arr[end];
            }
            while(arr[begin] <= pos && begin < end) {
                begin ++;
            }
            if(begin < end) {
                arr[end] = arr[begin];
            }
        }

        arr[begin] = pos;
        return begin;
    }

以上代码中有很重要的一块就是partition,很多快排的写法里面没有将其作为单独的一个函数,他的思想就是取出一个哨兵,然后把大于他的放到一边,小于他的放到另一边。这样如果我们按着这个思路,先找到一个数partition一次,判断这个树的最终位置是不是在k处,如果大于则找前面的部分(假设左小右大),如此直到我们找到第k个值的位置,此时k之前的都比k小,就得到了题解。下面我大概举个例子,给大家一个形象的表示。
arr = 4,3,5,9,2,4,6 找到最小的3个值
partition1 2 3 4 9 5 4 6 index = 3 分了一次刚好index返回3,所以最小的是2 3 4,对没毛病!
那我们现在来看一看这个算法的时间复杂度,逆序的时候复杂度最高为O(n^2),如果是随机的话,T(N) = T(T/2) + N,时间复杂度为O(N)。那么我们可以在O(N)的时间复杂度把这个问题给解决了。这比上述排序好多了,因为针对上述排序中,我们每次都要把序列找到一个哨兵然后左右都要去排序,这个时候,我们只处理我们需要处理的部分,时间复杂度就降低了下来。虽然简单,还是画个图表示一下下。如下图,如果我们想要去找前3小的数字时,如果哨兵是5,那么我们就可以不用管后面部分,只需要考虑前面绿色填充的数字,这样节约了很多时间。
这里写图片描述
但是这个算法仍然有点问题,同解法1,这个算法会调整数据,当数据量不断增加时,我们有时候希望能增量式的去处理,而不是每次有数据进来都乾坤大挪移,那么我们需要考虑外部存储来辅助这个算法保证这个原数组不会改变。

解法3 外部存储-小(大)根堆

我们日常也会遇到这样的算法例子,偶尔我们会用一个外部数组来存储,每次进来一个数字就判断。比如我们想找一个数组里面最大的3个数字,我开一个3空间的数组,那么我们遍历一次原数组,找到最大的3个依次放入,每次放入时和外部数组比较一下,这样也可以在O(N)时间内完成,且不改变原数组,好啦。貌似我们对这个算法已经了解的很深入了。
且慢,各位看客想一想,如果这个N非常非常大时候,如果我们要在几百万的数据中找前10,那会有什么不同么。对于算法复杂度来说,O(N)应该是不可避免了,至少每个数字都要遍历到,但是对于大数据处理来说,复杂度中隐藏的时间常熟因子也是十分关键的。我们现在来分析一波,对于外部数组,如果我们是找最大的K个数,那么我们每次需要找到数组中最小的,如果最小我们就要替换,所以会有替换操作。那么对于一个无顺序数组的话,大概O(K)可以完成,然后我们算法整体就是O(K*N),如果我们来维护一个有序数组的话,开销没什么区别。如果熟悉数据结构的同学,现在一定看出问题了,我们需要用堆来完成这些操作,取最小堆可以O(1),来完成,而插入堆也可以在O(lgN)完成(平均),OK,数据量一大时候,这个差异是非常大的,先给大家看一个感性的认识,我没有具体去算时间,只是进行了一下对比,heap为我自己实现的小根堆,orderarr是网上借鉴的别人实现的有序数组。下面应该十分明显了,k小时没有啥区别,k的变大,这个差距会越来越大。

    int n = 3000000;
    int k = 100;
    orderarr程序运行时间: 16ms
    heap程序运行时间: 13ms

    int n = 3000000;
    int k = 100000;
    orderarr程序运行时间: 5137ms
    heap程序运行时间: 59ms

算法不难,此处介绍一下思路即可,晚上有很多介绍堆思路的,算法导论中有heapify来维护堆的,java中的优先队列好像是用shiftup,shitfdown来维护insert操作,个人觉得都可以,思想都是一致的。大家有兴趣可以翻翻我的github,文末给出,我把这些代码都放在里面,有不对之处大家也可以指教。

public static void testHeap(int n,int k) {
        int[] arr = new int[n];
        Random random = new Random();

        for(int i=0;i<arr.length;i++) {
                arr[i] = random.nextInt();
            }

            int length = arr.length;
            MinHeap mHeap = new MinHeap();

            long startTime=System.currentTimeMillis();   //获取开始时间 

            for(int i=0;i<length;i++) {
                if(i<=k-1) {
                    mHeap.insert(arr[i]);
                }else {
                    if(arr[i] > mHeap.getTop()) {
                        mHeap.removeTop();
                        mHeap.insert(arr[i]);
                    }
                }
            }
//      mHeap.show();
        long endTime=System.currentTimeMillis(); //获取结束时间  
        System.out.println("heap程序运行时间: "+(endTime-startTime)+"ms");  

    }

热词搜索提示

现在终于到正题了,之前半天都是在介绍算法,现在也讲讲该算法的应用,现在xx搜索引擎公司需要根据用户的输入来给其他用户做输入提示,那么我们有很多输入词条,现在需要提示热度最高的。这实际就是一个topK问题,额外之处一个是操作对象不在是一个整数,而是一个键值对,另外一个问题是我们需要构建一颗trie树来帮助我们找到需要排序的词语。当然对于日志信息来说,数据是一条一条的,我们还需要用到hash表来帮我们进行统计。

第一步 hashMap统计

对于hash表来说,没有特别要多说的,统计一个大数据量,如果内存够的话,一张hash表无疑是很好的选择,O(n)的时间就可以搞定,当然这个大数据也是有一个限制的,如果上T或者更大,那可能就需要想其他的办法了。G级别的这个还是没问题的。此处我们使用java中的hashMap来完成。

第二步 构建trie树

因为涉及到应用,当输入“北”的时候,希望能提示“北京”,或者“北海”,不能提示“南京”吧,那么我们需要有一颗前缀树来实现,每次找到输入的节点的子树,对子树中的节点遍历,取得最大的K个,为了方便,前缀树结构如下,每个节点放置到当前节点位置的所有字符,并且添加对应频次,路过的词语频次为0,结构图大致如下。
这里写图片描述

第三部 topK算法

topK说的很多了,我们需要改成能针对键值对的就OK啦! ~^_^~

代码实现以及java学习

决策树部分

下面是决策树的实现,决策树中学习到的一个比较重要的点就是需要自己实现一个迭代器,之前的数组可以直接遍历,for循环就可以了,但是树没有这么简单,便需要实现一个iterator来帮助完成遍历。

首先class需要实现Iterable接口,调用x.iterator()返回一个Iterator类,这个类通常含有2个方法,hasnext(),next()。结构如下,具体请查看其他介绍,此处不赘述。
public class MyIteratorClass implements Iterable{

    @Override
    public Iterator iterator() {
        // TODO Auto-generated method stub
        return new MyIterator;
    }
    private class MyIterator implements Iterator<TrieNode>{
        @Override
        //返回是否还有下一个值
        public boolean hasNext() {
            return null;
        }

        @Override
        //返回下一个迭代的值
        public TrieNode next() {
            return null;
        }
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import datastructure.TrieTree.TrieNode;

public class TrieTree implements Iterable{     
    private  TrieNode root;

    public static void main(String[] args) {
        TrieTree trieTree = new TrieTree();
        trieTree.insert("北京动物园", 2);
        trieTree.insert("北京天安门", 3);
        trieTree.insert("北京", 1);
        String word = "北京";
        TrieNode subTree = trieTree.getSubTreeByWord(word);
        Iterator<TrieNode> iterator = trieTree.iterator(subTree);
        while(iterator.hasNext()) {
            TrieNode node = iterator.next();
            System.out.println(node.value + " " + node.count);
        }
        //trieTree.showTrieTree();
    }

    public TrieNode getRoot() {
        return root;
    }

    public TrieTree() {
        root = new TrieNode("root",0);
    }

    public class TrieNode{
        private String value;
        private ArrayList<TrieNode> son;
        private int count; //当前路径上统计数
        public TrieNode() {
            // TODO Auto-generated constructor stub
            this.value = "null";
            this.count = 0;
            this.son = new ArrayList<TrieNode>();
        }

        public TrieNode(String value,int count) {
            // TODO Auto-generated constructor stub
            this.value = value;
            this.count = count;
            this.son = new ArrayList<TrieNode>();
        }

        public String getValue() {
            return value;
        }

        public int getCount() {
            return count;
        }
    }




    //根据输入获取子树
    public TrieNode getSubTreeByWord(String str) {
        return _getSubTreeByWord(root,str);
    }
    private TrieNode _getSubTreeByWord(TrieNode root,String str) {
        int sonNum = root.son == null? 0 :root.son.size();

        if(root.value.equals(str)) {
            return root;
        }

        for(int i=0;i<sonNum;i++) {
            TrieNode node = _getSubTreeByWord(root.son.get(i),str);
            if(node != null) {
                return node;
            }
        }
        return null;
    }

    //插入时,把count放在最后一个节点上
    public void insert(String str,int count) {
        _insertNode(root, str, count ,1);
    }
    private void _insertNode(TrieNode root,String str,int count ,int index) {
        int sonNum = root.son.size();
        int findFlag = 0;


        for(int i=0;i<sonNum;i++) {
            if(root.son.get(i).value.equals(str.substring(0, index))) {
                findFlag = 1;
                if(str.length() == index) {
                    root.son.get(i).count = count;
                    return;
                }else {
                    _insertNode(root.son.get(i), str, count ,index+1);
                }
                break;
            }
        }
        //遍历之后没有找到就创建一个
        if(findFlag == 0) {
        //  System.out.println(str.substring(0, index));
            String newValue = str.substring(0, index);
            int newCount = index != str.length() ? 0 : count;
            TrieNode sonNode = new TrieNode(newValue,newCount);
            root.son.add(sonNode);
            if(str.length() != index) {
                _insertNode(sonNode, str, count ,index+1);
            }
        }
    }

    //循环遍历输出字典树内容
    public void showTrieTree() {
        _showTrieTree(root);
    }
    private void _showTrieTree(TrieNode root) {
        System.out.println(root.value + root.count);
        int sonNum = root.son.size();
        for(int i=0;i<sonNum;i++) {
            _showTrieTree(root.son.get(i));
        }
    }


    @Override
    public Iterator<TrieNode> iterator() {
        // TODO Auto-generated method stub
        return new TrieTreeIterator();
    }


    public Iterator<TrieNode> iterator(TrieNode itrRoot) {
        // TODO Auto-generated method stub
        return new TrieTreeIterator(itrRoot);
    }

    private class TrieTreeIterator implements Iterator<TrieNode>{
        private TrieNode next;
        private  Queue<TrieNode> queue;

        public TrieTreeIterator() {
            // TODO Auto-generated constructor stub
            next = root;
            queue = new LinkedList<TrieNode>();
            if(next == null) {
                return;
            }
        }

        public TrieTreeIterator(TrieNode itrRoot) {
            // TODO Auto-generated constructor stub
            next = itrRoot;
            queue = new LinkedList<TrieNode>();
            if(next == null) {
                return;
            }
        }

        @Override
        public boolean hasNext() {
            // TODO Auto-generated method stub
            int sonNum = next.son.size();
            for(int i=0;i<sonNum;i++) {
                queue.add(next.son.get(i));
            }
            if(queue.isEmpty()) {
                return false;
            }else {
                return true;
            }

        }

        @Override
        public TrieNode next() {
            // TODO Auto-generated method stub

            next = queue.remove();
            return next;
        }

    }

}  

Heap部分

此处借鉴了网友的代码,但是不记得哪里抄来的了,抱歉。
这里学到的比较重要的东西就是泛型的使用,对于这份代码来说,是适用与int的,但是我想拿这份代码来做键值对的处理,利用泛型和提供的Comparator就可以很方便的实现代码的复用。此处我定义了键值对的类型,提供了一些基础方法。然后出现了另外一个重要的问题,那就是关于对象复制的问题,这里学习了深克隆的方式,如果setRoot方法不传入clone(),则只是传入了索引,而不是对堆内进行赋值,这样逻辑上有误。所以这里传入一定是clone的内容。关于clone有深浅之分,这里我使用了序列化的方式,下面这博客写的不错,推一推。
克隆学习:https://www.cnblogs.com/Qian123/p/5710533.html

//代码复用可以学习的地方

//这是之前的代码
int[] arr = new int[n]
//此处省略,很多,这里重点在于描述差异之处
mHeap.setRoot(arr[i]);

//这是修改后的代码
KeyPair<String, Integer> tempKeyPair = new KeyPair<String, Integer>("", 0);
mHeap.setRoot(tempKeyPair.clone());
//建立heap来做键值对比较
Comparator<KeyPair<String, Integer>> comp = new Comparator<KeyPair<String, Integer>>() {  
            @Override
            public int compare(KeyPair<String, Integer> o1, KeyPair<String, Integer> o2) {
                // TODO Auto-generated method stub
                return o2.getValue() - o1.getValue();
            }  
        };  
//克隆可以学习的部分,序列化方式克隆。

public class KeyPair<K, V> implements Serializable {
    private K key;
    private V value;

    public KeyPair clone() {
            KeyPair outer = null;
            try { // 将该对象序列化成流,因为写在流里的是对象的一个拷贝,而原对象仍然存在于JVM里面。所以利用这个特性可以实现对象的深拷贝
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(this);
                // 将流序列化成对象
                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
                ObjectInputStream ois = new ObjectInputStream(bais);
                outer = (KeyPair) ois.readObject();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            return outer;
          }
    }

public class Heap<T> {  

    /** 
     * 以数组形式存储堆元素 
     */  
    private T[] heap;  

    /** 
     * 用于比较堆中的元素。c.compare(根,叶子) > 0。  
     * 使用相反的Comparator可以创建最大堆、最小堆。 
     */  
    private Comparator<T> c;  

    public Heap(T[] a, Comparator<T> c) {  
        this.heap = a.clone();  
        this.c = c;  
        buildHeap();  
    }  

    /** 
     * 返回值为i/2 
     *  
     * @param i 
     * @return 
     */  
    private int parent(int i) {  
        return (i - 1) >> 1;  
    }  

    /** 
         * 
     * 返回指定节点的left子节点数组索引。相当于2*(i+1)-1 
         * 
     *  
     * @param i 
     * @return 
     */  
    private int left(int i) {  
        return ((i + 1) << 1) - 1;  
    }  

    /** 
     * 返回指定节点的right子节点数组索引。相当于2*(i+1) 
     *  
     * @param i 
     * @return 
     */  
    private int right(int i) {  
        return (i + 1) << 1;  
    }  

    /** 
     * 堆化 
     *  
     * @param i 
     *           堆化的起始节点 
     */  
    private void heapify(int i) {  
        heapify(i, heap.length);  
    }  

    /** 
     * 堆化, 
     *  
     * @param i 
     * @param size 堆化的范围 
     */  
    private void heapify(int i, int size) {  
        int l = left(i);  
        int r = right(i);  
        int next = i;  
        if (l < size && c.compare(heap[l], heap[i]) > 0)  
            next = l;  
        if (r < size && c.compare(heap[r], heap[next]) > 0)  
            next = r;  
        if (i == next)  
            return;  
        swap(i, next);  
        heapify(next, size);  
    }  


    /** 
     * 对堆进行排序 
     */  
    public void sort() {  
        // buildHeap();  
        for (int i = heap.length - 1; i > 0; i--) {  
            swap(0, i);  
            heapify(0, i);  
        }  
    }  

    /** 
     * 交换数组值 
     *  
     * @param arr 
     * @param i 
     * @param j 
     */  
    private void swap(int i, int j) {  
        T tmp = heap[i];  
        heap[i] = heap[j];  
        heap[j] = tmp;  
    }  

    /** 
     * 创建堆 
     */  
    private void buildHeap() {  
        for (int i = (heap.length) / 2 - 1; i >= 0; i--) {  
            heapify(i);  
        }  
    }  

    public void setRoot(T root) {  
        heap[0] = root;  
        heapify(0);  
    }  

    public T root() {  
        return heap[0];  
    }  

    public T getByIndex(int i) {
        if(i<heap.length) {
            return heap[i];
        }
        return null;
    }
}

总结

这里写图片描述

这里写图片描述

上面贴出了测试数据和最终结果,以上代码估计跑不通,贴出来是为了突出重点,如果想看跑,可以follow我的github,上面有完整实例,另外还有一些问题没有解决完全,一个是大数据下的测试,另外有一个就是交互问题,因为考虑到后面还会写相关搜索引擎的板块,所以这里先不急着完善,以后有机会再做交互和完整测试。

哈,写了3个多小时,终于总结完了~,看完有收货的小伙伴,记得赞一个噢~。我们一起天天学算法~

github:https://github.com/Continue7777/algorithms

java使用Nagao算法实现新词发现、热门词的挖掘 主要介绍了java使用Nagao算法实现新词发现、热门词的挖掘的思路和详细代码,需要的朋友可以参考下 立即下载

相关推荐

PostgreSQL隐式类型转换全解析:告别operator does not exist

强类型数据库是现代数据系统的基石,它在带来严谨性的同时,也常常让开发者在类型不匹配报错前束手无策。当一条SQL因bigint与text比较而抛出operator does not exist时,根源往往在于对数据库隐式转换规则的误解。PostgreSQL通过pg_cast系统表将转换划分为隐式、赋值与显式三个级别,并依据操作符解析逻辑决定是否自动提升类型。理解这些机制,不仅能快速定位JOIN、UNION、函数调用等场景下的类型错误,还能帮助开发者合理使用EXPLAIN VERBOSE、pg_typeof等工

weixin_29587015的博客 304

《MySQL数据库》关联查询

有的时候,我们要查询的数据,一个简单的查询语句完成不了,并且我们使用的数据,表中不能直观体现出来。自连接是一种特殊的关联查询。在做关联查询时,我们所需要的数据,除了那些满足关联条件的数据外,还有不满足关联条件的数据。from子句用于指定表,如果想在一个子查询的结果里继续查询,则子查询需要写在from子句中,相当于一个表。需求1:查询所有员工的姓名,职位,及其部门编号,部门名称----分析:员工表为驱动表,部门表为从表。关联查询所涉及到的表与表之间都会存在有关联的字段,如员工表的部门编号和部门表的部门编号。

关注我!带你一路 "狂飙" 到底! 5965

Stata森林图进阶:个性化定制与高级技巧

本文详细介绍了Stata森林图的进阶技巧,包括个性化定制与高级应用。通过meta分析工具如metan命令,用户可以优化字体、颜色、坐标轴等视觉元素,提升森林图的专业性和可读性。文章还提供了黑白印刷设置、高分辨率导出等实用技巧,帮助科研人员制作符合学术出版要求的森林图。

ios99的博客 254

华为机试2022.4.6:查找舆情热词

100分的题目,但很麻烦,因为题目就很长。 查找舆情热词 题目描述: 网上新闻越来越多,希望对新闻进行热词处理并归类,方便获取信息,现在已经将每篇文章处理为2个字符串,即一个标题,一个正文串,字符串中使用“ ”作为分隔符进行分词。 M篇新闻按照新闻发布的先后顺序处理完并输入,现在希望对所有新闻中出现的词语进行处理,输出出现频率最高的topN个词语,作为热词。标题中出现的词语频率系数为3,正文中出现的词语频率系数为1;返回的答案按照词语出现频率由高到低排序,当词语出现的频率相同时,在标题中出现的频率次数高的排

小朱小朱绝不认输的博客 3207

BQ4050开发进阶(二):Protections参数优化策略与实战解析

本文深入解析了BQ4050电池管理芯片中Protections参数的优化策略与实战方法。针对电压、电流、温度等核心保护功能,详细阐述了如何根据具体电芯特性、负载场景和环境因素,精细调整CUV、COV、OCC/OCD等阈值与延时,以在确保电池绝对安全的前提下,实现极致的用户体验与系统可靠性。

rgv234567的博客 480

Marin说PCB之LPDDR5的布线注意事项--03

这个规则中是把每个字节中的WCK to RDQS 以及DM / DQ to WCK等长规则都加到了DM / DQ这个等长规则中去了,因为芯片手册上的设计要求中的DM / DQ这个group的等长误差是+-50MIL, WCK to RDQS的等长group的规则是62.5MIL,DM / DQ to WCK等长group的规则是相对松一些是150MIL。其实上面的等长规则还是建少了一些的,例如手册上要求的每个channel的WCK0:1,WCK2:3等长误差在+-488MIL,这个规则建议还是补上去吧。

weixin_45223454的博客 1229

热词的识别与提取算法

1.什么是热词 热词特点: 1.某一时间段内起点低,增长迅速 2.具有很强的延续性,和热词相关的其他词也被报道 3.热度随着时间的流逝而改变 2.热词排名计算的两种方法 热词排名方法一:贝叶斯平均法(Bayesian average) 先来理解一下利用贝叶斯统计算法的公式: WR是每个词的加权得分,WR越大表示热度越大 R是该词汇的平均得分(这里设定都为1) v是...

lionzl的专栏 9947

基于强化学习框架的交易策略学习过程

每个状态s∈S 是一个由六个微观结构特征构成的向量,这些特征反映了当前市场的订单簿状态和近期交易行为。

xu624735206的博客 1110

TopK问题详解

Top-K问题是一类算法和数据处理问题,其中任务是从一个包含大量数据项的集合中找到前K个最重要或最高排名的元素。搜索引擎:在搜索引擎中,Top-K问题可以用于返回用户查询的前K个最相关的搜索结果。推荐系统:在电子商务网站或媒体流推荐中,可以使用Top-K问题来提供用户最感兴趣的产品或内容。数据分析:在大数据分析中,Top-K问题可用于查找最频繁出现的元素或最高价值的数据点。数据挖掘:在聚类和分类问题中,可以使用Top-K问题来选择具有最高重要性的特征或数据点。

Forward♞的博客 4902

最优化学习笔记1——关于拟牛顿法推导

从2020年3月份到现在,一年的时间里断断续续自学最优化,结合豆瓣读书、知乎等网站上的推荐,翻阅了以下书和课程: Stephen Boyd和Lieven Vandenberghe的《Convex Optimization》,这本书被公认为学习凸优化的必读经典著作,其中大量的篇幅讲应用,讲理论和算法的篇幅稍少一些。在B网站上有Boyd的视频课程,油管上也有,不过只有英文字幕,如果是零基础接触,直接看Boyd的视频稍显费劲,B站上也有凌青老师以《Convex Optimization》为教材的讲解,推荐作为入

xiaofei473的博客 1768

在线考试题库抓取:Python 爬虫抓取各类在线考试平台题库和试题

通过这些技术,您可以抓取各类在线考试平台上的题库数据,进行数据分析、生成练习题集,甚至用于考试准备。为了演示抓取过程,本篇博客选择了两个常见的在线考试平台进行数据抓取:一个是教育考试平台(如“中小学题库”),另一个是职业考试平台(如“华图教育”)。对于这些网站,我们需要找到包含题库信息的页面,并分析每一页的 HTML 结构,从而定位到题目、选项、答案等数据。大多数在线考试平台的题库是分页显示的,因此我们需要遍历多个页面,抓取所有的题库数据。因此,我们需要设置合适的请求头来模拟真实的浏览器请求。

2201_76125261的博客 3431

[CUDA 学习笔记] 百度 STI2 赛题二-基于向量交集的 TopK 搜索 优化学习笔记

给定850万条规模的数据文件,每条数据是最大128维度的整型id向量 (称为doc),id取值范围是0-50000,给定一个最大128维的整型id向量(称为query),求query与doc全集内各数据的交集个数topk(k=100)topk_v0_base.cu 为赛题的 Baseline 实现. 该实现整体思路比较简单, 首先是主机端内存分配等预处理, 然后在 GPU 上进行求交集, 最后在主机端根据求交集的分数降序排序得到最终结果.这个 Baseline 主要值得一提的是其 docs 的 swizzl

LostUnravel的博客 1050

第一节:Vben Admin介绍和初次运行

本文是【实战Vben Admin + Python】的第一节,本文内容主要是介绍Vben Admin框架和我们后端使用的技术,开发环境和工具的准备本节文章的目的是Vben Admin的简单介绍,通过配置可以让项目前端运行起来,由于项目默认开启了mock,展示的都是静态模拟数据,我们在此基础上,下一节将研究分析下Vben Admin的目录。为我们后端搭建准备。

cuiwin的专栏 7410

网易文本挖掘算法岗笔试:KMP、排序与机器学习考点全解析

在算法工程师的校招笔试中,数据结构与机器学习基础始终是核心考察点。字符串匹配作为文本处理的基本操作,KMP算法利用next数组避免重复匹配,将时间复杂度从O(mn)优化至O(m+n),是敏感词过滤、搜索引擎等场景的关键技术。排序算法同样重要,面对海量数据时,堆排序、快速排序的选择体现了工程师对场景的理解。此外,机器学习算法如K-means聚类、KNN分类以及BM25相关性打分,共同构成了文本挖掘的技术底座。理解这些算法的原理、适用场景与工程权衡,是应对大厂笔试、提升实战能力的关键。本文以网易校招文本挖掘算法

weixin_30235225的博客 399

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

秋招算法笔试备战:瓜子二手车2019笔试卷2全解析

算法岗秋招笔试中,数据结构与经典算法是高频考察点,KMP字符串匹配、动态规划背包问题、排序稳定性分析等技术原理,直接关系到候选人能否在有限时间内高效解题。这些算法不仅是在线编程的基础,也广泛应用于二手车价格预测、推荐系统、图像审核等真实业务场景。掌握其实现原理与复杂度边界,有助于提升代码质量与面试通过率。本文以瓜子二手车2019秋招算法笔试卷2为样本,系统拆解了字符串算法、经典算法思想及机器学习高频概念,并给出在线OJ实战技巧,为算法岗候选人提供可落地的备考思路。

weixin_29219539的博客 311

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

云从科技算法笔试题全解析:从KMP到动态规划的考点与代码

算法笔试是AI算法岗求职中绕不开的关卡,它既考察数据结构、字符串匹配、动态规划等基础算法功底,也检验对机器学习、深度学习原理的理解深度。KMP算法作为线性时间复杂度的模式匹配方法,在笔试中常以next数组计算、循环节判断等变形出现;动态规划则通过编辑距离、背包问题等经典模型,重点考察边界条件处理与状态转移建模。扎实掌握这些核心算法,能帮助求职者在实战中快速分析问题、优化复杂度。本文结合云从科技算法岗笔试的题型结构与高频考点,逐一拆解KMP、排序搜索、损失函数推导、图论最短路等关键内容,并提供可直接上手的参考

weixin_28704565的博客 223

Rust构建Windows原生Gemini CLI:集成Nushell与thinkingConfig实战

Gemini API 是 Google 提供的大语言模型服务接口,其核心能力依赖于稳定、低延迟、结构化强的客户端实现。在 Windows 平台,传统 Python/Node.js 客户端常因 SSL 证书链、流式响应解析、进程管道语义不一致等问题失效;而 Rust 凭借内存安全、零运行时依赖、MSVC 工具链深度适配等特性,成为构建高可靠性 Gemini 本地客户端的理想选择。结合 Nushell 的表格化管道和 Gemini 3.0 Pro 的 thinkingConfig 高级推理控制,可实现命令行级

weixin_34335458的博客 504

Spring AI 2.0 抽象升级:构建可替换、可观测的AI能力层

在大模型应用工程化进程中,‘模型即服务’已从简单调用演进为需兼顾可迁移性、可观测性与厂商解耦的系统能力。Spring AI 2.0 通过契约化抽象(ChatModel/EmbeddingModel)、分层选项(ModelOptions/ClientOptions)和依赖隔离机制,将LLM接入从‘适配器拼凑’升级为标准化工程实践。其技术价值在于支撑RAG、Agent等复杂场景下的多模型协同与动态治理,避免因API限流、版本冲突或埋点缺失导致的生产事故。本文围绕spring ai 2.0与智谱等国产模型的合规集

weixin_33717117的博客 481

用Python情感分析揭开“双生火焰能量检测”的文本玄机

在情感社群与短视频评论中,“双生火焰”“能量回升”“断联期”等话语反复出现,构成高度模式化的情绪叙事。从自然语言处理视角看,这些现象其实是一类典型的中文主观文本分析场景。通过Python的snownlp、jieba等工具,可以将“阳性方/阴性方”的自述文本转化为可量化的情感得分与关键词频率,再利用pandas完成分组对比,用matplotlib呈现情绪趋势。这种工程化方法不仅适用于灵性话题,也能迁移到情感社区舆情监测、用户自述文本分类、产品评论洞察等实际业务中。文章以“双生火焰能量检测”为案例,完整演示了文

weixin_31292729的博客 307

面试中常问的TOPK

前言:本文将介绍随机选择,分治法,减治法的思想,以及TopK问题优化的来龙去脉,原理与细节,保证有收获。   面试中,TopK,是问得比较多的几个问题之一,到底有几种方法,这些方案里蕴含的优化思路究竟是怎么样的,今天和大家聊一聊。 画外音:除非校招,我在面试过程中从不问TopK这个问题,默认大家都知道。   问题描述: 从arr[1, n]这n个数中,找出最大的k个数,这就是经典的...

F & Q的专栏 2496

【推搜】embedding评估 | faiss的top-k向量检索

在推荐系统的召回阶段中,很多时候item变化不会很明显,所以一般会将item embedding存入faiss之类的向量数据库(也可以使用HnswLib等,注意要建立对应的向量索引),然后在线计算更新user embedding(可由用户的历史item序列的item embedding的mean pooling操作生成,也可以用其他方法),最后将user embedding和item embedding进行最近邻的topk查找。即典型的【离线存储】和【线上召回】策略。最初的双塔模型就是这么做的。

发现问题,并解决问题,批判性思维 2407

TopK问题(快排变形/堆/二叉搜索树/计数排序)leetcode347

1. TopK问题描述 TopK问题,不管是求前K大/前K小/第K大/第K小等,都有4种不错的方法喔: 1. O(N):用快排变形最最最高效解决TopK问题 2. O(NlogK):大根堆(前K小)/小根堆(前K大) 3. O(NlogK):二叉搜索树 4. O(N): 对于数据范围有限的情况例如本题,可以直接计数排序O(N)高效解决 1. 快排变形 用快排变形最最最高效解决TopK问题 O(N) 先通过快排切分排好第K小的数,根据快排切分的性质,它左边的K - 1个数都小于等于它,因此它以

5lovetutu的专栏 791

Beam Search搜索和topk采样优缺点

seq2seq任务中两种搜索算法的优缺点

knight的博客 2635

搜索引擎的查询意图识别(query理解)

by 张帆(自然语言处理组@搜狗) 通用搜索VS垂直搜索         通用搜索特点:                 抓取互联网上一切有价值的页面,同意建立索引,以关键字匹配为基本检索方式,以网页title和summary为展现方式                 google, 百度,搜狗,搜搜,有道         垂直搜索特点:                 以一特定类

zhoubl668的专栏:远帆,梦之帆! 2万+

类似百度搜索框关联查询

于关联查询,找了很多方法,可都不尽人意,最后参考了好心网友提供的代码加上自己的实际需求,于此记录。 首先,html &lt;input type="search" value="" name="curOrganName" placeholder="请填写" id="cl_obj" autocomplete="off" class="inputs paddingle

crystalHy的博客 1875

android搜索的关联性判断,百度聚合搜索对于相关性搜索结果页面是如何判断的分享!...

前段时间对于百度推出的《不相关静态搜索结果页将被严厉处理》大概内容如下:百度网页搜索反作弊团队近期发现一部分网站遍历热门关键词生成大量的站内搜索结果页来获取搜索引擎流量,其中存在大量的不相关内容严重损害了搜索引擎的用户体验并且侵占了相应领域的优质网站收益,对于此类网站我们将做出严厉的处理,希望存在此问题的站长及时进行整改调整。搜索结果页面,也就是网站站内的聚合搜索结果展示,不少SEO对于聚合搜索骗...

weixin_39725756的博客 245
上一篇: hadoop 超详细入门wordcount
下一篇: HMM超详细讲解+代码
continueOo
博客等级 码龄12年 90粉丝 29原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值