排序的概念
- 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。
- 稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
- 内部排序:数据元素全部放在内存中的排序。
- 外部排序:数据元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序。
常见的排序算法
基本思想
直接插入排序是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
直接插入排序
比如有手上有很多的牌,第一次抓一张牌出来,放到最前面,认为有序,第二次在抓一张牌出来和有序的牌中作比较,找到合适位置插入,依次类推。
直接插入排序的特性总结:
- 元素集合越接近有序,数据量比较少,直接插入排序算法的时间效率越高
- 时间复杂度:O(N^2)
- 空间复杂度:O(1),它是一种稳定的排序算法
- 稳定性:稳定
算法实现
对于 3 2 5 8 4 7 6 9
对于这种情况
不断重复重复第一次的操作就行,如果当前数比前一个数大,外循环就往后走
//2 4 5 9 3 6 8 7 1 0
for(int i=1;i<size;++i)
{int key =array[i];//保存当前数int end = i-1;while(key < array[end]&& end>=0){array[end+1] = array[end];//往后搬移end--;}array[end+1]=key
}
void InsertSort(int *array, int size)
{for (int i = 1; i < size; ++i){int key = array[i];int end = i - 1; //i前面的已经排好序了while (key < array[end] && end >= 0){array[end + 1] = array[end];end--;}array[end + 1] = key;}
}
希尔排序
希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为的记录分在同一组内,并对每一组内的记录进行排序。然后,取,重复上述分组和排序的工作。当到达=1时,所有记录在统一组内排好序。
希尔排序的特性总结:
- 希尔排序是对直接插入排序的优化。
- 当gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
- 希尔排序的时间复杂度不好计算,需要进行推导,推导出来平均时间复杂度: O(N1.3—N2)没有到O(N2)
- 稳定性:不稳定‘
void ShellSort(int* array, int size)
{int gap = size;while (gap > 1) {gap = gap / 3 + 1;for (int i = gap; i < size; ++i)//i每回加1,让分组同时进行排序,不需要+gap{int key = array[i];int end = i - gap; //i的前一个元素while (key < array[end] && end >= 0){array[end + gap] = array[end];end -= gap;}array[end + gap] = key;} }
}
选择排序
基本思想:
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完 。
直接选择排序:
- 在元素集合
array[i]--array[n-1]
中选择关键码最大(小)的数据元素 - 若它不是这组元素中的最后一个(第一个)元素,则将它与这组元素中的最后一个(第一个)元素交换
- 在剩余的
array[i]--array[n-2](array[i+1]--array[n-1])
集合中,重复上述步骤,直到集合剩余1个元素
直接选择排序的特性总结:
- 直接选择排序思考非常好理解,但是效率不是很好。实际中很少使用
- 时间复杂度:O(N^2)
- 空间复杂度:O(1)
- 稳定性:不稳定
代码实现
void Swap(int *pLeft, int *pRight)
{int temp = *pLeft;*pLeft = *pRight;*pRight = temp;
}
//直接选择排序
void SelectSort(int*array, int size)
{for (int i = 0; i < size - 1; ++i) //总共找多少次,-1是因为最后一次,区间中只有一个元素了{//找区间中最大元素的位置//找最大元素的方式int maxPos = 0; //找最大的元素for (int j = 1; j < size - i; ++j) //-i,从没排序的序列中找最大的元素{if (array[j]>array[maxPos]) //如果大,下标变更maxPos = j;}if (maxPos != size - i - 1) //如果最大元素下标不等于此时当前没排序区间的最后一个元素Swap(&array[maxPos], &array[size - i - 1]);//最大位置的元素和最后位置的元素交换}
}
直接选择排序优化
一次排序时,找出当前区间,当前区间,当前区间最大和最小的元素,最大元素和当前区间的最后一个元素交换最小元素和当前区间第一个元素进行交换,交换完,区间缩小,重复操作,直到有序
//选择排序优化
void SelectSort_OP(int*array, int size)
{int begin = 0;int end = size - 1;while (begin < end){int maxPos = begin;//认为当前区间第一个元素最大int minPos = begin;//认为当前区间第一个元素最小int i = begin;//i从当前区间第一个元素开始//在区间中找到最大和最小元素的位置while (i <= end){if (array[i]>array[maxPos])maxPos = i;if (array[i] < array[minPos])minPos = i;i++;}//最大和最小元素和当前区间第一位置和最后一位置进行交换if (maxPos != end)Swap(&array[maxPos], &array[end]);//交换的是下标里的值,下标并没有改变//如果当前区间最后的元素放的正好是你找到最小元素,更新最小元素的位置if (minPos == end)minPos = maxPos;if (minPos != 0)Swap(&array[minPos], &array[begin]);//缩小区间begin++;end--;}
}
直接选择排序的缺陷
每选一次都要从前往后再比一次
堆排序
堆的简介与相关实现
堆排序的特性总结:
- 堆排序使用堆来选数,效率就高了很多。
- 时间复杂度:O(N*logN)
- 空间复杂度:O(1)
- 稳定性:不稳定
代码实现
//O(logN)
void HeapAdjust(int*array, int size, int parent)
{int child = parent * 2 + 1; //左孩子//int right = parent * 2 + 2;//右孩子while (child<size){//找左右孩子中最大的if (child+1<size && array[child + 1] > array[child])child += 1;//检测双亲是否满足堆的性质if (array[child] > array[parent]) //不满足{Swap(&array[child], &array[parent]);parent = child;child = parent * 2 + 1;}elsereturn;}
}//O(NlogN)
void HeapSort(int *array, int size)
{//建堆----升序(大堆) 降序(小堆)//从倒数第一个非叶子结点----向下调整//size-1数组中最后一个数,parent=(child-1)/2;int last = ((size - 1 - 1) / 2);for (; last >= 0; last)HeapAdjust(array, size, last--);//排序---堆的删除int end = size - 1; //剩余排序的元素个数while (end){Swap(&array[0], &array[end]);HeapAdjust(array, end, 0);--end;}
}