文章目录

  • 十大排序算法概述
  • 应用场景
  • 代码实现
    • 一、冒泡排序
    • 二、选择排序
    • 三、插入排序
    • 四、希尔排序
    • 五、归并排序
    • 六、快速排序
    • 七、堆排序
    • 八、计数排序
    • 九、桶排序
    • 十、基数排序

十大排序算法概述

(图片来源于网络)

注意

  • 原地快排的空间占用是递归造成的栈空间的使用,最好情况下是递归log2n次,所以空间复杂度为O(log2n),最坏情况下是递归n-1次,所以空间复杂度是O(n)。
  • 非原地排序每次递归都要声明一个总数为n的额外空间,所以空间复杂度变为原地排序的n倍,即最好情况下O(nlog2n),最差情况下O(n的平方)

应用场景

  1. 从平均时间来看,快速排序是效率最高的,但快速排序在最坏情况下的时间性能不如堆排序和归并排序。而后者相比较的结果是,在n较大时归并排序使用时间较少,但使用辅助空间较多。

  2. 简单排序包括除希尔排序之外的所有冒泡排序、插入排序、简单选择排序。其中直接插入排序最简单,但序列基本有序或者n较小时,直接插入排序是好的方法,因此常将它和其他的排序方法,如快速排序、归并排序等结合在一起使用。

  3. 基数排序的时间复杂度也可以写成O(d*n)。因此它最使用于n值很大而关键字较小的的序列。若关键字也很大,而序列中大多数记录的最高关键字均不同,则亦可先按最高关键字不同,将序列分成若干小的子序列,而后进行直接插入排序。

  4. 从方法的稳定性来比较,基数排序是稳定的内排方法,所有时间复杂度为O(n^2)的简单排序也是稳定的。但是快速排序、堆排序、希尔排序等时间性能较好的排序方法都是不稳定的。稳定性需要根据具体需求选择。

  5. 上面的算法实现大多数是使用线性存储结构,像插入排序这种算法用链表实现更好,省去了移动元素的时间。具体的存储结构在具体的实现版本中也是不同的。

代码实现

一、冒泡排序

  1. 比较相邻的元素。如果第一个比第二个大,就交换它们两个;
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
  3. 针对所有的元素重复以上的步骤,除了最后一个;
  4. 重复步骤1~3,直到排序完成。

应用场景:
冒泡排序思路简单,代码也简单,特别适合小数据的排序。但是,由于算法复杂度较高,在数据量大的时候不适合使用。

