Sort---排序

来源:互联网 发布:java注释的作用 编辑:程序博客网 时间:2024/05/17 06:33

<span style="font-family: 'Microsoft YaHei'; background-color: rgb(255, 255, 255);">学习了排序算法之后,觉得应该把它总结一遍,顺一遍自己的思路。因为排序算法比较杂,所以下来和大家一起来看看这么多的排序算法。先来看</span><span style="font-family: 'Microsoft YaHei'; background-color: rgb(255, 255, 255);">比较排序,</span>


我们看下面一张图


我们就以下面的图来讲一讲各种排序,

1.直接插入排序

思想:

每次选择一个元素K,将K插入到已经有序的部分A[1....i]中,插入之前,要把K和A中的每个元素进行比较,若K<A[n],则将K插入到A[n]之前(n是A中的某个下标),A[n]之后的所有元素都要向后移动,若发现A[i]>K,则K插到A[i]的后面。

时间复杂度:

最好的情况:正向有序,只需要比较n 次,不需要移动,时间复杂度O(N);

最坏的情况:逆序有序,每一个元素都要移动接近n次,时间复杂度O(N²);

平均时间复杂度:O(N²)

稳定性:

稳定性就是两个相同的元素排序完之后若两者顺序不变,就是稳定性高,反之稳定性低。

K1是有序部分的元素,插入K2,若K2=K1,则把K2插到K1后面即可,K1不需要移动,所以插入排序稳定性较高。

代码:

<span style="font-family:Times New Roman;font-size:18px;">void InsertSort(int* a, size_t n){int i = 0;for (i = 0; i < n - 1; i++){int end = i;int tmp = end + 1;while ((end >= 0) && (a[end]>a[tmp])){swap(a[end], a[tmp]);end--;tmp--;}}}</span>

2.希尔排序

思想:

希尔排序也是插入排序的一种,实际上是一种分组插入的方法,这样做的目的是为了让大的数据尽快的跑到前面去,先确定一个小于size的分量gap,将所有距离为gap的元素分为一组进行插入排序,然后取第二个分量gap(小于原来的gap)继续上一步操作,直到gap取到1,也就是最后的一次直接插入排序。


时间复杂度:

最坏情况下:小于插入排序的最坏情况,大于插入排序的最好情况,大概是O(N¹·³)

