数据结构的七种排序

来源:互联网 发布:php后端面试题 编辑:程序博客网 时间:2024/06/06 08:39

七种排序
从大类上可以分为插入排序、选择排序、交换排序和归并排序
       插入排序: 插入排序、希尔排序
       选择排序:选择排序、堆排序
       交换排序:冒泡排序、快速排序
       归并排序: 归并排序


1、插入排序 
      插入排序通过将一个元素插入一个按照大小排序好的序列这一过程,来实现对所有元素的排序。
      时间复杂度:O(N^2)
插入时的两种情况:
      1)找到了比tmp小的元素下标end,然后将tmp插入end下标的后面
       2)直到end<0时,说明tmp是最小的元素,则将tmp插入第一个位置
代码实现:

//插入排序template<class T>void InsertSort(T* a,int n){for (int i = 0; i < n-1; ++i){int end = i;int tmp = a[end+1];for (end = i; end >= 0; --end){if (a[end] > tmp){a[end + 1] = a[end];}else{break;}}a[end + 1] = tmp;}}

2、希尔排序
      对一组数据进行多次分组(gap为每组相邻两元素的间距)预排序,然后再进行一次插入排序。希尔排序是对插入排序的优化。
     原理:是通过预排序将小的元素尽可能较快的移至前面。最后通过 一次插入排序最终整合
     时间复杂度:O(N)~O(N^2)
问:如何确定gap的值?
      刚开始定义gap=n即等于元素的个数,
     单趟预排序通过gap=gap/3+1;来决定每次循环gap的值
     gap>1时进行预排序,gap==1时进行插入排序整合。
注意:预排序不是将每组分开排序,而是将所有组穿插着进行排序
代码实现:

//希尔排序void ShellSort(int *a, int n){int gap = n;while (gap > 1)  //进行多次预排序{gap = gap / 3 + 1;//gap>1时进行预排序   gap==1时进行最后一次插入排序//预排序多组间交替排序(即一次循环可以排多组)for (int i = 0; i < n - gap; ++i){int end = i;int tmp = a[end + gap];for (end = i; end >= 0; end -= gap){if (a[end] > tmp){a[end + gap] = a[end];}else{break;}}a[end + gap] = tmp;}}}

3、选择排序
      选择排序是每次从某个集合里选出最大或最小的一个元素,每次可以确定一个元素的位置,将集合不断缩小,最终完成对整个集合的排序。
      时间复杂度:O(N^2)
     优化版:每次可以取出最大值和最小值,确定两个位置,提高一倍效率
注意:
       优化版本可能会出现记录最大值的下标(max)在集合最左端(begin),记录最小值的下标(min)在集合最右端(end),出现交换两次后又将两元素交换回去的情况
解决方法:
       当将min下标的元素和begin下标的元素进行交换后,将max变量更新为min,加一条判断即可(详情见代码实现)
代码实现:

//选择排序//优化版选择排序---每次选择两个(最大和最小)void SelectSort(int *a, int n){int begin = 0;int end = n - 1;while (begin < end){int max = begin;int min = begin;for (int i = begin; i <= end; ++i){if (a[i] > a[max]){max = i;}if (a[i] < a[min]){min = i;}}swap(a[min], a[begin]);if (max == begin)   //解决max==begin且min==end造成交换两次的情况{max = min;}swap(a[max], a[end]);begin++;end--;}}

4、堆排序
      通过建堆和堆算法来实现多一个集合的排序
     时间复杂度:O(N*lgN)
堆排序过程:
     1、建大堆
     2、将堆顶元素和堆的最后一个元素交换
    3、堆顶向下调整
    4、将最后一个元素去除在堆外(但不是真正的删除)
代码实现:

