快排的深入学习

目录

交换类排序

一、冒泡排序

1. 算法介绍

2.算法流程

3. 算法性能分析

(1)时间复杂度分析

(2) 空间复杂度分析

冒泡排序的特性总结:

二、快速排序

1.算法介绍

2. 执行流程

1). hoare版本

2). 挖坑法

3). 前后指针版本

3.快速排序优化(小demo)

1). 三数取中法选key

2). 递归到小的子区间时,可以考虑使用插入排序

4.快速排序非递归

5.快速排序的特性总结:

6.快排的进一步深入学习

1).快排之三路划分

eg:

2).快排之自省排序

7.分治思想(三路划分)

数组分三块:


交换类排序

基本思想:所谓交换,就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置,交换排 序的特点是:将键值较大的记录向序列的尾部移动,键值较小的记录向序列的前部移动。

说起快排,那一定从冒泡排序切入,我们先来回顾一下冒泡排序。

一、冒泡排序

1. 算法介绍

       起泡排序又称冒泡排序。它是通过一系列的“交换”动作完成的。首先第一个关键字和第二个关键字比较,如果第一个大,则二者交换,否则不交换;然后第二个关键字和第三个关键字比较,如果第二个大,则二者交换,否则不交换······。一直按这种方式进行下去,最终最大的那个关键字被交换到了最后,一趟起泡排序完成。经过多趟这样的排序,最终使整个序列有序。在这个过程中,大的关键字像石头一样“沉底”,小的关键字像气泡一样逐渐向上“浮动”,冒泡排序的名字由此而来。

2.算法流程

原始序列:49  38  65  97  76  13  27  49

下面进行第一趟冒泡排序。

1)1号和2号进行比较,49 > 38,交换。

结果:38  49  65  97  76  13  27  49

2)2号和3号进行比较,49 < 65,不交换。

结果:38  49  65  97  76  13  27  49

3)3号和4号进行比较,65 < 97,不交换。

结果:38  49  65  97  76  13  27  49

4)4号和5号进行比较,97 > 76,交换。

结果:38  49  65  76  97  13  27  49

5)5号和6号进行比较,97 > 13,交换。

结果:38  49  65  76  13  97  27  49

6)6号和7号进行比较,97 > 27,交换。

结果:38  49  65  76  13  27  97  49

7)7号和8号进行比较,97 > 49 ,交换。

结果:38  49  65  76  13  27  49  97

      至此一趟起泡排序结束,最大的97被交换到了最后,97到达了它最后的位置。接下来对序列38  49  65  76  13  27  49 按照同样的方法进行第二趟起泡排序。经过若干趟起泡排序后,最终序列有序。要注意的是,冒泡排序算法结束的条件是在一趟排序过程中没有发生关键字交换。
冒泡排序算法代码如下:

#include <iostream>
#include <vector>
using namespace std;// 冒泡排序
void BubbleSort(vector<int>& v, int n)
{for (int i = 0; i < n - 1; i++){//j<n-1 就是每次排序都要排除最后已经被排好序的元素,从头开始排序for (int j = 0; j < n - 1 - i; j++){if (v[j] > v[j + 1]){//交换元素 大值往后换int temp = v[j];v[j] = v[j + 1];v[j + 1] = temp;}}}for (auto e : v) cout << e << " "; cout << endl;
}int main()
{vector<int> v = { 49,38,65,97,76,13,27,49 };BubbleSort(v, v.size());return 0;
}

eg:

3. 算法性能分析

(1)时间复杂度分析

由起泡排序算法代码可知,可选取量内层循环中的关键字交换操作作为基本操作。
1)最坏情况,待排序列逆序,此时对于外层循环的每次执行,内层循环中if语句的条件R[j]<R[j-1]始终成立,即基本操作执行的次数为 n - i 。 i 的取值为1 ~ n - 1。因此,基本操作总的执行次数为(n - 1 + 1)(n - 1) / 2=n(n - 1) / 2,由此可知时间复杂度 O(n^2)。

2)最好情况,待排序列有序,此时内层循环中if 语句的条件始终不成立,交换不发生,且内层循环执行n - 1次后整个算法结束,可见时间复杂度为O(n)。
综合以上两种情况,平均情况下的时间复杂度为O(n^2)。


