【快速选择】解决TopK问题

目录

一、什么是TopK问题

二、优先级队列

优先级队列介绍

代码实现

三、使用优先级队列解决TopK问题

四、快速选择算法解决TopK问题

快速选择

图解快速选择

代码解决前k小个元素

五、优先级队列与快速选则算法比较

优先级队列

快速选择


一、什么是TopK问题

TopK问题就是在一个数组中寻找出最小(大)的前K个数,或者寻找出第K大(小)的数

常见TopK问题图示

常见TopK问题链接

最小的K个数_牛客题霸_牛客网给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是。题目来自【牛客题霸】icon-default.png?t=N7T8https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf?tpId=295&tqId=23263&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj

寻找第K大_牛客题霸_牛客网

. - 力扣(LeetCode)

二、优先级队列

优先级队列介绍

优先级队列是一种数据结构,它根据元素的优先级来决定元素的插入和删除顺序。以下是一些关键特点:

  1. 基于优先级排序:每个元素在队列中都有一个优先级,优先级最高的元素会首先被移除。
  2. 使用特定数据结构:优先级队列通常可以使用堆(尤其是二叉堆)这种数据结构来实现,以保持队列的有序状态并高效地处理插入和删除操作。
  3. 应用广泛:它在很多算法中有广泛应用,如任务调度、最短路径寻找、事件驱动模拟等场合。
  4. 自定义比较:可以通过仿函数或运算符重载来支持自定义的数据类型和比较函数,从而定义元素的优先级。
  5. Java实现:在Java中,优先级队列可以通过最小堆来实现,其中的元素按照自然顺序或者根据提供的Comparator来确定优先级。
  6. 操作方法:主要操作包括插入元素(入队)和删除最高优先级的元素(出队)。
  7. 性能优化:由于内部结构的优化,即使在大量元素的情况下,优先级队列依然能够快速地确定下一个要处理的元素。

总的来说,优先级队列提供了一种有效的方式来管理那些需要按照特定顺序处理的元素,是许多复杂算法和系统设计中不可或缺的工具。

优先级队列博客

【数据结构】优先级队列(堆)与PriorityQueue_unsortedpriorityqueue增加复杂度为1的getmax-CSDN博客文章浏览阅读504次,点赞2次,收藏2次。比如一组数据 1 2 3 4 5 6,我们将他创建成一个大根堆时,我们先从最后一个位置所在的树开始进行调整,由于我们能获取到数组的长度,且根据堆的性质父亲节点的下标 * 2 + 1就孩子节点下标可知,知道孩子的下标(数组长度-1)就能知道父亲的下标。比如我们在大根堆 4 3 2 2 1 1的堆里插入11,先将11存在最后,然后拿他与父亲比较,如果大就交换,如果不比父亲大那他就是大根堆不需要调整,此时交换后,要继续更换父亲与儿子的位置重复比较交换操作,直到孩子下标小于或者等于0时不在需要调整。_unsortedpriorityqueue增加复杂度为1的getmaxhttps://blog.csdn.net/qq_61903414/article/details/128423670?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522170929235016777224499491%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=170929235016777224499491&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-128423670-null-null.nonecase&utm_term=%E4%BC%98%E5%85%88%E7%BA%A7%E9%98%9F%E5%88%97&spm=1018.2226.3001.4450