void AdjustDown(int *a, int root, int end)   //向下调整{int parent = root;int child = parent * 2 + 1;while (child <= end){if (a[child] < a[child + 1] && (child + 1) <= end){++child;}if (a[parent] >= a[child]){break;}swap(a[parent], a[child]);parent = child;child = parent * 2 + 1;}}void HeapSort(int *a, int n){int end = n-1;  //end是数组最后一个元素下标//建大堆for (int i = (end - 1) / 2; i >= 0; --i){AdjustDown(a,i,end);}//交换元素和调整while (end){swap(a[0],a[end]);--end;    //每次将交换后最大的元素隐藏AdjustDown(a,0,end);}}

5、冒泡排序
      通过相邻的元素进行交换排序,每次循环可以确定一个最大或最小的元素,然后缩小集合范围,直至集合有序。
      优化:可以增加一个标志位flag,当序列已经有序时不用再进行多余的比较
      时间复杂度:O(N^2)
代码实现:

//冒泡排序void BubbleSort(int *a,int n){for (int i = n-1; i > 0; --i){bool flag = 0;  //加标志位提高效率(已经有序时直接退出)for (int j = 0; j < i; ++j){if (a[j + 1] < a[j]){swap(a[j + 1], a[j]);flag = 1;}}if (flag == 0)  //表示上次一单趟比较没有进行交换--可以直接结束排序{break;}}}

6、快速排序
      找出一个键值key,将集合中小于key的值放一边,大于key的值放在另外一边,即一次排序可以确定key值的准确位置,然后递归排序key的左边和key的右边,直至集合有序。
      时间复杂度:O(N*lgN)
      空间复杂度:O(lgN)
快排的时间复杂度最坏情况的O(N^2),但快排可以通过各种优化保证时间复杂度不会到最坏,所以认为快排的时间复杂度为O(N*lgN)。

快排的单趟排序方法:
       1)左右指针法、挖坑法,两种方法原理基本一致(见代码)。
       2)前后指针法:通过两个指针走的快慢不同将单趟集合元素分出大于key和小于key两个区间,具体实现见代码。

快排在时间复杂度上的优化:
       1)三数取中法:对key值的选取一般为最右端的值,但最右端得值若是最大或最小值,则此次单趟排序没意义,故取最左端、中间、最右端三个值当中的中间大小的值与最右端值进行交换,可以保证每趟排序不会出现没有元素交换的现象。
       2)小区间优化法:当快排递归到比较小的区间进行排序时,可选用插入排序等排序方式代替快排,减小空间和调用函数栈帧时间上的开销。

非递归实现快排:
       用栈结构模拟递归的调用过程实现,每次单趟排序从栈里取需要排序的区间,并将单趟排序得到的两个小区间分别入栈,等待下次出栈排序,直至栈为空排序结束
代码实现:

//快速排序//[left,right]闭区间int PartSort(int *a, int left, int right)  //单趟排序{//int key = a[right];   //挖坑法int key = right;        //左右指针法while (left < right){while (left < right && a[left] <= a[key]){left++;}//a[right] = a[left];while (left < right && a[right] >= a[key]){right--;}//a[left] = a[right];swap(a[left],a[right]);}swap(a[left], a[key]);//a[left] = key;return left;}int PartSort2(int *a, int begin, int end)    //前后指针法{int cur = begin;int prev = cur - 1;while (cur < end){if (a[cur] < a[end] && ++prev != cur){swap(a[cur], a[prev]);}cur++;}swap(a[++prev], a[end]);return prev;}void QuickSort(int *a, int begin, int end){if (begin >= end){return;}if (end - begin < 5)   //优化---对深层次递归优化为其他排序方式(减少空间开销){InsertSort(a + begin, end - begin + 1);}else{int mid = PartSort2(a, begin, end);QuickSort(a, begin, mid - 1);QuickSort(a, mid + 1, end);}}//非递归快排void QuickSortNonR(int *a, int begin, int end){stack<int> s;s.push(end);s.push(begin);while (!s.empty()){int left = s.top();s.pop();int right = s.top();s.pop();int mid = PartSort(a, left, right);if (left < mid - 1)    //当区间只有一个元素进行排序时不用入栈{s.push(mid - 1);s.push(left);}if (mid + 1 < right){s.push(right);s.push(mid + 1);}}}

