排序算法
Posted xiongxinxzy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法相关的知识,希望对你有一定的参考价值。
目的:跟编程纠缠了这么久,今天我想跟排序算法做个了断。
第一种:归并排序
思想:
1) 两个已经排好序的序列,如果要融合那复杂度是多少呢? 很简单 O(n)。例子:
1 3 5 7 9 和 2 4 6 8 10 12原理很简单: 敌不动我不动,谁大谁动(假设按照从大到小的顺序排序的话)
2) 归并排序,先将所有的序列分解,分解到只剩最后一个的时候返回之,上一级的递归在得到返回的序列时一定是排好序的,这样再将其弄来排序,排好了在返回给再上一级。
上代码:
1 //归并排序 s_id:起始位置 e_id:结束位置 使用闭区间 等会儿再去看看有没有更方便的方法 2 vector<int> mergesort(vector<int>& nums, int s_id, int e_id){ 3 if(s_id==e_id) 4 return vector<int>{nums[s_id]}; 5 vector<int> ans_vec; 6 int mid = (s_id+e_id)/2; 7 vector<int> vec_si = mergesort(nums, s_id, mid); 8 vector<int> vec_sii = mergesort(nums, mid+1, e_id); 9 //下面对返回的vec_si和vec_sii重新排序 从大到小排序 10 int si = 0; 11 int sii = 0; 12 while(si<vec_si.size() && sii<vec_sii.size()){ 13 if(vec_si[si]>vec_sii[sii]){ 14 ans_vec.push_back(vec_si[si]); 15 si++; 16 17 }else{ 18 ans_vec.push_back(vec_sii[sii]); 19 sii++; 20 } 21 } 22 while(si<vec_si.size()){ 23 ans_vec.push_back(vec_si[si]); 24 si++; 25 } 26 while(sii<vec_sii.size()){ 27 ans_vec.push_back(vec_sii[sii]); 28 sii++; 29 } 30 return ans_vec; 31 };
反思:1) 开始的时候我想在原数组上面直接更改,但是我发现这样不太方便,要变的东西太多了,思路很搅.
2) 看到一个不错的微信公众号上面用的是标兵,但是我觉得不好用。
第二种:快速排序
思想:
1) 选择一个标称量,然后将给定的数组按照标称量,大于它的放一边,小于它的放另一边儿,那等于的怎么办呢?
2)等于的随便放,放左边也可以放右边也可以。
3)往复递归。
1 int partion(vector<int>& nums, int s_id, int e_id){ 2 //下面开始按照参照的节点分成两部分 参考节点选择 s_id 3 int refnum = nums[s_id]; 4 int left = s_id; 5 int right = e_id; 6 while(left<right){ 7 while(left<right && refnum>=nums[right]) right--; 8 nums[left] = nums[right]; 9 while(left<right && refnum<=nums[left]) left++; 10 nums[right] = nums[left]; 11 } 12 nums[left] = refnum; 13 return left; 14 } 15 //快速排序算法 许嵩的歌声伴随我写代码 消散了寂寞 谢谢嵩哥 16 //s_id e_id 起始和终止点 使用闭区间 17 void qsort(vector<int>& nums, int s_id, int e_id){ 18 //现在的任务是将nums 在 [s_id, e_id]中的元素 按照标定值分成左右两半 左边大于等于标定 右边小于标定 19 if(s_id<e_id) 20 { 21 int mid = partion(nums, s_id, e_id); 22 qsort(nums, s_id, mid-1); 23 qsort(nums, mid+1, e_id); 24 } 25 }
看似简单的思路后面藏着的是很多的细节,下面一一列举:
1)s_id 和 e_id代表什么意义?
我这里的处理是代表范围的闭区间。回想之前的双指针和二分查找我用的是左开右闭。
2)while 和 if 中的退出条件为什么是s_id < e_id ?
因为此时闭区间内还有至少两个元素,既然有两个以上的元素那肯定是需要排序的。如果只要一个元素此时s_id == e_id这时就不要再排序了。
3)先从高的往回缩和先从底的往前缩有没有区别?
肯定有区别,在这里使用替换而不是交换的策略,这样这能是先缩高的位置。
4)while里面的判断条件的等号可以没有吗?
可以没有,但不能两个都没有。至少要有一个,要不然当出现一个和标称值一样的值时就会陷入死循环。
如果没有的话和我们的既定逻辑是不匹配的,因为如果没有的话就相当于忽略掉了和标称值相等时该怎么处理的情况。
5)完。
第三种:堆排序
思想:
1)构建一个大堆树,树的特点是跟节点是最大的。
2)排序过程为:取根节点---》调整树---》取根节点。。。。。
代码:
1 //功能函数一 调整大堆树 对这课树有一个要求:只有最上面一个不满足其余的都满足 2 //所谓大堆树就是根节点是最大的,左右节点的大小并无要求 3 //node:待调整的节点 绝对位置 比数组中的位置大一 4 void heapify(vector<int>& nums, int node, int len){ 5 //策略 当前头结点如果没法统治他的两个小节点就将其下放 6 //采用迭代的方法实现 7 // int len = nums.size(); 8 while(node*2<=len){ 9 int max_node = node; 10 //定位简单3树的最大节点位置 11 if(node*2<=len && nums[max_node-1]<nums[node*2-1]) max_node = node*2; 12 if(node*2+1<=len && nums[max_node-1]<nums[node*2+1-1]) max_node = node*2+1; 13 if(max_node==node) break; 14 //交换值 15 int temp = nums[max_node-1]; 16 nums[max_node-1] = nums[node-1]; 17 nums[node-1] = temp; 18 node = max_node; 19 } 20 } 21 //功能函数二:通过调整堆的方法生成一颗大堆树 22 void getheaptree(vector<int>& nums, int len){ 23 //生成的方式是从末端开始向上生成 24 int i = len/2; 25 while(i>0){ 26 heapify(nums, i, len); 27 i--; 28 } 29 }
分析:
1)以上分为两个功能函数,一个负责调整堆,调整的前提是:待调整的堆除了根节点之外其它的节点均已满足大堆的条件。
复杂度分析:
排序算法 平均时间复杂度 最差时间复杂度 空间复杂度 数据对象稳定性
冒泡排序 O(n2) O(n2) O(1) 稳定
快速排序 O(n*log2n) O(n2) O(log2n) 不稳定
堆排序 O(n*log2n) O(n*log2n) O(1) 不稳定
归并排序 O(n*log2n) O(n*log2n) O(n) 稳定
以上是关于排序算法的主要内容,如果未能解决你的问题,请参考以下文章