//时间复杂度(平均):O(n^2)
//时间复杂度(最坏):O(n^2)
//时间复杂度(最好):O(n)
//空间复杂度():O(1)
//稳定性:稳定
function betterBubbleSort(arr) {const len = arr.length  for(let i=0;i<len;i++) {let flag = falsefor(let j=0;j<len-1-i;j++) {if(arr[j] > arr[j+1]) {[arr[j], arr[j+1]] = [arr[j+1], arr[j]]// 只要发生了一次交换,就修改标志位flag = true}}// 若一次交换也没发生,则说明数组有序,直接放过if(flag == false)  return arr;}return arr
}
console.log(bubbleSort([6,5,3,2,22,56]));

二、选择排序

  1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
  2. 从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
  3. 重复第二步,直到所有元素均排序完毕。

适用场景
选择排序实现也比较简单,并且由于在各种情况下复杂度波动小,因此一般是优于冒泡排序的。在所有的完全交换排序中,选择排序也是比较不错的一种算法。但是,由于固有的O(n2)复杂度,选择排序在海量数据面前显得力不从心。因此,它适用于简单数据排序。

//经过n-1趟直接选择
//选择最小的交换到前面//时间复杂度(平均):O(n^2)
//时间复杂度(最坏):O(n^2)
//时间复杂度(最好):O(n^2)
//空间复杂度():O(1)
//稳定性:不稳定function selectSort(arr)  {// 缓存数组长度const len = arr.length // 定义 minIndex,缓存当前区间最小值的索引,注意是索引let minIndex  // i 是当前排序区间的起点for(let i = 0; i < len - 1; i++) { // 初始化 minIndex 为当前区间第一个元素minIndex = i  // i、j分别定义当前区间的上下界,i是左边界,j是右边界for(let j = i; j < len; j++) {  // 若 j 处的数据项比当前最小值还要小,则更新最小值索引为 jif(arr[j] < arr[minIndex]) {  minIndex = j}}// 如果 minIndex 对应元素不是目前的头部元素,则交换两者if(minIndex !== i) {[arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]}}return arr
}
console.log(selectionSort([6,5,3,2,22,56]));

三、插入排序

  1. 把待排序的数组分成已排序和未排序两部分,初始的时候把第一个元素认为是已排好序的。
  2. 从第二个元素开始,在已排好序的子数组中寻找到该元素合适的位置并插入该位置。
  3. 重复上述过程直到最后一个元素被插入有序子数组中。

适用场景
插入排序由于O( n2 )的复杂度,在数组较大的时候不适用。但是,在数据比较少的时候,是一个不错的选择,一般做为快速排序的扩充。例如,在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充,用于少量元素的排序。又如,在JDK 7 java.util.Arrays所用的sort方法的实现中,当待排数组长度小于47时,会使用插入排序。

//时间复杂度(平均):O(n^2)
//时间复杂度(最坏):O(n^2)
//时间复杂度(最好):O(n)
//空间复杂度():O(1)
//稳定性:稳定// 插入排序的时间复杂度分析。在最坏情况下,数组完全逆序,插入第2个元素时要考察前1个元素,插入第3个元素时,要考虑前2个元素,……,插入第N个元素,要考虑前 N - 1 个元素。
// 因此,最坏情况下的比较次数是 1 + 2 + 3 + ... + (N - 1),等差数列求和,结果为 N^2 / 2,所以最坏情况下的复杂度为 O(N^2)。
// 最好情况下,数组已经是有序的,每插入一个元素,只需要考查前一个元素,因此最好情况下,插入排序的时间复杂度为O(N)。// 从第一个元素开始,该元素可以认为已经被排序;
// 取出下一个元素,在已经排序的元素序列中从后向前扫描;
// 如果该元素(已排序)大于新元素,将该元素移到下一位置;
// 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
// 将新元素插入到该位置后;
// 重复步骤2~5。function insertSort(arr) {// 缓存数组长度const len = arr.length// temp 用来保存当前需要插入的元素let temp  // i用于标识每次被插入的元素的索引for(let i = 1;i < len; i++) {// j用于帮助 temp 寻找自己应该有的定位let j = itemp = arr[i]  // 判断 j 前面一个元素是否比 temp 大while(j > 0 && arr[j-1] > temp) {// 如果是,则将 j 前面的一个元素后移一位,为 temp 让出位置arr[j] = arr[j-1]   j--}// 循环让位,最后得到的 j 就是 temp 的正确索引arr[j] = temp}return arr
}
console.log(insertionSort([6,5,3,2,22,56]));

四、希尔排序

  1. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
  2. 按增量序列个数k,对序列进行 k 趟排序;
  3. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

适用场景
插入排序在基本有序或者规模较小时十分高效,较大规模且无序时,可以用用希尔排序:相当于是将数据分组,再每组进行插入排序;

//时间复杂度(平均):O(n^1.3)
//时间复杂度(最坏):O(n^2)
//时间复杂度(最好):O(n)
//空间复杂度():O(1)
//稳定性:不稳定// 缩小增量排序
// 最后成为增量为1的插入排序function shellSort(arr) {var len = arr.length,temp;for (var gap=Math.floor(len/2);gap>0;gap=Math.floor(gap/2)) {//Math.floor(gap / 2),返回小于等于(gap/2)的最大整数for (var i = gap; i < len; i++) {//循环小组//每组进行插入排序, 前gap—1个相当于是每组插入排序的第一个,所以从gap开始;temp = arr[i];for (var j = i-gap; j >= 0 && arr[j]> temp; j-=gap) {arr[j + gap] = arr[j];}arr[j + gap] = temp;}}return arr;
}console.log(shellSort([6,5,3,2,22,56]));