代码实现
import java.util.Arrays;/*** @author 1886**/
public class PriorityQueue <T extends Comparable<T>> {// 优先级队列底层模拟堆的数组private Object[] queue;// 记录当前优先级队列元素个数private int size;private static final int DEFAULT_INITIAL_CAPACITY = 11;/*** 默认构造方法*/public PriorityQueue() {this.queue = new Object[DEFAULT_INITIAL_CAPACITY];}/*** 指定大小* @param capacity*/public PriorityQueue(int capacity) {this.queue = new Object[capacity];}/*** 传入指定数组* @param queue*/public PriorityQueue(T[] queue) {// 1.初始化成员变量this.queue = queue;this.size = queue.length;// 2.将数组进行大根堆调整HeapIfy();}
}
/*** @author 1886**/
public class PriorityQueue <T extends Comparable<T>> {// 优先级队列底层模拟堆的数组private Object[] queue;// 记录当前优先级队列元素个数private int size;private static final int DEFAULT_INITIAL_CAPACITY = 11;/*** 默认构造方法*/public PriorityQueue() {this.queue = new Object[DEFAULT_INITIAL_CAPACITY];}/*** 指定大小* @param capacity*/public PriorityQueue(int capacity) {this.queue = new Object[capacity];}/*** 传入指定数组* @param queue*/public PriorityQueue(T[] queue) {// 1.初始化成员变量this.queue = queue;this.size = queue.length;// 2.将数组进行大根堆调整heapIfy();}/*** 堆化*/private void heapIfy() {for (int parent = (this.size - 1 - 1) >> 1; parent >= 0; parent--) {siftDown(parent, this.size);}}/*** 向下调整* @param parent 父亲节点下标* @param len    向下调整的结束位置*/private void siftDown(int parent, int len) {// 计算出孩子节点位置int child = parent * 2 + 1;// 开始向下调整while (child < len) {// 寻找出两个孩子节点中最大的if (child + 1 < len && ((T)queue[child]).compareTo(((T)queue[child + 1])) < 0) {child++;}// 与父亲节点进行判断,如果孩子节点比父亲节点大就进行调整 否则调整完毕if (((T)queue[child]).compareTo(((T)queue[parent])) < 0) {break;} else {T tmp = (T)queue[child];queue[child] = (T)queue[parent];queue[parent] = tmp;parent = child;child = parent * 2 + 1;}}}
}
import java.util.Arrays;/*** @author 1886**/
public class PriorityQueue <T extends Comparable<T>> {// 优先级队列底层模拟堆的数组private Object[] queue;// 记录当前优先级队列元素个数private int size;private static final int DEFAULT_INITIAL_CAPACITY = 11;/*** 默认构造方法*/public PriorityQueue() {this.queue = new Object[DEFAULT_INITIAL_CAPACITY];}/*** 指定大小* @param capacity*/public PriorityQueue(int capacity) {this.queue = new Object[capacity];}/*** 传入指定数组* @param queue*/public PriorityQueue(T[] queue) {// 1.初始化成员变量this.queue = queue;this.size = queue.length;// 2.将数组进行大根堆调整heapIfy();}/*** 入队操作* @param data 进入优先级队列的数据*/public void offer(T data) {// 如果优先级队列满 进行扩容if (this.size == this.queue.length) {grow();}// 将元素放到末尾this.queue[this.size] = data;// 进行向上调整siftUp(this.size++);}/*** 出队操作* @return*/public T poll() {// 如果为空就返回空if (this.size == 0) return null;// 将堆顶元素与最后一个位置进行交换T top = (T)queue[0];queue[0] = this.queue[this.size - 1];queue[this.size - 1] = top;// 删除元素this.size--;// 向下调整siftDown(0, this.size);// 返回return top;}/*** 排序*/public void sort() {int len = this.size - 1;while (len >= 0) {T tt = (T) queue[0];queue[0] = queue[len];queue[len] = tt;siftDown(0, len--);}}/*** 堆化*/private void heapIfy() {for (int parent = (this.size - 1 - 1) >> 1; parent >= 0; parent--) {siftDown(parent, this.size);}}/*** 向上调整* @param child*/private void siftUp(int child) {// 计算出父亲节点的下标int parent = (child - 1) / 2;// 开始向上调整while (child > 0) {if (((T)queue[child]).compareTo((T)queue[parent]) >= 0) {T tmp = (T)queue[child];queue[child] = (T)queue[parent];queue[parent] = tmp;child = parent;parent = (child - 1) / 2;} else {break;}}}/*** 向下调整* @param parent 父亲节点下标* @param len    向下调整的结束位置*/private void siftDown(int parent, int len) {// 计算出孩子节点位置int child = parent * 2 + 1;// 开始向下调整while (child < len) {// 寻找出两个孩子节点中最大的if (child + 1 < len && ((T)queue[child]).compareTo(((T)queue[child + 1])) < 0) {child++;}// 与父亲节点进行判断,如果孩子节点比父亲节点大就进行调整 否则调整完毕if (((T)queue[child]).compareTo(((T)queue[parent])) < 0) {break;} else {T tmp = (T)queue[child];queue[child] = (T)queue[parent];queue[parent] = tmp;parent = child;child = parent * 2 + 1;}}}/*** 扩容方法*/private void grow() {this.queue = Arrays.copyOf(this.queue, this.queue.length * 2);}private void display() {for (Object x : this.queue) {System.out.println(x);}}
}

三、使用优先级队列解决TopK问题

