排序算法C++实现
须知
算法使用模板进行编写,有1个错误的排查并没有完成,我会标注在代码上的
递归——顺序查找算法
因为代码十分简短,故没有对代码逻辑进行描述,递归就行了
template<class T>
int sequential_search(T a[], int n,const T&x)
{
if (n < 1)return -1;
if (a[n - 1] == x)return n - 1;
else return sequential_search(a, n - 1, x);
}
等级排序
这个算法的思想就是a[]为输入的乱序数组,n为元素数量,r[]用来装载数组a中每个元素在数组中的大小顺序,但没有对a进行排序移位,下边还有一段被注销的代码,那个算法的效率是比上边算法效率低的,然后下边的rear_range函数就是用来对数组a依据数组r进行重排序的,n依然为数组大小
//template<class T>
//a[]原本是T类的声明
void rank_(int a[], int n, int r[])
{
for (int i = 0; i < n; i++)
{
r[i] = 0;
}//初始化
for (int i = 1; i < n; i++)
{
for (int j = 0; j < i; j++)
{
if (a[j] <= a[i])
r[i]++;
else
r[j]++;
}
}
//j从0开始,为内循环,当j=i时停止内循环
//i从1开始,每一个a[i]都和它前边的i个数比较,大的顺序自增,如a中第二个三,达成一次自增,则顺序为1
//如果出现后边还有小的数,如4,3,9,3,7,2
//第一个3和前边的4对比,4的序号加一,轮到2和4,3,9,3,7对比时,它们全增一
//其实这就是一个排序
//不同于书上的排序
/*
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
if (a[j] >= a[i])
r[j]++;
else
r[i]++;
}
}
//此处j也进行n次循环,则r中最小的数会为2,而且是两个2
//每一个a[i]都和所有的a对比,会出现和自己对比
//自增一次,和另一个相同的数对比,自增一次,结果即2
//故当数组内没有相同的数时最小的排序值为1
//出现两个相同的数时则为2,以后同理,用j<i作为条件可以避免
//每一个a[i]只和它前边的对比,不会和自身对比故从零自增
//后边相同的数和自己对比出现if中的条件
//然后增的是后来的,不是内循环中的,故还能排序先出和后出现
*/
template<class T>
void rear_range(T a[], int n, int r[])
{
T*u[] = new T[n];
for (int i = 0; i < n; i++)
u[r[i]] = a[i];
//把a中的数按照r得到的名次进行排序
//即4去3号位,3去1号位,9去5号位,3去2号位,7去4号位
for (int i = 0; i < n; i++)
a[i] = u[i];
delete[]u;
}
}
最大值函数和交换函数
实际上进行排序的时候,加入一些简单的工具性函数更高效(写代码的时候)
int index_of_max(T a[], int n)
{//找最大值函数,n为数组前n个数
if (n <= 0)
{
cout << "error!" << endl;
return - 1;
}
//从前n-1个数中找到最大的数,进行n-1次比较
int indexofmax = 0;
for (int i = 1; i < n; i++)
if (a[indexofmax] < a[i])
indexofmax = i;
return indexofmax;
}
void swap(int&a,int&b)
{
int temp = a;
a = b;
b = temp;
}
选择排序算法1
这个算法比较简单暴力,很容易懂,找到最大的,放到最后,然后不管了,开始循环这样的操作
template<class T>
void selection_sort(T a[], int n)
{//选择排序
for (int i = n - 1; i > 0; i--)
{
int j = index_of_max(a, i);
//得到前i个数中最大的值的位置,下一次i自减
swap(a[i], a[j]);
//交换第i+1和最大值数的位置
//此选择排序执行次数每次都不会变,即总为n-1次
}
}
选择排序算法2
这个算法设置了flag,可以用来减少排序次数,并且每次排序的时候会检查整个数组是否完成排序,可以算是优化了吧
template<class T>
void selectionSort(T a[], int n)
{
bool sorted = false;
for (int size = n; !sorted && (size > 0); size--)
{//从后往前放,从大放到小
int index_of_max = 0;
//不管最大的一开始是哪个,先从第一个开始
sorted = true;
//如果没有出现逆序,就停止最外层循环
for (int i = 0; i < size; i++)
{//不管已经排序的部分,减少排序次数
if (a[index_of_max] < a[i])index_of_max = i;
//若出现当前index错误则纠正
else sorted = false;
//若内循环的全部次数里出现了一次index错误即表示没有排序完,需要再进行排序
}
swap(a[index_of_max], a[size - 1]);
//对当前未排序的部分的最后一个数进行排序
}
}
冒泡算法
相邻比较,两个相邻的数大的向右移动,小的向左移动,就算冒个泡,冒泡次数很明显是不会变化的
//template<class T>
void bubble(int a[], int n)
{//把a[0:n-1]中最大元素移到最右边
for (int i = 0; i < n - 1; i++)
if (a[i] > a[i + 1])
swap(a[i], a[i + 1]);
}
//template<class T>
void bubble_sort(int a[], int n)
{
for (int i = n; i > 1; i--)
bubble(a, i);
}
及时终止的冒泡算法
变化在哪?冒泡算法用了bool型,设立了flag,如果一次冒泡中后边的数存在已完成排序的情况,由flag表示冒泡完成,减少的排序次数应该是在冒泡排序算法上,如果某一次冒泡时已经是顺序了,就不用冒泡
template<class T>
bool bubble(T a[], int n)
{
//及时终止的冒泡
bool swapped = false;//先设置一个flag为false
for (int i = 0; i <= n - 1; i++)
{
//循环n次
if (a[i] > a[i + 1])
{
swap(a[i], a[i + 1]);//传统冒泡行为
swapped = true;//如果存在冒泡行为即表示没有完成排序
}
}
return swapped;
}
template<class T>
void bubble_sort(T a[], int n)
{
for (int i = n - 1; i > 0 && bubble(a, i); i--);
//最多进行n次循环,如果存在没有冒泡行为就停止
//即当bubble为false,即完成排序,即可省略之后的循环次数
}
插入排序1
插入排序总共有三个算法,这是第一个
template<class T>
void insert0(T a[], int& n, const T&x)
{
//插入排序即在有序数组中插入一个元素
//其中n为数组中元素个数,并不是数组最大容量
//数组最后一个数为a[n-1]
int i;//i需申明在循环以外,否则会报错的
for (i = n - 1; i >= 0 && x < a[i]; i--)
a[i + 1] = a[i];//比x大的数往后移
a[i + 1] = x;
//跳出循环即x>=a[i],则有x应当处于a[i]后边一位
n++;//总数加一
}
插入排序2
插入排序总共有三个算法,这是第二个
template<class T>
void insert(T a[], int& n, const T&x)
{//插入排序即在有序数组中插入一个元素
//其中n作为最后一个数的标志,即把x插入有序数组a[0:n-1]中
//下边用循环中变化的i表示有序数组的大小
//其中n为数组中元素个数,并不是数组最大容量
//数组最后一个数为a[n-1]
int i;//i需申明在循环以外
for (i = n - 1; i >= 0 && x < a[i]; i--)
a[i + 1] = a[i];//比x大的数往后移
a[i + 1] = x;
//跳出循环即x>=a[i]
//则有x应当处于a[i]后边一位
}
template<class T>
void insert_sort(T a[], int n)
{
for (int i = 1; i < n; i++)
{
T t = a[i];
//获得当前最后一个未排序的数值
insert(a, i, t);
//就像放麻将一样,拿起最后未排序的数放入应该放的位置
//所有比它大的数提前后移,这是插入与放麻将的一点区别
}
}
插入排序3
插入排序总共有三个算法,这是第三个
template<class T>
void insertionsort(T a[],int n)
{
for (int i = 1; i < n; i++)
//内循环决定从后向前进行对比,则外循环需从零开始增加
{
T t = a[i];//当前未排序的数的值
int j;
for (j = i-1; j >=0 && (t < a[j]);j--)
//①前面一个数跟后面一个数对比,从后向前比
//只有当前未排序的数小于以此遇到的数才能继续循环
a[j+1]=a[j];
//②因为已经用t寄存当前插入数值,故可视后边一个位置为空
//各个比当前数值大的数都后移,以留出当前数值的位置
a[j + 1] = t;
//③此时j记录的是比当前数值小的数的位置
//因为当前数值要放在该位置之后一位,故用j+1
}
}
归并排序(正确)
这是网上借鉴来的一个算法,我用三目运算符简化了一下代码
//对已排好的两个数组进行合并
void merge(int *arr, int low, int mid, int high)
{//从数组arr[low:high]
int i, j, count = 0;
int *data = new int[high - low + 1];
for (i = low, j = mid + 1; i <= mid && j <= high; count++)data[count] = (arr[i] < arr[j]) ? arr[i++] : arr[j++];
//进行循环直到for循环里的条件被破坏,则一定会在下列两种情况中选出一种
for (; i <= mid;count++,i++)data[count] = arr[i];
for (; j <= high;count++,j++)data[count] = arr[j];
i = low;
for (int k = 0; k < count&&i <= high; k++, i++)arr[i] = data[k];
delete[] data;
}
//分而治之,将长数组一直进行二等分,当分到只剩一个时返回
void Msort(int* arr, int low, int high)
{
if (low >= high){return;}
int mid = (low + high) / 2;
Msort(arr, low, mid);
Msort(arr, mid + 1, high);
merge(arr, low, mid, high);
}
归并排序(故障)
这个算法来自《无处不在的算法》机械工业出版社,原本好像是java写的,我用c++改了一下,但改来改去都不能出正确结果,有大佬会改的话求指教!我是真的尽力从理论上在修改过了一个晚上,但是奈何…其实放在这里既算是一个记录,也是一个求助
template<class T>
void merge(T a[], int al, int ar, T b[], int bl, int br, T c[])
{
//用三目运算符在ar和br之前把小的数放入c中,遇到超出ar或br就放没超出的那个,执行次数为两个数组之和
//该函数用于把两个有序的数组合并到一起
int i = al, j = bl;
for (int k = 0; k < ar - al + br - bl + 1; k++)
{
if (i < ar&&j < br)c[k] = (a[i] < b[j]) ? a[i++] : b[j++];
if (i > ar)c[k] = b[j++];
if (j > br)c[k] = a[i++];
}
}
template<class T>
void merge_sort(T a[], int al, int ar)
{
if (ar > al)
{
int m = (ar + al) / 2;//找到中间点
merge_sort(a, al, m);
//用递推法把a在m的左右逐步分为最小个数,然后执行下方排序
merge_sort(a, m + 1, ar);
T*b = new T[ar - al + 1];
//声明一个新数组临时存储有序的a
merge(a, al, m, a, m + 1, ar, b);
//把有序的数逐步合起来
for (int i = 0; i < ar - al + 1; i++)
a[al + i] = b[i];
}
}
快速排序
代码里含有用于观察数字如何变化的cout代码,都已经注释掉了,可以直接用哒
void quickSort(int *A, int al, int ar)
{
if (al < ar)
{
int pivot = A[al];
int i = al, j = ar + 1;
while (true)
{
while (A[++i] > pivot&&i < ar)
{
//cout << "达成 i 的移动" << endl;
};
while (A[--j] < pivot&&j > al)
{
//cout << "达成 j 的移动" << endl;
};
if (i < j)
{
//cout << "交换" << A[i] << " 和 " << A[j] << endl;
swap(A[i], A[j]);
}
else
break;
}
//cout << endl << "还未移动pivot,此时已完成除pivot以外的所有数的移动,当前数组顺序如下:" << endl;
//for (int k = 0; k < ar - al + 1; k++)
//cout << A[k] << " ";
//cout << endl;
//cout << " j= " << j <<" i= "<<i<< endl;
//上方步骤用来把pivot两边的数值堆起来,两边的里层顺序并没有排序
swap(A[al], A[j]);
//此步骤用于把pivot放到它应该在的位置
//之后就是把pivot的左右两边用递归来排序的,也是再找各自的左边第一个数为pivot,然后把pivot再放到正确位置,再对这个pivot的左右进行排序,然后回归到最外层
quickSort(A, al, j - 1);
quickSort(A, j + 1, ar);
}
}