五、归并排序

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4. 重复步骤3直到某一指针到达序列尾
  5. 将另一序列剩下的所有元素直接复制到合并序列尾
    //时间复杂度(平均):O(nlog2 n)//时间复杂度(最坏):O(nlog2 n)//时间复杂度(最好):O(nlog2 n)//空间复杂度():O(n)//稳定性:稳定function mergeSort(arr) {  // 采用自上而下的递归方法var len = arr.length;if (len < 2) {return arr;}       var middle = Math.floor(len / 2),//向下取整left = arr.slice(0, middle),right = arr.slice(middle);return merge(mergeSort(left), mergeSort(right));}function merge(left, right) {var result = [];while (left.length>0 && right.length>0) {if (left[0] <= right[0]) {//<=为了稳定性result.push(left.shift());} else {result.push(right.shift());}}while (left.length)result.push(left.shift());while (right.length)result.push(right.shift());return result;}console.log(mergeSort([6,5,3,2,22,56]));

六、快速排序

  • 原地快排的空间占用是递归造成的栈空间的使用,最好情况下是递归log2n次,所以空间复杂度为O(log2n),最坏情况下是递归n-1次,所以空间复杂度是O(n)。
  • 非原地排序每次递归都要声明一个总数为n的额外空间,所以空间复杂度变为原地排序的n倍,即最好情况下O(nlog2n),最差情况下O(n的平方)

原地快排

    //时间复杂度(平均):O(nlog2 n)//时间复杂度(最坏):O(n^2)//时间复杂度(最好):O(nlog2 n)//空间复杂度():O(log2 n)//稳定性:不稳定//思路:两个哨兵,i,j,j从右边找比基数小的,//i从左边找比基数大的,然后交换两个目标元素的位置,//直到i=j,然后交换i和基数的位置,递归处理。// 快速排序入口
function quickSort(arr, left = 0, right = arr.length - 1) {// 定义递归边界,若数组只有一个元素,则没有排序必要if(arr.length > 1) {// lineIndex表示下一次划分左右子数组的索引位const lineIndex = partition(arr, left, right)// 如果左边子数组的长度不小于1,则递归快排这个子数组if(left < lineIndex-1) {// 左子数组以 lineIndex-1 为右边界quickSort(arr, left, lineIndex-1)}// 如果右边子数组的长度不小于1,则递归快排这个子数组if(lineIndex<right) {// 右子数组以 lineIndex 为左边界quickSort(arr, lineIndex, right)}}return arr
}
// 以基准值为轴心,划分左右子数组的过程
function partition(arr, left, right) {// 基准值默认取中间位置的元素let pivotValue = arr[Math.floor(left + (right-left)/2)]// 初始化左右指针let i = leftlet j = right// 当左右指针不越界时,循环执行以下逻辑while(i<=j) {// 左指针所指元素若小于基准值,则右移左指针while(arr[i] < pivotValue) {i++}// 右指针所指元素大于基准值,则左移右指针while(arr[j] > pivotValue) {j--}// 若i<=j,则意味着基准值左边存在较大元素或右边存在较小元素,交换两个元素确保左右两侧有序if(i<=j) {swap(arr, i, j)i++j--}}// 返回左指针索引作为下一次划分左右子数组的依据return i
}// 快速排序中使用 swap 的地方比较多,我们提取成一个独立的函数
function swap(arr, i, j) {[arr[i], arr[j]] = [arr[j], arr[i]]
}console.log(quickSort([22,14,3,61,9,8,41,13]));

非原地快排:

function quickSort(arr) {//检查数组的元素个数,如果小于等于1,就返回if (arr.length <= 1) { return arr; }//选择"基准"(pivot),并将其与原数组分离var pivot = arr.pop();//定义两个空数组,用来存放一左一右的两个子集。var left = [];var right = [];//遍历数组,小于"基准"的元素放入左边的子集,大于基准的元素放入右边的子集。for (var i = 0; i < arr.length; i++){if (arr[i] < pivot) {left.push(arr[i]);} else {right.push(arr[i]);}}//用递归不断重复这个过程return quickSort(left).concat([pivot], quickSort(right));};

七、堆排序

//时间复杂度(平均):O(nlog2 n)
//时间复杂度(最坏):O(n^2)
//时间复杂度(最好):O(nlog2 n)
//空间复杂度():O(1)
//稳定性:不稳定// 初始化化堆 是从非叶子结点从后往前进行调整为堆,而后序的堆调整 都是从下标为0处开始调整为堆。
// 因为起初的完全二叉树是完全无效的,所有只能从后往前调整。在初始化完堆之后,
// 尽管将最值移动到尾部,打乱了堆,因为原来的堆结构已经基本形成,//1、调整为大顶堆或者小顶堆
//2、将堆的第一个和最后一个互换
//3、调整打乱后的堆
//重复1、2、3var len;    // 因为声明的多个函数都需要数据长度,所以把len设置成为全局变量function buildMaxHeap(arr) {   // 建立大顶堆len = arr.length;for (var i = Math.floor(len/2); i >= 0; i--) {heapify(arr, i);}
}function heapify(arr, i) {     // 堆调整var left = 2 * i + 1,right = 2 * i + 2,largest = i;if (left < len && arr[left] > arr[largest]) {largest = left;}if (right < len && arr[right] > arr[largest]) {largest = right;}if (largest != i) {swap(arr, i, largest);heapify(arr, largest);}
}function swap(arr, i, j) {var temp = arr[i];arr[i] = arr[j];arr[j] = temp;
}function heapSort(arr) {buildMaxHeap(arr);for (var i = arr.length - 1; i > 0; i--) {swap(arr, 0, i);len--;heapify(arr, 0);}return arr;
}

八、计数排序

//时间复杂度(平均):O(n+k)
//时间复杂度(最坏):O(n+k)
//时间复杂度(最好):O(n+k)
//空间复杂度():O(n+k)
//稳定性:稳定//适用于整数// 基本思想为一组数在排序之前先统计这组数中其他数小于这个数的个数,则可以确定这个数的位置。
// 例如要排序的数为 7 4 2 1 5 3 1 5;则比7小的有7个数,所有7应该在排序好的数列的第八位,
// 同理3在第四位,对于重复的数字,1在1位和2位(暂且认为第一个1比第二个1小),5和1一样位于6位和7位。function countingSort(arr){var maxValue = Math.min.apply(this,arr);var bucket = new Array(maxValue+1);var sortIndex = 0;for(var i =0;i<arr.length;i++){if(!bucket[arr[i]]){bucket[arr[i]] = 0;}bucket[arr[i]]++;}for(var j  =0;j<bucket.length;j++){while(bucket[j]>0){arr[sortIndex++] = j;bucket[j]--;}}return arr;
}
console.log(countingSort([6,5,3,2,22,56]));

九、桶排序

当输入的数据可以均匀的分配到每一个桶中时最快

当输入的数据被分配到了同一个桶中时最慢