(2) 空间复杂度分析

由算法代码可以看出,额外辅助空间只有一个temp,因此空向复杂度为O(1)。

冒泡排序的特性总结:

1. 冒泡排序是一种非常容易理解的排序

2. 时间复杂度:O(N^2)

3. 空间复杂度:O(1)

4. 稳定性:稳定

二、快速排序

1.算法介绍

       快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中 的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右 子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

       快速排序也是“交换”类的排序,它通过多次划分操作来实现排序。以升序为例,其执行流程可以概括为:每一趟选择当前所有子序列中的一个关键字(通常是第一个)作为枢轴,将子序列中比枢轴小的移到枢轴前面,比枢轴大的移到枢轴后面;当本趟所有子序列都被枢轴以上述规则划分完毕后会得到新的一组更短的子序列,它们成为下一趟划分的初始序列集。

       说人话就是,找数组第一个元素为参考,用left 和 right 指针分别从右至左 和 从左至右 进行搜索,满足小于参考元素 或 大于的就进行交换,最后 一定是较小元素与参考元素nums[0] 进行交换。后面讲解。

 // 假设按照升序对array数组中[left, right)区间中的元素进行排序
void QuickSort(int array[], int left, int right){if(right - left <= 1)return;// 按照基准值对array数组的 [left, right)区间中的元素进行划分
int div = partion(array, left, right);}// 划分成功后以div为边界形成了左右两部分 [left, div) 和 [div+1, right)// 递归排[left, div)QuickSort(array, left, div);// 递归排[div+1, right)QuickSort(array, div+1, right);


2. 执行流程

       上述为快速排序递归实现的主框架,发现与二叉树前序遍历规则非常像,同学们在写递归框架时可想想二叉 树前序遍历规则即可快速写出来,后序只需分析如何按照基准值来对区间中数据进行划分的方式即可。 将区间按照基准值划分为左右两半部分的常见方式有:

1). hoare版本

//hoare版本
void PartSort1(vector<int>& v, int left, int right)
{if (left >= right) return;int begin = left, end = right;int temp = v[left];while (begin < end){while (begin < end && v[end] >= temp) end--;while (begin < end && v[begin] <= temp) begin++;swap(v[end], v[begin]);}swap(v[left], v[begin]);PartSort1(v, left, begin - 1);PartSort1(v, begin + 1, right);
}int main()
{vector<int> v = { 49,38,65,97,76,13,27,49 };PartSort1(v, 0,v.size() - 1);for (auto e : v) cout << e << " "; cout << endl;return 0;
}

现在考虑为什么每次快排,以第一个元素为基准的时候,为什么都是right先走呢?

那是因为:

1.假如left 碰 right,右边先走,那么right停在了要被交换的小于基准的元素。这个时候left在开始向右寻找,结果在left < right 的条件下,left 碰了 right 这个时候 right下标的元素就是小于基准的,就也可以满足与基准进行交换,那么此时,就已经确定了基准元素的最终位置!

2.假如right 碰 left ,那么right先走,这个时候,left与right刚被交换完,此时还是right先走,这个时候right向左寻找,一直没找到满足小于基准的元素,这个时候碰到了left,可是left下标的元素就是刚跟right下标元素进行交换的元素,这个时候,left下标元素就是小于基准的 那么现在right 碰 left 就也是小于基准的元素,也满足 right 下标元素可以跟基准元素交换。

所以综上所述,可以看出,如果选最左边为基准,就right先走;如果选右边为基准,就left先走;

2). 挖坑法

挖坑法,也是称为最简单的快排思想,只需要有一个坑,找到一个满足大于或小于基准元素,将它填满就行。

将第一个元素作为基准元素,存放在temp中,让第一个元素的位置形成一个坑位。在后续的交换过程中,其实就是不停的填坑位。一个坑位被填满,另一个坑位就空出来了。

// 快速排序挖坑法
void PartSort2(vector<int>& v, int left, int right)
{if (left >= right) return;int begin = left, end = right;int temp = v[left];while (begin < end){while (begin < end && v[end] >= temp) end--;if (begin < end){v[begin++] = v[end];}while (begin < end && v[begin] <= temp) begin++;if (begin < end){v[end--] = v[begin];}}PartSort2(v, left, begin - 1);PartSort2(v, begin + 1, right);
}int main()
{vector<int> v = { 49,38,65,97,76,13,27,49 };PartSort2(v, 0,v.size() - 1);for (auto e : v) cout << e << " "; cout << endl;return 0;
}