7、归并排序
      将两段有序区间归并为一段有序的区间,但归并排序在空间上会有消耗(开辟同等大小的缓冲区),更适合外排序,
      时间复杂度:O(N*lgN)
      空间复杂度:O(N)
代码实现:

//归并排序void _Merge(int *a, int *tmp, int begin, int mid_left, int mid_right, int end){int tmp_begin = begin;int left_begin = begin;int left_end = mid_left;int right_begin = mid_right;int right_end = end;if (left_begin >= right_end)return;while (left_begin <= left_end && right_begin <= right_end){if (a[left_begin] > a[right_begin]){tmp[tmp_begin++] = a[right_begin++];}else{tmp[tmp_begin++] = a[left_begin++];}}if (left_begin > left_end){memcpy(tmp + tmp_begin, a + right_begin, sizeof(a[0]) * (right_end - right_begin + 1));}else{memcpy(tmp + tmp_begin, a + left_begin, sizeof(a[0]) * (left_end - left_begin + 1));}memcpy(a + begin, tmp + begin, sizeof(a[0]) * (end - begin + 1));}void MergeSort(int *a, int *tmp, int begin, int end){if (begin >= end){return;}int mid = begin + ((end - begin) >> 1);MergeSort(a, tmp, begin, mid);MergeSort(a, tmp, mid + 1, end);_Merge(a, tmp, begin, mid, mid + 1, end);}

对各种排序的比较:
     时间复杂度都为N^2的三个排序里,效率高低的相对排序(最好情况)
    插入排序  >  冒泡排序 > 选择排序

稳定的排序(三种):
    插入排序、冒泡排序、归并排序


原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 美团外卖下单后电话没改怎么办 从日本邮寄东西到中国被扣怎么办 淘宝拒收卖家收到货不退款怎么办 拒收的快递丢了卖家拒绝退款怎么办 京东买了东西超过七天想退货怎么办 微信买东西支付宝付款被骗怎么办 京东购物己付款卖家没发货怎么办 天猫先行退款卖家拒收怎么办 京东金条提前还款借不了怎么办 网银密码输入六次错误怎么办 迷失在时间和空间的交界处该怎么办 用卫生巾过后瘙痒起疹子了怎么办 装修公司倒闭了装修保修卡怎么办 丈夫把妻子的车抵押出去了怎么办 亚马逊海外购超过两万的额度怎么办 增值税申报表进项税转出忘填怎么办 一般纳税人注册下来后未营业怎么办 增值税税率把3错开成了5怎么办 在义乌做压痕加工老板拖欠钱怎么办 蓝洞棋牌是赌博输了几万怎么办 夏季来月经用卫生巾外阴瘙痒怎么办 用洗衣机洗衣服忘掏卫生纸了怎么办 剖腹产后一个月了还有血怎么办 剖腹产两个月同房后下面有血怎么办 剖腹产后月子里便秘有血怎么办 产后10天b超检查有血块怎么办 吃完优思明月经没有血块怎么办 刨腹产妇42天还有恶露怎么办 打完孩子第五天同房了出血了怎么办 打完孩子同房了出了一点血怎么办 宫腔镜检查一个月同房流血多怎么办 宫颈活检后三天同房有出血怎么办 顺产侧切两个月之后同房感染怎么办 顺产40天还有暗红色的恶露怎么办 来姨妈了没带卫生棉条去游泳怎么办 母猪肚子里面的小猪下不出来怎么办 刚生小猪的母猪肚子胀怎么办 吃了两天中药肚子还疼怎么办 怀孕期间垫了脚够东西怎么办 月经期垫卫生巾有边红肿有疹怎么办 四十天拉今天恶露特别多怎么办