//时间复杂度(平均):O(n+k)
//时间复杂度(最坏):O(n^2)
//时间复杂度(最好):O(n)
//空间复杂度():O(n+k)
//稳定性:稳定// 桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。
// 桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,
// 每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。// 按照函数的映射关系设置一个定量的数组当作空桶;
// 遍历输入数据,并且把数据一个一个放到对应的桶里去;
// 对每个不是空的桶进行排序;
// 从不是空的桶里把排好序的数据拼接起来。 function bucketSort(arr, bucketSize) {//数组、桶的大小(可不输入,有默认值5)if (arr.length === 0) {return arr;}var i;var minValue = arr[0];var maxValue = arr[0];for (i = 1; i < arr.length; i++) { if (arr[i] < minValue) {minValue = arr[i];                // 输入数据的最小值} else if (arr[i] > maxValue) {maxValue = arr[i];                // 输入数据的最大值}}// 桶的初始化var DEFAULT_BUCKET_SIZE = 5;            // 设置桶的默认大小为5bucketSize = typeof bucketSize != 'number' ? DEFAULT_BUCKET_SIZE : bucketSize;var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;  //Math.floor():向下舍入;取得桶的数量var buckets = new Array(bucketCount);for (i = 0; i < buckets.length; i++) {buckets[i] = [];}// 利用映射函数将数据分配到各个桶中for (i = 0; i < arr.length; i++) {buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);}arr.length = 0;for (i = 0; i < buckets.length; i++) {console.log(buckets[i]);insertionSort(buckets[i]);                      // 对每个桶进行排序,这里使用了插入排序for (var j = 0; j < buckets[i].length; j++) {arr.push(buckets[i][j]);                     }}return arr;
}function insertionSort(arr) {var len  = arr.length;var preIndex, //前面有序队列中用以比较的下标current;//后面乱序中提出来,用以比较的数for(var i = 1; i<len ; i++){preIndex = i - 1;current = arr[i];while(preIndex >= 0 && arr[preIndex] > current){arr[preIndex + 1] =arr[preIndex]; //向右移动preIndex--;}arr[preIndex + 1] = current;}return arr;
}console.log(bucketSort([6,5,3,2,22,56]));

十、基数排序

//时间复杂度(平均):O(n*k)
//时间复杂度(最坏):O(n*k)
//时间复杂度(最好):O(n*k)
//空间复杂度():O(n+k)   k为桶的数量
//稳定性:稳定// 取得数组中的最大数,并取得位数;
// arr为原始数组,从最低位开始取每个位组成radix数组;
// 对radix进行计数排序(利用计数排序适用于小范围数的特点);// 正是因为高位的数值决定数字的大小,所以应该后排,否则高位排好再排低位,高位不就乱了?
// 因为基数排序是稳定的,所以可以这么做,即高位如果相等的,还是保持低位排好的顺序// LSD Radix Sort
var counter = [];
function radixSort(arr) {//数组var maxValue = arr[0];for (i = 1; i < arr.length; i++) { if (arr[i] > maxValue) {maxValue = arr[i];       }}var maxDigit = getMaxDigit(maxValue);//最高位数var mod = 10;var dev = 1;for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {for(var j = 0; j < arr.length; j++) {var bucket = parseInt((arr[j] % mod) / dev); //个位、十位......的数if(counter[bucket]==null) {counter[bucket] = [];}counter[bucket].push(arr[j]);}var pos = 0;for(var j = 0; j < counter.length; j++) {var value = null;if(counter[j]!=null) {while ((value = counter[j].shift()) != null) { //shift(),把数组的第一个元素从其中删除,并返回第一个元素的值。arr[pos++] = value;}}}}return arr;
}
function getMaxDigit(maxValue){//求整数的位数var maxDigit = 1;
while(maxValue/10>=1){maxValue=maxValue/10;maxDigit++;
}
return maxDigit;
}

参考:
https://zhuanlan.zhihu.com/p/42586566

本文链接https://blog.csdn.net/qq_39903567/article/details/115638973