3). 前后指针版本

     初始时,prev指向数组首元素,cur指向prev下一个元素

前后指针法写起来比较简洁,只要考虑 prev 和 cur 两个指针对应的元素的大小进行交换即可。具体思想其实跟 hoare 大差不差。

实现思想:

仍然是用temp将第一个元素作为基准存起来,在满足cur <= right 当cur还没走到最右边时,就进行元素比较和交换。只要cur下标对应的元素小于temp && 在++prev != cur 时 防止自己跟自己交换,那么就将两者元素进行交换,这种双指针法思想也挺简单的。就是要注意,在交换前满足++prev

最后循环完了就交换prev 与 left 对应下标的元素,将left下边元素的值确定到最终位置。

// 快速排序前后指针法
void PartSort3(vector<int>& v, int left, int right)
{if (left >= right) return;int prev = left;int cur = prev + 1;int temp = v[left];while (cur <= right){while (v[cur] < temp && ++prev != cur)swap(v[cur], v[prev]);++cur;}swap(v[prev], v[left]);PartSort3(v, left, prev - 1);PartSort3(v, prev + 1, right);
}int main()
{vector<int> v = { 49,38,65,97,76,13,27,49 };PartSort3(v, 0,v.size() - 1);for (auto e : v) cout << e << " "; cout << endl;return 0;
}

3.快速排序优化(小demo)

1). 三数取中法选key

每次选key 选择的都是最左边的left对应下标的元素,有点过于随机,可能会发生本身数组就是有序的,我还选left下标对应的元素,那么快排就可能会退化到O(N^2),为了防止这种随机的事件来拖慢快排的效率,那么我们就可以在选key时就保证不是选择的最大或最小,防止快排退化的过于严重。

那么就采取三数取中策略,取left  mid= (left + right) / 2  right 三个下标对应的值来选取,既不选择最大的,也不选择最小的,那么第三个数就一定是中间值,不会存在极端情况。

//找中间值 三数取中
int GetMidi(vector<int>& v, int left, int right)
{int midi = (left + right) / 2;//left midi rightif (v[left] < v[midi]){if (v[midi] < v[right]) return midi;else if (v[left] < v[right]) return right;//走了else 就说明v[midi]>v[right]else return left;//剩下就是v[left] 就是中间值}else{if (v[midi] > v[right]) return midi;else if (v[right] > v[left]) return left;else return right;}
}

三数取中,这样肯定能保证性能得到优化。

2). 递归到小的子区间时,可以考虑使用插入排序

递归到小的子区间优化,考虑插入排序,因为在一般O(N^2) 的较弱的排序中,插入排序对于小数组是非常nice的,吊打冒泡和交换排序。

如果快排 排序的数很多,就有可能会不断的递归造成栈溢出,为了减小栈溢出的概率,我们可以减少快排的递归的次数,在最后几次递归的过程中,对于小数组的递归,消耗较大,没有必要,这个时候就可以采用 递归到小的子区间时,可以考虑使用插入排序,这时就可以完全满足减少递归消耗,还能保证效率。

此时,就可以将小区间优化跟三数取中结合在一起:

void QuickSort(vector<int>& v, int left, int right)
{if (left >= right) return;//小区间优化,不在递归分割排序,减少递归次数if ((right - left + 1) < 10){//进行插入排序InSertSort(v + left, right - left + 1);}else{//三数取中int midi = GetMidi(v, left, right);//将midi下标对应的元素 交换到left处std::swap(v[left], v[midi]);}
}

4.快速排序非递归

在部分面试笔试的过程中,可能会存在对于递归快排非常熟悉,要求手撕非递归快排,那么就要考虑用 栈stack 来模拟实现递归的过程,就可以达到同样的效果。