从上面优先级队列的介绍可知道,我们只需要创建出一个大小为K的优先级队列。当我们求前K的最小的元素时,我们只需要创建出一个大小为K的大根堆。首先让数组中k个元素进入堆中,然后从k+1开始遍历数组,在遍历过程中与堆顶的元素进行比较,这个时候堆顶的元素一定是这个堆中最大的一个,如果此时的值比堆顶的元素小就让这个元素替换堆顶的元素,依次往复,在遍历完整个数组后,这个堆中就会存储前K个最小元素。相反如果要寻找出最小的前K个数就创建大根堆重复操作

前K小的元素

public ArrayList<Integer> GetLeastNumbers_Solution (int[] input, int k) {// write code hereArrayList<Integer> ret = new ArrayList<>();if (k <= 0 || input == null) return ret;PriorityQueue<Integer> queue = new PriorityQueue<>(k, (o1, o2)->{return o2 - o1;});for (int i = 0; i < input.length; i++) {if (i < k) queue.offer(input[i]);else {if (input[i] < queue.peek()) {queue.poll();queue.offer(input[i]);}}}while (!queue.isEmpty()) {ret.add(queue.poll());}return ret;}
import java.util.*;/*** @author 1886**/public class Solution {static class PriorityQueue <T extends Comparable<T>> {// 优先级队列底层模拟堆的数组private Object[] queue;// 记录当前优先级队列元素个数private int size;private static final int DEFAULT_INITIAL_CAPACITY = 11;/*** 默认构造方法*/public PriorityQueue() {this.queue = new Object[DEFAULT_INITIAL_CAPACITY];}/*** 指定大小* @param capacity*/public PriorityQueue(int capacity) {this.queue = new Object[capacity];}/*** 传入指定数组* @param queue*/public PriorityQueue(T[] queue) {// 1.初始化成员变量this.queue = queue;this.size = queue.length;// 2.将数组进行大根堆调整heapIfy();}/*** 入队操作* @param data 进入优先级队列的数据*/public void offer(T data) {// 如果优先级队列满 进行扩容if (this.size == this.queue.length) {grow();}// 将元素放到末尾this.queue[this.size] = data;// 进行向上调整siftUp(this.size++);}/*** 出队操作* @return*/public T poll() {// 如果为空就返回空if (this.size == 0) return null;// 将堆顶元素与最后一个位置进行交换T top = (T)queue[0];queue[0] = this.queue[this.size - 1];queue[this.size - 1] = top;// 删除元素this.size--;// 向下调整siftDown(0, this.size);// 返回return top;}/*** 排序*/public void sort() {int len = this.size - 1;while (len >= 0) {T tt = (T) queue[0];queue[0] = queue[len];queue[len] = tt;siftDown(0, len--);}}/*** 堆化*/private void heapIfy() {for (int parent = (this.size - 1 - 1) >> 1; parent >= 0; parent--) {siftDown(parent, this.size);}}/*** 向上调整* @param child*/private void siftUp(int child) {// 计算出父亲节点的下标int parent = (child - 1) / 2;// 开始向上调整while (child > 0) {if (((T)queue[child]).compareTo((T)queue[parent]) >= 0) {T tmp = (T)queue[child];queue[child] = (T)queue[parent];queue[parent] = tmp;child = parent;parent = (child - 1) / 2;} else {break;}}}/*** 向下调整* @param parent 父亲节点下标* @param len    向下调整的结束位置*/private void siftDown(int parent, int len) {// 计算出孩子节点位置int child = parent * 2 + 1;// 开始向下调整while (child < len) {// 寻找出两个孩子节点中最大的if (child + 1 < len && ((T)queue[child]).compareTo(((T)queue[child + 1])) < 0) {child++;}// 与父亲节点进行判断,如果孩子节点比父亲节点大就进行调整 否则调整完毕if (((T)queue[child]).compareTo(((T)queue[parent])) < 0) {break;} else {T tmp = (T)queue[child];queue[child] = (T)queue[parent];queue[parent] = tmp;parent = child;child = parent * 2 + 1;}}}public T peek() {return this.size == 0 ? null : (T)queue[0];}/*** 扩容方法*/private void grow() {this.queue = Arrays.copyOf(this.queue, this.queue.length * 2);}private void display() {for (Object x : this.queue) {System.out.println(x);}}
}/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param input int整型一维数组 * @param k int整型 * @return int整型ArrayList*/public ArrayList<Integer> GetLeastNumbers_Solution (int[] input, int k) {// write code hereArrayList<Integer> ans = new ArrayList<>();if (k <= 0 || input == null) return ans;PriorityQueue<Integer> queue = new PriorityQueue<>(k);for (int i = 0; i < input.length; i++) {if (i < k) queue.offer(input[i]);else if (input[i] < queue.peek()) {queue.poll();queue.offer(input[i]);}}while (queue.peek() != null) {ans.add(queue.poll());}return ans;}
}

四、快速选择算法解决TopK问题

快速选择

快速选择算法(Quickselect)是用于在未排序的数组中查找第k小或第k大元素的高效算法,它的时间复杂度为O(n)。该算法与快速排序有密切的联系,但它不对整个数组进行完整的排序,而是只关注于找到所需的特定顺序的元素。以下是它的一些关键点:

  1. 基本思想
  • 选择一个基准值(pivot),按照这个基准值将数组分为两部分,左侧部分的所有元素都小于等于基准值,右侧部分的所有元素都大于基准值。
  1. 递归搜索
  • 确定基准值的位置后,根据k与基准值的位置关系,选择数组的哪一部分继续进行递归搜索。如果k小于基准值的索引,则在第一部分(小于等于基准值的部分)继续搜索;否则,在第二部分(大于基准值的部分)继续搜索。
  1. 时间复杂度
  • 快速选择算法的平均时间复杂度为O(n),但最坏情况下的时间复杂度会退化到O(n^2)。尽管如此,由于其不需要完全排序数组,它在实际操作中通常比完全的排序算法更加高效。
  1. 实际应用
  • 快速选择算法及其变种是在实际应用中最常使用的高效选择算法之一,尤其适用于解决Top K问题等场景。

总的来说,快速选择算法是一种基于快速排序的选择算法,它高效地解决了在不完全排序的数组中寻找特定顺序元素的问题,并因此在各种算法竞赛和实际应用场景中得到了广泛的使用。

图解快速选择

图解

对下面这个数组寻找到前k小的元素

首先我随机生成一个下标指向一个基准元素

然后使用一个指针指向开始位置依次往后遍历,如果当前元素比基准元素大则将该元素放在末尾,也就是基准元素后面,如果比当前元素小则将他放在基准元素前面

此时遍历指针i指向的值比基准元素大,此时需要执行以下操作进行交换:swap(arr[--e], arr[i])

此时进行将遍历指针指向的元素与基准元素进行比较依次重复此操作,当遍历指针指向的元素比基准元素小时执行:swap(arr[i++], arr[++s]) ,当与基准元素相等时只需要执行i++即可。当遍历指针与末尾指针e相遇时即可停止。

此时在l到s,e到r重复执行上述操作,知道l >= r时结束递归就是基于快速选择的快排算法。但是此时我们需要寻找前k小个元素,而数组已经被分成了三部分

此时我们可以通过判断k是否小于等于a,如果k小于等于a那么前k小的元素一定是在左边这段区域,如果k小于等于a+b时说明此时第k小就在中间这个区域,只需要返回他就可以。如果上面两种情况都不成立,那么我们只需要区右边区域找到第k - a - b个最小的元素,找到该元素后,该元素之前的元素就是最小的K个数

代码解决前k小个元素

最小的K个数_牛客题霸_牛客网给定一个长度为 n 的可能有重复值的数组,找出其中不去重的最小的 k 个数。例如数组元素是。题目来自【牛客题霸】icon-default.png?t=N7T8https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf?tpId=295&tqId=23263&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj

#include <vector>
class Solution {
public:
/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可** * @param input int整型vector * @param k int整型 * @return int整型vector*/
int rand_num(int l, int r) {srand(time(0));return rand() % (r - l + 1) + l;
}int qselect(vector<int>& arr, int l, int r, int k) {if (l >= r) return arr[l];int i = l, s = l - 1, e = r + 1, x = arr[rand_num(l,r)];while (i < e) {if (arr[i] > x) swap(arr[i], arr[--e]);else if (arr[i] < x) swap(arr[i++], arr[++s]);else i++;}int a = s - l + 1, b = e - s - 1;if (a >= k) return qselect(arr, l, s, k);else if (a + b >= k) return x;else return qselect(arr, e, r, k - a - b);
}vector<int> GetLeastNumbers_Solution(vector<int>& input, int k) {// write code herevector<int> ans(k);if (k <= 0 || input.size() < 1) return ans;qselect(input, 0, input.size() - 1, k);for (int i = 0; i < k; i++) ans[i] = input[i];return ans;
}
};

null有一个整数数组,请你根据快速排序的思路,找出数组中第 k 大的数。 给定一个整数数组。题目来自【牛客题霸】icon-default.png?t=N7T8https://www.nowcoder.com/practice/e016ad9b7f0b45048c58a9f27ba618bf?tpId=295&tqId=44581&ru=%2Fpractice%2F6a296eb82cf844ca8539b57c23e6e9bf&qru=%2Fta%2Fformat-top101%2Fquestion-ranking&sourceUrl=%2Fexam%2Foj

import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可*** @param a int整型一维数组* @param n int整型* @param K int整型* @return int整型*/private int randNum(int l, int r) {Random random = new Random();return l + random.nextInt(r - l + 1);}private void swap(int[] arr, int i, int j) {int t = arr[i];arr[i] = arr[j];arr[j] = t;}private int qSelect(int[] arr, int l, int r, int k) {if (l >= r) return arr[l];int i = l, s = l - 1, e = r + 1, x = arr[randNum(l, r)];while (i < e) {if (arr[i] > x) swap(arr, i, --e);else if (arr[i] < x) swap(arr, ++s, i++);else i++;}int c = r - e + 1, b = e - s - 1;if (c >= k) return qSelect(arr, e, r, k);else if (b + c >= k) return x;else return qSelect(arr, l, s, k - c - b); }public int findKth (int[] a, int n, int K) {// write code herereturn qSelect(a, 0, n - 1, K);}
}

五、优先级队列与快速选则算法比较

优先级队列

使用优先级队列(通常实现为堆)解决TopK问题的时间复杂度是O(NlogK)。具体分析如下:

  1. 建堆:首先对K个元素进行建堆操作,建堆的时间复杂度是O(K)。这是因为堆的构建过程涉及到对K个元素进行排序,以形成一个最大堆或最小堆。
  2. 维护堆:然后遍历剩余的N-K个元素,对于每个元素,执行堆调整操作以维护堆的性质。每次插入或删除操作的时间复杂度是O(logK),因为堆的高度大约是logK,而单次堆调整的时间复杂度与堆的高度成正比。因此,对于N-K个元素的遍历,总的时间复杂度是O((N-K)logK)。
  3. 总时间复杂度:综合以上两个步骤,总的时间复杂度是O(K + (N-K)logK),由于通常情况下K远小于N,可以简化为O(NlogK)。
快速选择

快速选择算法的平均时间复杂度是 O(N),但最坏情况下的时间复杂度是 O(N^2)。以下是关于快速选择算法时间复杂度的详细分析:

  1. 平均时间复杂度
  • 在平均情况下,快速选择算法的时间复杂度是O(N)。这是因为每次分区操作平均只需要遍历数组的一半长度。在快速选择算法中,我们选择一个枢纽元素(pivot),并将数组分为两部分,一部分包含小于枢纽的元素,另一部分包含大于枢纽的元素。这个过程与快速排序相似,但是快速选择只递归地处理包含第K小元素的那一部分数组,而不是整个数组。由于每次递归调用处理的数组大小大约减半,所以平均时间复杂度为O(N)。
  1. 最坏情况时间复杂度
  • 在最坏的情况下,如果每次选择的枢纽都是最大或最小元素,那么每次分区操作只能减少一个元素的大小,导致需要进行N次分区操作,因此最坏情况下的时间复杂度是O(N^2)。为了减少这种情况的发生,可以通过随机选择枢纽来提高算法的效率。
  1. 优化措施
  • 为了优化快速选择算法并避免最坏情况的发生,可以采用“中位数的中位数”作为枢纽,这种方法被称为BFPRT算法。通过计算近似中位数并将其作为枢纽,可以有效地将数组分为大致相等的两部分,从而保持算法的平均时间复杂度。
  1. 数学推导
  • 快速选择算法的时间复杂度也可以通过数学归纳法进行推导。可以将数组分成若干组,每组选取中位数,然后从中选取中位数的中位数作为枢纽。这样的分组策略可以保证至少有一部分数组的大小显著减少,从而在数学上证明算法的平均时间复杂度为O(N)。具体的推导过程涉及到递归式和分组策略的详细分析。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/502938.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

精酿啤酒:创新原料的研发与市场前景

随着消费者口味和需求的不断变化&#xff0c;创新已成为啤酒行业持续发展的重要驱动力。Fendi Club啤酒在创新原料的研发方面走在行业前列&#xff0c;积极探索新型原料和风味组合&#xff0c;以满足市场对多样化、个性化产品的需求。 Fendi Club啤酒注重研发新型原料和添加剂。…

节省时间,创造价值:人工智能在工作中的实际应用

AI时代的工作流程&#xff1a;智能化操作&#xff0c;创新不止步 在当前的人工智能技术领域&#xff0c;无论是国内研发还是国际上的先进大型模型&#xff0c;本质上均采用了GPT&#xff0c;即生成式预训练Transformer模型。该模型的核心能力在于基于已学习的知识库生成回答。其…

数据分析-Pandas数据探查初步圆饼图

数据分析-Pandas数据探查初步圆饼图 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表&am…

使用css的transition属性实现抽屉功能

需求 使用css手写一个抽屉&#xff0c;并且不能遮挡住原来的页面 效果&#xff1a;&#xff08;录的gif有点卡&#xff0c;实际情况很丝滑&#xff09; 实现代码&#xff1a; <template><div class"dashboard-container"><div class"mainBox&…

python模型训练

目录 1、新建模型 train_model.py 2、运行模型 &#xff08;1&#xff09;首先会下载data文件库 &#xff08;2&#xff09;完成之后会开始训练模型&#xff08;10次&#xff09; 3、 训练好之后&#xff0c;进入命令集 4、输入命令&#xff1a;python -m tensorboard.ma…

数据中心IT设备硬件智能化运维管理探索

工业和信息化部日前出台《新型数据中心发展三年行动计划》&#xff0c;统筹推进新型数据中心发展&#xff0c;构建以新型数据中心为核心的智能算力生态体系&#xff0c;发挥对数字经济的赋能和驱动作用。新型数据中心是以5G、工业互联网、云计算、人工智能等应用需求为牵引&…

EMO在哪体验?阿里对口型视频生成工具EMO下载地址?阿里巴巴新模型EMO的技术原理

这几天&#xff0c;阿里的对口型视频生成工具EMO火了。根据官方宣传&#xff0c;EMO只需要上传一张图片和一段音频就可以一键生成对口型视频&#xff0c;而且视频中的嘴型还可以与声音匹配。这项技术支持多语言、对话、唱歌以及快速语速的适配&#xff0c;但也可能成为制造虚假…

web学习笔记(二十一)

目录 1.构造函数创建对象 1.1规则 1.2 new关键字调用构造函数时&#xff0c;函数内部做了什么事情&#xff1f; 1.3总结 2.混合模式创建对象 3.JavaScript 继承---借助构造函数 4.原型链 4.1原型链实现方法继承 5.完美的组合继承 6.call方法的使用 1.构造函数创建对象…

端游如何防破解

在2023年这个游戏大年中&#xff0c;诸多热门大作涌现&#xff0c;作为世界级IP哈利哈利波特的衍生游戏——《霍格沃茨之遗》毫无悬念地成为2023年游戏圈的首款爆款作品&#xff0c;斩获了一众玩家的青睐。 在众多光环的加持下&#xff0c;《霍格沃茨之遗》很快被著名游戏破解…

leetcode 热题 100_移动零

题解一&#xff1a; 双指针遍历&#xff1a;将非零的值往数组前端依次放置&#xff0c;将放置之后数组后端多余的位置都置为0&#xff0c;参考下图&#xff08;来源. - 力扣&#xff08;LeetCode&#xff09;&#xff09; class Solution {public void moveZeroes(int[] nums)…

数据可视化原理-腾讯-热力图

在做数据分析类的产品功能设计时&#xff0c;经常用到可视化方式&#xff0c;挖掘数据价值&#xff0c;表达数据的内在规律与特征展示给客户。 可是作为一个产品经理&#xff0c;&#xff08;1&#xff09;如果不能够掌握各类可视化图形的含义&#xff0c;就不知道哪类数据该用…

哪个有名的工具可以安全记事 私密记事本笔记推荐

在这个数字化的时代&#xff0c;我们的生活已经离不开各种记事工具。它们帮助我们记录生活中的点点滴滴&#xff0c;无论是工作上的重要事项&#xff0c;还是个人的私密心情。然而&#xff0c;当我在寻找一个能够安心记录私密事情的工具时&#xff0c;安全性成为了我最关心的因…