十大排序算法JS实现以及复杂度分析相关推荐

  1. 【十大排序算法系列】快速排序

    写在前面 上一篇更新了这个系列的排序算法([十大排序算法系列]冒泡排序).分析了冒泡的逻辑和优化点,下面来写下快速排序(为什么跳这么快?因为比较走心hhhh) 照例给出系列内所有算法的对比.. 常见的 ...

  2. JS 实现十大排序算法

    文章目录 前言 零.十大排序 一.冒泡排序(bubbleSort) 二.选择排序(selectionSort) 三.插入排序(insertSort) 四.希尔排序(shellSort) 五.归并排序( ...

  3. 十大排序算法(Java)

    文章目录 十大排序算法(Java) 一.冒泡排序(Bubble Sort) 二.选择排序(Selection Sort) 三.堆排序(Heap Sort) 四.插入排序(Insertion Sort) ...

  4. 这或许是东半球分析十大排序算法最好的一篇文章

    作者 | 不该相遇在秋天 转载自五分钟学算法(ID:CXYxiaowu) 前言 本文全长 14237 字,配有 70 张图片和动画,和你一起一步步看懂排序算法的运行过程. 预计阅读时间 47 分钟,强 ...

  5. 「干货总结」程序员必知必会的十大排序算法

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:硬刚一周,3W字总结,一年的经验告诉你如何准备校招! 个人原创100W+访问量博客:点击前往,查看更多 绪论 身 ...

  6. 「归纳|总结」程序员必知必会的十大排序算法

    微信搜一搜「bigsai」关注这个有趣的程序员 新人原创公众号,求支持一下!你的点赞三连肯定对我至关重要! 文章已收录在 我的Github bigsai-algorithm 欢迎star 本文目录 绪 ...

  7. 归并排序执行次数_十大排序算法,看这篇就够了

    排序算法分类[1][2] 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序. 非比较类排序:不通过比较来决定元素间的相对次序,它可以 ...

  8. 八十八、Python | 十大排序算法系列(下篇)

    @Author:Runsen @Date:2020/7/10 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏 ...

  9. 八十七、Python | 十大排序算法系列(上篇)

    @Author:Runsen @Date:2020/7/10 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏 ...

最新文章

  1. 关于IOS获取本地通讯录信息(包含iOS9.0前后)
  2. mac本机 Linux服务器anaconda安装
  3. 博客重构 / Blog Refactoring
  4. 第三百七十七节,Django+Xadmin打造上线标准的在线教育平台—apps目录建立,以及数据表生成...
  5. java rocketmq消费_rocketmq消费负载均衡--push消费详解
  6. FactoryBean 源码
  7. 数据结构——双向链表的实现
  8. 有几种部署模式_来!PyFlink 作业的多种部署模式
  9. sox处理mp3_使用SoX将mp3文件拆分为TIME秒
  10. strictmath_Java StrictMath rint()方法与示例
  11. python模块下载过程_常用的python模块及安装方法
  12. paping使用来测试联通网站由于tcp协议导致的无法通信问题超时问题
  13. Google发布中文名称“谷歌”
  14. 使用react-native做一个简单的应用-01项目介绍
  15. Atitit 封装的艺术 目录 1.1. 规范是不暴露特有的api 1 1.2. 方便理解,提升可读性 1 1.3. Atitit 提升可读性 数据结构特殊化专用api 比较通用的对象
  16. 电脑怎么让图片颜色反转?怎么调图片反色效果?
  17. 计算机怎么转换英语版本,电脑英文字母大小写怎么转换
  18. ZIP压缩算法详细分析及解压实例解释
  19. tar.gz和tgz的区别
  20. 大数据之路 阿里巴巴大数据实践 读书笔记

热门文章

  1. 全面了解mysql中utf8和utf8mb4的区别
  2. 毛坯房验房流程与注意事项
  3. 电商退货退款处理服务流程,快递鸟提供最有效的解决方案(内附教程)
  4. 优思学院|8D和DMAIC两种方法应如何选择?
  5. English Learning - L1-4 从此没有不会的表达(下) 2022.12.15 周四
  6. 中通消息服务运维平台实践(已开源)
  7. 推荐一款本地伪原创工具
  8. 阿里程序员:螺丝钉的工作,撕逼的氛围,末位淘汰的压力
  9. 网曝南方电网搞末位淘汰和裁员,给各下属单位强制规定辞退率和降岗降级率!...
  10. 外汇交易中144均线回调中的反转策略 反转策略的含义 反转是怎样形成的