void QuickSortNonR(int* a, int left, int right){Stack st;StackInit(&st);StackPush(&st, left);StackPush(&st, right);while (StackEmpty(&st) != 0){right = StackTop(&st);StackPop(&st);left = StackTop(&st);StackPop(&st);if(right - left <= 1)continue;int div = PartSort1(a, left, right);// 以基准值为分割点,形成左右两部分:[left, div) 和 [div+1, right)StackPush(&st, div+1);StackPush(&st, right);StackPush(&st, left);StackPush(&st, div);}StackDestroy(&s);}

5.快速排序的特性总结:

1). 快速排序整体的综合性能和使用场景都是比较好的,所以才敢叫快速排序

2). 时间复杂度:O(N*logN)

3). 空间复杂度:O(logN)

4). 稳定性:不稳定

6.快排的进一步深入学习

1).快排之三路划分

(专治有大量重复的情况)

a.key默认取left位置的值

b.left指向区间最左边,right指向区间最后边,cur指向left+1位置

c.cur遇到比key小的值后跟left位置交换,换到左边,left++,cur++

d.cur遇到比key大的值后跟right位置交换,换到右边,right--

e.cur遇到跟key相等的值后,cur++

f.直到cur > right 结束

eg:

将left下标对应的元素存入key中,key=6;

1.v[cur] < key 跟v[left++]交换

2.此时v[cur] > key ,那么就跟v[right--] 无脑交换 ,不用在乎v[right] 是否大于v[cur]

3.此时继续判断v[cur] > key ,那么继续执行刚才的逻辑,继续无脑跟 v[right--] 进行交换

4.因为cur没动,继续判断v[cur] < key 那么此时swap(v[cur++], v[left] );进行交换,让left,和cur都向前走

5.此时当遇到 v[cur]==key 时,就让cur一直走,直到 cur > right 时 就停止运动,然后这个时候就进行左右递归

伪代码用例,可以加入QuickSort 专门解决出现大量重复数据的快排问题,速度很快。

//快排之三路划分
void Count_out_three(vector<int>& v, int left, int right)
{int begin = left, end = right;int key = v[left];int cur = left + 1;while (cur <= right){if (v[cur] < key) swap(v[cur++], v[left++]);else if (v[cur] > key) swap(v[cur], v[right--]);else cur++; //v[cur] == key }//[begin,left-1] [left,right] [right+1,end]Count_out_three(v, begin, left - 1);Count_out_three(v, right + 1, end);
}

2).快排之自省排序

在前面的优化中,都是针对特定的情况下,可以时快排的性能得到较大的优化,但是在一些极端情况下,可能优化就不会那么明显,包括三数取中,三路划分等问题。

但是在工业中,面对C++,STL里面sort() 快排就是用的introsort 自省排序。当递归深度太深,如果继续使用当前的排序,那么性能可能大打折扣,那么此时可以改为堆排序,堆排序在给个情况下都是十分稳定的O(NlogN);

7.分治思想(三路划分)

讲到这里,其实可以看出,快排就是一种分治的思想,先排好一个元素,在排排左边的元素和右边的元素,最后得到有序数组。最核心的一步就是数据划分的步骤。朴素版本就是数据划分两部分。

但是但是,如果有很多重复元素的话,那么我们时间复杂度就退化成O(N^2)了,那么这个时候,我们就在可能存在重复元素的基础上多划分一步,变成数据划三份来进行排序。

[<key] [==key] [>key]

利用数据划分三块进行排序数组,来进行分类讨论:

数组分三块:

1.nums[i] < key  swap(nums[++left],nums[i++])

2.nums[i] == key i++;

3.nums[i] > key swap(nums[--right],nums[i]) //这里i不++,因为此时i还要继续判断

优化:随机选key (逼近NlogN)

int r=rand()

return nums[r%(right-left+1)+left];就一定返回 [0,n-1] 这个区间的值

            

//数组分三块class Solution {
public:vector<int> sortArray(vector<int>& nums) {srand(time(NULL)); //种下一颗随机数的种子qsort(nums,0,nums.size()-1);return nums;}void qsort(vector<int>& nums,int l,int r){if(l>=r) return;int key=getrand(nums,l,r); //得到这个随机数返回的nums的值int i=l,left=l-1,right=r+1;while(i<right){if(nums[i]==key) i++;else if(nums[i]<key) swap(nums[++left],nums[i++]);else swap(nums[--right],nums[i]);}//[l,left] [left+1,right-1] [right,r]qsort(nums,l,left);qsort(nums,right,r);}int getrand(vector<int>& nums,int left,int right){int r=rand();//得到这个随机数return nums[r%(right-left+1)+left];  //控制在区间[0,n-1]之间取值}
};