平均情况:O(N¹·³

稳定性:

我们知道一次插入排序是稳定的,但是多次插入排序中,相同的元素可能会在各自的分组中进行移动,就有可能会打乱相同元素原本的顺序,所以希尔排序是不稳定的

代码:

<span style="font-size:18px;">void ShellSort(int* a, size_t n){int gap = n;int i = 0;while (gap > 1){gap = gap / 3 + 1;//保证最后一次是直接插入排序for (i = 0; i< n - gap; i++){int end = i;int tmp = a[end + gap];while ((a[end] > tmp) && (end >= 0)){//swap(a[end + gap], a[end]);a[end + gap] = a[end];end -= gap;}a[end + gap] = tmp;}}}</span>

3.冒泡排序

思想:

通过无序区间中相邻字段的比较,将一个最大的数冒到区间的尾部,然后缩小空间,再从头开始比较,每一冒一个当前空间中最大的数,直到区间长度为1.

时间复杂度:

最好情况:正向有序,只需要比较n次,O(N)

最坏情况:逆向有序,每个数字都要比较n次,并且移动n次,O(N²)

平均情况:O(N²

稳定性:

排序中只会交换两个相邻的元素,因此吗,当两个相邻元素相同时,是没有必要进行交换的,所以冒泡排序是稳定的

代码:

<span style="font-family:Microsoft YaHei;">void BubbleSort(int* a, size_t n){int end = n - 1;while (end){for (int i = 0; i <= end; i++){if (a[i - 1] > a[i]){swap(a[i - 1], a[i]);}}end--;}}</span>

4.选择排序

思想:

在未排序区间中找到最大元素,放至区间末尾,再到剩下的元素中找最大元素,放至末尾,直到未排序区间只剩下一个元素

时间复杂度:

最好情况:正向有序,只需要比较,不需要交换,O(N²)

最坏情况:逆向有序,比较并且需要交换吗,也是O(N²)

平均情况:O(N²)

稳定性:

排序中可能会交换最后一个元素的位置,最后一个元素的位置很不确定,所以不稳定

代码;

void SelectSort(int* a, size_t n){int start = 0;int end = n - 1;while (start < end){int MaxIndex = start;int MinIndex = start;for (int i = start + 1; i <= end; i++){if (a[i] >= a[MaxIndex]){MaxIndex = i;}if (a[i] <= a[MinIndex]){MinIndex = i;}}swap(a[start], a[MinIndex]);//排除最大的在第一位,最小的在最后一位要交换两次的情况if (a[end] < a[MaxIndex]){swap(a[end], a[MaxIndex]);}start++;end--;}}


5.快速排序

思想:

  1. 从数列中挑出一个元素作为基准数。

  2. 分区过程,将比基准数大的放到右边,小于或等于它的数都放到左边。

  3. 再对左右区间递归执行第二步,直至各区间只有一个数。

时间复杂度:

最好情况下:每次划分轴值的左侧子序列与右侧子序列的长度相同,时间复杂度为O(nlogn),

最坏情况下:待排序序列为正序或逆序,时间复杂度为O(n^2);

平均情况下:时间复杂度为O(nlogn)

代码;

int GetKey(int* a, int left, int right){int key = a[right];int start = left;int end = right;while (start < end){while ((a[start]<key) && (start<end)){start++;}if (a[start]>key){a[end] = a[start];end--;}while ((a[end]>key) && (start<end)){end--;}if (a[end] < key){a[start] = a[end];start++;}}a[start] = key;return start;}// 非递归void QuickSort(int* a, int left, int right){stack<int> s;s.push(right);s.push(left);while (!s.empty()){int start = s.top();s.pop();int end = s.top();s.pop();if (start < end){int div = GetKey(a, start, end);s.push(end);s.push(div + 1);s.push(div - 1);s.push(start);         }}}
递归:

left,right]void QuickSort(int* a,int left, int right){if (left < right){int pos = GetKey(a, left, right);        QuickSort(a, left, pos-1);QuickSort(a, pos + 1, right);}}

6.归并排序

思想;将若干个有序序列进行两两归并,直至所有待排记录都在一个有序序列为止。

先考虑合并两个有序数组,基本思路是比较两个数组的最前面的数,谁小就先取谁,取了后相应的指针就往后移一位。

然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可。

再考虑递归分解,基本思路是将数组分解成 left right,如果这两个数组内部数据是有序的,

那么就可以用上面合并数组的 方法将这两个数组合并排序。如何让这两个数组内部是有序的?可以再二分,直至分解出的小组只含有一个元素时为止,此时认为该小组内部已有序。然后合并排序相邻二个小组即可。

时间复杂度:

最好,最坏,平均都是O(nlogn)。

代码:

oid Merge(int* a, int* tmp, int start, int mid, int end){int i = start;int j = mid + 1;int k = start;while ((i < mid + 1) && (j < end + 1)){while ((a[i] <= a[j]) && (i < mid + 1)){tmp[k++] = a[i++];}while ((a[i] > a[j]) && (j < end + 1)){tmp[k++] = a[j++];}}while (i < mid + 1){tmp[k++] = a[i++];}while (j < end + 1){tmp[k++] = a[j++];}for ( i = start; i < k; i++){a[i] = tmp[i];}}void MergeSort(int* a, int* tmp, int start, int end){if (start < end){int mid = (start + end) / 2;MergeSort(a, tmp, start, mid);MergeSort(a, tmp, mid + 1, end);Merge(a, tmp, start, mid, end);}}

7,堆排

思想:

1,堆排序是对简单选择排序的改进。

2,首先将待排序的记录序列构造成一个堆,此时,选出了堆中所有记录的最大者即堆顶记录,然后将他从堆中移走,并将剩下的记录再调整成堆,这样又找出了次大的记录,以此类推,直到堆中只有一个记录为止。

时间复杂度;

最好,最坏,平均的时间复杂度都是O(nlogn)

代码:

void _AdjustDown(int* a, size_t n, int i){int parent = i;int child = 2 * i + 1;while (child < n){if((child + 1 < n) && (a[child] < a[child + 1])){child++;//swap(a[child], a[child + 1]);}if (a[child] > a[parent]){swap(a[child], a[parent]);parent = child;child = 2 * parent + 1;}else{break;}}}void HeapSort(int* a, size_t n){int i = 0;for (i = (n - 2) / 2; i >= 0; i--){_AdjustDown(a, n, i);}int size = n;for (i = 0; i < n; i++){swap(a[0], a[n - 1 - i]);_AdjustDown(a, --size, 0);}}
就讲到这了,小伙伴们听懂了吗微笑







0 0
原创粉丝点击