选择排序算法准则:
每种排序算法都各有优缺点。
影响排序的因素有很多,平均时间复杂度低的算法并不一定就是最优的。相反,有时平均时间复杂度高的算法可能更适合某些特殊情况。同时,选择算法时还得考虑它的可读性,以利于软件的维护。一般而言,需要考虑的因素有以下四点:
1.待排序的记录数目n的大小;
2.记录本身数据量的大小,也就是记录中除关键字外的其他信息量的大小;
3.关键字的结构及其分布情况;
4.对排序稳定性的要求。
设待排序元素的个数为n.
1)当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。
**快速排序**:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短;
**堆排序** : 如果内存空间允许且要求稳定性的,
**归并排序**:它有一定数量的数据移动,所以我们可能过与插入排序组合,先获得一定长度的序列,然后再合并,在效率上将有所提高。
2)当n较大,内存空间允许,且要求稳定性 ——归并排序
3)当n较小,可采用直接插入或直接选择排序。
**直接插入排序**:当元素分布有序,直接插入排序将大大减少比较次数和移动记录的次数。
**直接选择排序** :元素分布有序,如果不要求稳定性,选择直接选择排序
4)一般不使用或不直接使用传统的冒泡排序。
5)基数排序
它是一种稳定的排序算法,但有一定的局限性:
1、关键字可分解。
2、记录的关键字位数较少,如果密集更好
3、如果是数字时,最好是无符号的,否则将增加相应的映射复杂度,可先将其正负分开排序
插入排序:
直接插入排序
思想:将数组中的所有元素依次和前面的已经排好序的元素相比较(依次),如果选择的元素比已排序的元素小,则交换,直到全部元素都比较过。
#include <stdio.h>
#include<iostream>
using namespace std;
void display(int array[], int size) {
for (int i = 0; i < size; i++) {
printf("%d ", array[i]);
}
printf("\n");
}
void InsertSort(int array[], int size) {
for (int i = 1; i < size; i++) { // 第 1 个数肯定是有序的,从第 2 个数开始遍历,依次插入有序序列
int temp = array[i]; // 取出第 i 个数,和前 i-1 个数比较后,插入合适位置
int j = i - 1; // 因为前 i-1 个数都是从小到大的有序序列,所以只要当前比较的数 (array[j]) 比 temp 大,就把这个数后移一位
while (j >= 0 && array[j] > temp) { // 当 j < 0 或 array[j] < temp(array[i]) 时终止
array[j + 1] = array[j]; // 将大于 temp(array[i]) 的数据后移
j--; // 向前比较
} // 结束循环
array[j + 1] = temp; // array[i]插入到正确的位置上
//打印每次排序结果
display(array, size);
}
}
int main() {
int array[] = { 49, 38, 65, 97, 76, 13, 27, 49, 10 };
int size = sizeof(array) / sizeof(int);
//打印原始数据
printf("%d \n", size);
display(array, size);
InsertSort(array, size);
system("pause");
return 0;
}
最好的情况:数组本身就是有序的,只是进行比较,时间复杂度是O(n)
最坏的情况:数组是逆序的,时间复杂度O(n²)
平均情况:O(n²)
希尔排序
思想:希尔排序也称缩小增量排序;希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序,随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时(用gap = gap/3+1 控制),保证了最后一次进行直接插入排序,算法终止。(其中直接插入排序是希尔排序gap=1的特例)另外,gap越大,值越大的容易到最后面,但是不太接近有序。—— 一般gap不要超过数组大小的一半
https://www.bilibili.com/video/BV1rE411g7rW?from=search&seid=8131314960769167697
void print(int a[], int n ,int i){
cout<<i <<":";
for(int j= 0; j<8; j++){
cout<<a[j] <<" ";
}
cout<<endl;
}
/**
* 直接插入排序的一般形式
*
* @param int dk 缩小增量,如果是直接插入排序,dk=1
*
*/
void ShellInsertSort(int a[], int n, int dk)
{
for(int i= dk; i<n; ++i){
if(a[i] < a[i-dk]){ //若第i个元素大于i-1元素,直接插入。小于的话,移动有序表后插入
int j = i-dk;
int x = a[i]; //复制为哨兵,即存储待排序元素
a[i] = a[i-dk]; //首先后移一个元素
while(x < a[j]){ //查找在有序表的插入位置
a[j+dk] = a[j];
j -= dk; //元素后移
}
a[j+dk] = x; //插入到正确位置
}
print(a, n,i );
}
}
/**
* 先按增量d(n/2,n为要排序数的个数进行希尔排序
*
*/
void shellSort(int a[], int n){
int dk = n/2;
while( dk >= 1 ){
ShellInsertSort(a, n, dk);
dk = dk/2;
}
}
int main(){
int a[8] = {3,1,5,7,2,4,9,6};
//ShellInsertSort(a,8,1); //直接插入排序
shellSort(a,8); //希尔插入排序
print(a,8,8);
}
选择排序
选择排序
思想:在一个无序数组中选择出每一轮中最值元素,然后把这一轮中最前面的元素和min交换,最后面的元素和max交换;然后缩小范围(开始位置(begin++)++,最后位置(end - -)- -),重复上面步骤,最终得到有序序列(升序)。
void SelectSort(int* a,size_t n)
{
assert(a);
int begin = 0;
int end = n-1;
while ( begin < end )
{
int min = begin,max = begin;
for(int i = begin; i <= end; ++i)
{
if( a[min] > a[i] )
min = i;
if( a[max] < a[i] )
max = i;
}
swap( a[min],a[begin] );
if( max == begin )//如果首元素是最大的,则需要先把min 和 max的位置一换,再交换,否则经过两次交换,又回到原来的位置
max = min;
swap( a[max],a[end] );
begin++;
end--;
}
}
堆排序
思想:
堆排序利用了大堆(或小堆)堆顶记录的关键字最大(或最小)这一特征,使得当前无序的序列中选择关键最大(或最小)的记录变得简单。(升序—建大堆,降序—建小堆)
#include<iostream>
#include<vector>
using namespace std;
// 递归方式构建大根堆(len是arr的长度,index是第一个非叶子节点的下标)
void adjust(vector<int> &arr, int len, int index)
{
int left = 2 * index + 1; // index的左子节点
int right = 2 * index + 2;// index的右子节点
int maxIdx = index;
if (left<len && arr[left] > arr[maxIdx]) maxIdx = left;
if (right<len && arr[right] > arr[maxIdx]) maxIdx = right;//找到树里的最大值的下标
if (maxIdx != index)//最大值的下标不是树顶
{
swap(arr[maxIdx], arr[index]);
adjust(arr, len, maxIdx);
}
}
// 堆排序
void heapSort(vector<int> &arr, int size)
{
// 构建大根堆(从最后一个非叶子节点向上)
for (int i = size / 2 - 1; i >= 0; i--)
{
adjust(arr, size, i);
}
// 调整大根堆
for (int i = size - 1; i >= 1; i--)
{
swap(arr[0], arr[i]); // 将当前最大的放置到数组末尾
adjust(arr, i, 0); // 将未完成排序的部分继续进行堆排序
}
}
int main()
{
vector<int> arr = { 8, 1, 14, 3, 21, 5, 7, 10 };
heapSort(arr, arr.size());
for (int i = 0; i<arr.size(); i++)
{
cout << arr[i] << endl;
}
system("pause");
return 0;
}
交换排序
冒泡排序
基本思想就是:从无序序列头部开始,进行两两比较,根据大小交换位置,直到最后将最大(小)的数据元素交换到了无序队列的队尾,从而成为有序序列的一部分;下一次继续这个过程,直到所有数据元素都排好序。
算法的核心在于每次通过两两比较交换位置,选出剩余无序序列里最大(小)的数据元素放到队尾。
#include<iostream>
#include<vector>
using namespace std;
vector<int> sort(vector<int> &ivec);
int main()
{
cout << "请输入想要排序的数字:" << endl;
int i = 0;
vector<int> ivec;
while (cin >> i)
{
ivec.push_back(i);
}
cout << "为排序前的序列:" << endl;
for (vector<int>::iterator iter = ivec.begin();
iter != ivec.end(); ++iter)
{
cout << *iter << " ";
}
cout << endl;
cout << "排序后的序列如下:" << endl;
//排序
vector<int> i_vec = sort(ivec);
//输出
for (vector<int>::iterator i_iter = i_vec.begin();
i_iter != i_vec.end(); ++i_iter)
{
cout << *i_iter << " ";
}
cout << endl;
system("pause");
return 0;
}
vector<int> sort(vector<int> &ivec)
{
const int COUNT = ivec.size();
for (int i = 1; i<COUNT; i++)//这一行只是为了排除最后已经沉到最底下的元素
{
for (int j = 0; j<COUNT - i; ++j)
{
if (ivec[j]>ivec[j + 1])
{
int temp = 0;
temp = ivec[j];
ivec[j] = ivec[j + 1];
ivec[j + 1] = temp;
}
for (vector<int>::iterator i_iter = ivec.begin();
i_iter != ivec.end(); ++i_iter)
{
cout << *i_iter << " ";
}
cout << endl;
}
}
return ivec;
}
快速排序
基本思想:
1)选择一个基准元素,通常选择第一个元素或者最后一个元素,
2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的元素值比基准值大。
3)此时基准元素在其排好序后的正确位置
4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。
void quickSort(int *array, int left, int right)
{
if(left < right)
{
int pivot = array[left];
int low = left, high = right;
while(low < high)
{
while(array[high] >= pivot && low < high)
high--;
array[low] = array[high];
while(array[low] <= pivot && low < high)
low++;
array[high] = array[low];
}
array[low] = pivot;
quickSort(array, left, low - 1);
quickSort(array, low + 1, right);
}
}
归并排序
#include <iostream>
#include <algorithm>
#define MAXSIZE 1000
using namespace std;
void swap(int array[], int i, int j)
{
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
void Merge(int src[], int des[], int low, int mid, int high)
//归并的过程
//src是数组,des是目的地
{
int i = low;
int j = mid + 1;
int k = low;
while ((i <= mid) && (j <= high))//将小的放到目的地中
//这个如果没有等于号就不是稳定排序
{
if (src[i] < src[j]) des[k++] = src[i++];
else des[k++] = src[j++];
}
while (i <= mid)//若还剩几个尾部元素
des[k++] = src[i++];
while (j <= high)//若还剩几个尾部元素
des[k++] = src[j++];
}
//每次兵分两路,当只剩下一个元素时,就不需要再划分
void Msort(int src[], int des[], int low, int high, int max)
//划分的过程
{
if (low == high)//只有一个元素,不用归并,结果赋给des[low]
des[low] = src[low];
else //如果两个元素,进行二路划分
{
int mid = (low + high) / 2;
int TR2[MAXSIZE+1];//辅助数组
//递归进行两路两路的划分
//当剩下一个元素的时候,递归划分结束,然后开始merge归并操作
Msort(src, TR2, low, mid, max);
Msort(src, TR2, mid + 1, high, max);
Merge(TR2, des, low, mid, high);//调用归并函数进行归并
}
}
void MergeSort(int array[], int len)
{
Msort(array, array, 0, len - 1, len);
//Msort(原数组,目标数组,原数组中的最低位和最高位,长度)
}
int main()
{
int array[] = { 12, 5, 433, 7, 98, 32, 11, 8, 2, 56 };
int len = sizeof(array) / sizeof(*array);
cout << "before sorting:" << endl;
for (int i = 0; i <len; ++i)
{
cout << array[i] << " ";
}
cout << endl;
MergeSort(array, len);
cout << "after sorting:" << endl;
for (int i = 0; i <len; ++i)
{
cout << array[i] << " ";
}
cout << endl;
system("pause");
return 0;
}