这个版本真的很重要,一定一定要写会!!!

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

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

相关文章

使用 scikit-learn 实战感知机算法

一 引言 感知机&#xff08;Perceptron&#xff09;是最早的人工神经网络模型之一&#xff0c;由 Frank Rosenblatt 在 1957 年提出。虽然它相对简单&#xff0c;但在处理线性可分问题时却非常有效。本文将介绍如何使用 Python 的 scikit-learn 库来实现感知机&#xff0c;并通…

828华为云征文|华为云Flexus X实例docker部署srs6并调优,协议使用webrtc与rtmp

828华为云征文&#xff5c;华为云Flexus X实例docker部署srs6并调优&#xff0c;协议使用webrtc与rtmp 华为云最近正在举办828 B2B企业节&#xff0c;Flexus X实例的促销力度非常大&#xff0c;特别适合那些对算力性能有高要求的小伙伴。如果你有自建MySQL、Redis、Nginx等服务…

Android 下的 XML 文件(概念理解、存储位置理解)

一、XML 1、XML 概述 XML&#xff08;Extensible Markup Language&#xff0c;可扩展标记语言&#xff09;是一种用于存储和传输数据的标记语言 类似于 HTML&#xff0c;但旨在传输和存储数据&#xff0c;而不是显示数据&#xff0c;且基本语法都是标签 2、XML 的特点 &am…

【数据结构与算法 | 搜索二叉树篇 力扣篇】力扣530, 501

1. 力扣530&#xff1a;二叉搜索树的最小绝对差 1.1 题目&#xff1a; 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 示例 1&#xff1a; 输入&#xff1a;root [4,…

BUUCTF—[BJDCTF2020]The mystery of ip

题解 打开环境点击上面的flag可以看到这个IP页面。 抓个包看看有啥东西无&#xff0c;可以看到在返回包有IP。 看到IP就想到X-Forwarded-For这个玩意&#xff0c;我们用X-Forwarded-For随便添加个IP看看。可以看到返回的IP内容变成了123。 X-Forwarded-For:123 推测它会输出我…

Java项目怎么从零部署到Linux服务器上?

目录 一.Java环境&#xff08;JDK&#xff09;安装 二.数据库&#xff08;MySQL&#xff09;安装 三.部署上线 ▐ 部署Jar包 ▐ 运行程序 ▐ 开放端口 一个Java项目首先需要一个支持它编译的Java环境&#xff0c;因此首先要保证服务器上安装的有相应的JDK 一.Java环境&a…

负载均衡 Ribbon 与 Fegin 远程调用原理

文章目录 一、什么是负载均衡二、Ribbon 负载均衡2.1 Ribbon 使用2.2 Ribbon 实现原理 (★)2.3 Ribbon 负载均衡算法 三、Feign 远程调用3.1 Feign 简述3.2 Feign 的集成3.3 Feign 实现原理 (★) 一、什么是负载均衡 《服务治理&#xff1a;Nacos 注册中心》 末尾提到了负载均…

第二十篇——行军篇:别指望外援,好好培养亲兵

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 微观层面&#xff0c;从驯服的视角&#xff0c;我们可以洞察到人性中非常…

leveldb源码剖析(二)——LSM Tree

LSM Tree LSM Tree&#xff1a;Log-Structured Merge Tree&#xff0c;日志结构合并树。是一种频繁写性能很高的数据结构。 LSM Tree将写入操作与合并操作分离&#xff0c;数据首先写入磁盘中的日志文件&#xff08;WAL&#xff09;&#xff0c;随后写入内存缓存&#xff0c;…

UDP英译汉网络词典

这里我们用UDP实现一个简单的英译汉小词典。我们还是仿照前一篇的UDP编程&#xff0c;将各自的组件封装起来&#xff0c;实现高内聚低耦合。 一. 字典翻译功能实现 首先我们将我们的字典知识库放在txt文本中。 apple: 苹果 banana: 香蕉 cat: 猫 dog: 狗 book: 书 pen: 笔 ha…

云计算之ECS

目录 一、ECS云服务器 1.1 ECS的构成 1.2 ECS的实例规格 1.3 镜像 1.4 磁盘 1.5 安全组 1.6 网络 1.7 产品结构 二、块存储介绍 2.1 快存储定义 2.2 块存储性能指标 2.3 快存储常用操作-云盘扩容 2.4 块存储常见问题 三、快照介绍 3.1 快照定义 3.2 快照常见问题…

flume 使用 exec 采集容器日志,转储磁盘

flume 使用 exec 采集容器日志&#xff0c;转储磁盘 在该场景下&#xff0c;docker 服务为superset&#xff0c;flume 的sources 选择 exec &#xff0c; sinks选择 file roll 。 任务配置 具体配置文件如下&#xff1a; #simple.conf: A single-node Flume configuration#…

GPT-4o mini轻量级大模型颠覆AI的未来

GPT-4o mini发布&#xff0c;轻量级大模型如何颠覆AI的未来&#xff1f; 引言 随着人工智能技术的飞速发展&#xff0c;大型AI模型的发布已成常态。然而&#xff0c;庞大的计算资源和存储空间限制了它们在广泛场景中的应用。为满足市场需求&#xff0c;轻量级大模型应运而生&a…

TOMATO靶机漏洞复现

步骤一&#xff0c;我们来到tomato页面 什么也弄不了只有一番茄图片 弱口令不行&#xff0c;xxs也不行&#xff0c;xxe还是不行 我们来使用kali来操作... 步骤二&#xff0c;使用dirb再扫一下, dirb http://172.16.1.133 1.发现这个文件可以访问.我们来访问一下 /antibot_i…

【课程学习】信号检测与估计

文章目录 3.7-CRB延展到向量的形式3.8-参数变换形式的CRB CRB for transformation, pp45-463.9-高斯分布 CRLB for the General Gaussian Case3.7-CRB延展到向量的形式 0904 向量和变换形式的CRLB形式 估计参数真实值 θ \theta θ,估计值 θ ^ \hat \theta θ^ 与信号与系统…

C# NX二次开发-获取对象名称

UG软件是所有带标签对象都能设置名称和属性&#xff1a; 代码&#xff1a; theUf.Obj.AskName(body.Tag,out string name);name.NxListing(); 免责声明&#xff1a; 只用于参考&#xff0c;如果有什么问题不要找我呀。

【JAVA入门】Day27 - 单列集合体系结构综述

【JAVA入门】Day27 - 单列集合体系结构 文章目录 【JAVA入门】Day27 - 单列集合体系结构1.1 Collection 集合的基本方法1.2 Collection 集合的遍历方式1.2.1 迭代器遍历1.2.2 增强 for 遍历1.2.3 利用 Lambda 表达式进行遍历 1.3 List 集合的基本方法1.4 List 集合的遍历方式 J…

WordPress自适应美图网站整站打包源码

直接服务器整站源码数据库打包了&#xff0c;恢复一下就可以直接投入使用。保证好用易用&#xff0c;无需独立服务器就可以使用。 强调一下&#xff0c;我这个和其他地方的不一样、不一样、不一样。具体的看下面的说明。 现在网络上同样的资源包都是用的加密带后门的主题&…

年化收益37.7%的A股小市值策略,小市值和动量因子长期有效(附具体逻辑)

“ 原创内容第640篇&#xff0c;专注量化投资、个人成长与财富自由” 股票的“小市值”策略&#xff0c;就像ETF的趋势动量一样&#xff0c;长期有效。 这是一个很神奇的异象。 年化37.07%&#xff0c;夏普0.89。 这里我做了一些特殊的处理&#xff1a; 1、包括排除了科创板…

Pencils Protocol生态新进展,即将上线 Vault 产品

“极高的盈利预期、通证的持续回购与销毁&#xff0c;Vault产品的推出正在成为Pencils Protocol生态发展的重磅利好。” Pencils Protocol是目前Scroll生态TVL最高的DeFi平台 &#xff0c;即便是行情整体较为平淡&#xff0c;其仍旧能够保持在3亿美元左右的锁仓价值&#xff0c…