#include <iostream>
#include <ctime>
using namespace std;
/***
* 排序算法的总结:
* 排序分为稳定排序和不稳定排序
* 稳定排序:相同的值的位置在排序过程中不会发生改变
* 非稳定排序:相同的值的相对位置也许会在算法结束时产生变动
* 1、冒泡排序:什么叫冒泡排序?O(n^2)
* 主要的思想就是:1>、每次比较相邻的元素,如果前者大于后者就将它们的值进行交换
* 依次循环n-1次就行--->因为每次循环都把最大的元素放在了最后面,n-1次后最前面的元素自然是最小的了
* 下面是主要的逻辑代码:
* for(int i=0;i<9;++i)
{
for(int j=0;j<9;++j)
{
if(arr[j]>arr[j+1])
swap(arr[j],arr[j+1]);
}
}
*2、接下来介绍的就是选择排序法:
× 应用范围:适合于小规模的排序算法中,因为它的时间复杂度为n^2,每次都要循环遍历一遍数组才能确定一个值,如果有n个值每个数都要进行n次循环,从而算法的时间复杂度为n*n=n^2
*何为选择排序法呢?------>所谓的选择就是从第n个元素开始
* 每次从n后的数组中找到一个最小或者最大的元素与第n个元素进行交换
* 从而可以确保最前面的元素一定是最小的元素,依次往后增加
* 下面是主要的逻辑代码:
* for(int i=0;i<9;++i)
{
int min=i;
for(int j=i+1;j<10;j++)
if(arr[min]>arr[j])
min=j;
swap(arr[i],arr[min]);
}
*3.再接下来就是我们的插入排序算法:
* 应用范围:适用于小规模的排序算法中,且是基本有序的数组序列中,因此希尔排序对有序性做出了改进。
* 定义:在一个已经有序的数组中插入一个元素要使数组在插入之后依旧是有序的的一种排序算法
* 既然叫插入排序肯定就与插入有关---->在前面找到一个合适的位置然后将本元素插入进去,依次循环直到最后一个
* 那么插入的是什么样的元素呢?
* 1>、从后往前依次去比较前者大于后者则前者覆盖后者的位置
* 下面是主要的逻辑代码:
* for(int i=1;i<10;++i)
{
int temp=arr[i]; //由于会被前面的覆盖,所以要保存起来
for(j=i-1;j>=0&&arr[j]>temp;--j)
arr[j+1]=arr[j];
arr[j+1]=temp;
}
* 2>、另一种思路就是:从前往后依次去比较
* 下面是主要的逻辑代码:
* for(int i=1;i<10;++i)
{
for(int j=0;j<i;++j)
if(arr[i]<arr[j])
swap(arr[i],arr[j]);
}
* 接下来就是对前面的排序算法的升级版:
* 按照顺序首先是冒泡排序算法的升级------->快速排序算法
* 应用范围:快速排序时间复杂度为(n*logn)因此使用于大规模的排序中
* 那么它们有什么相似点呢?快速排序又在那些方面对冒泡排序做出了升级
* 让我们来一探究竟:首先相似点为;都是通过依次排序后使的前面的比后面的大
* 下面是完整的代码:
* bool Quick(int arr[],int low,int high)
{
if(low>high)return false; //当它的左边的序列大于右边的时就退出----因为low-high之间所围成的区域就是我们要求的小于key值的区域
int i=low; //小于的时候说明黎明没有一个元素自然就没有求的必要了,因此直接返回即可
int j=high; //j是用来作为分界点用的--->j以后的都是比key值大的,j以前的都是比key小的
int key=arr[low]; //取最低位为key值
while(i<j)
{
while(i<j&&arr[j]>key)
j--;
while(i<j&&arr[i]<key)
i++;
swap(arr[i],arr[j]);
}
Quick(arr,low,j-1);
Quick(arr,j+1,high);
//寥寥几笔就完成了快速排序------>看起来确实简单,但是中间的过程一定要思考清楚
}
*主要的思路(再次总结):按习惯取最左边的值为key值,
*再选择好分界下标如上面的i,j其中以j为分割线j+1为大于key的,j-1为小于key的值
* 通过递归结构再分别对左边的数组与右边的数组进行相同的操作
* 在接下来就是对上述的选择排序法的升级了:堆排序的算法:
* 堆排序算法的时间复杂度为:n*logn,使用于小规模的排序算法中,而为什么同样的时间复杂度的快速排序却可以用于大会规模的排序算大中去,
× 评论一个算法的好坏还要分析它的空间复杂度,堆排序的时间复杂度固然低,但是它的空间复杂度却会随着数据量的增大而增大,并且可能还会有很多的空间的浪费
* 相同点:都是利用了在数组中寻找最大/最小的元素去对填补最前面的元素与最后面的元素
* 不同点:他们的寻找最大值或最小值的方法不同,堆排序利用堆的特性而选择排序利用的则是循环遍历
* 用数组来实现堆:堆分为最大堆和最小堆
* 堆排序正是利用了堆的这个特点:
* 1、先利用数组中的元元素构造出一个最大堆或者最小堆出来
* 2、再将最大堆或者最小堆中的元素与堆的最后的一个元素进行交换
* 3、利用堆的自构性-----再次取出最上面的元素与倒数第二个元素进行交换
* 4、依次循环直到最后一个元素,至此排序就完成了!
* 下面我们来具体的实现这个排序算法:
* bool HeapSort(int arr[],int low,int high)
{
int temp=arr[low];//首先要做的事情就是保存当前的值,理由见插入排序-----**
int index=low; //temp的值不会变但是他的位置却会发生改变,所以用index来保存他的下标;---***
for(int i=low*2+1;i<high;i=2*i+1)
{
if(i+1<high&&arr[i]<arr[i+1]) //先看左孩子与右孩子那个的值大
i++;
if(temp>=arr[i]) //再将大的值与父亲的值去进行比较
break;
swap(arr[index],arr[i]); //父节点的值如果小于则退出
index=i;
}
return true;
}
* 在接下来就是对上述的插入排序法的升级了:希尔排序算法--------->*****
* 与插入排序算法的区别在于,希尔排序算法中增加了增量这一个词,主要就是为了完成对数组的一个基本的有序排列
* 可以减少插入排序的移动次数从而提高效率:
* 希尔排序的主要思想在于:将原本一组数据分成多个数据,分别对这多个数据组去进行直接插入排序(就是比较大小然后在交换值)
* 然后在减少增量(减少分组数)进行相同的操作
* 最后减少成一组进行相同的操作从而达到了排序的目的
* 最后可能还要配合插入排序已达到最佳的效果
× 希尔排序算法讲白了还是简单的直接插入排序:只不过在进行真正的直接差入排序之前对数据做了一个基本的有序化罢了
× 因此希尔排序算法的最后的增量一定要是1,因为1才是真正的直接插入排序
* 从而在时间复杂度上:为n^1.3-2 ,实际的大小还取决与增量的选定
* 代码如下:
* bool shellSort(int arr[],int low,int high)
{
for(int gap=high>>1;gap>=1;gap/=2) //增量gap相当于组数
{
for(int i=gap;i<high;++i)
{
int j=i; //用来记录其中的值----------------如在一个增量组中将一个小的数换到前面来,如果这个较小的数的后面还有数,那么这个较小的数
//也有可能比它前面的数还要大于是我们要保留他的系数让它继续与它前面的数去进行比较
while(j-gap>=0&&arr[i]<arr[j-gap])
{
swap(arr[i],arr[i-gap]);
j-=gap;
}
}
}
return true;
}
现在来介绍一个以空间换取时间的排序算法:
*桶(基数排序算法)排序算法:
* 优点是:排序的过程中不用比较因此算法的效率比较高
* 缺点嘛:他只能用来做整数排序,可以说是整数排序内无敌手---从而缺点也就很明显的
* 还有一个缺点就是它的空间浪费比较大:
bool Sort(int arr[],int low,int high)
{
int temp[10][10]={};
for(int n=1;n<1000;n*=10) //小于1000说明目前的这个排序算法只对1-1000内的数有效果
{
for(int i=0;i<10;++i)
{
for(int j=0;j<high;++j)
{
temp[i][j]=-1;
}
}
for(int j=0;j<high;++j)
{
int index=(arr[j]/n)%10; //先对他的个位数的大小去排序,在对他的十位数去排序,再对他的百位数等等---------
temp[index][j]=arr[j]; //如某个数的个位数是3则把他放到第三行去,列就是他在数组中的列
}
int k=0;
for(int i=0;i<10;++i)
{
for(int j=0;j<10;++j)
{
if(temp[i][j]!=-1)
arr[k++]=temp[i][j]; //从二维的数组中依次去把它里面的值给取出来再依次放到数组中去
}
}
}
return true;
}
*
现在补全最后一个排序算法-------》归并排序
归并排序重在对其思想的理解:
归并排序是把一个数组分成两个有序的子数组在把它们合并起来
而用到的方法就是分治法,白话就是,当事情太多时我们就得一个一个的做好,到最后自然所有的事就全部做好了
下面是他的代码:
const int MAXSIZE = 100;
typedef struct SQlist
{
int arr[MAXSIZE];
int Len;
} SQlist;
bool Init_SQlist(SQlist &space)
{
memset(space.arr, 0, sizeof(space.arr));
space.Len = 0;
return true;
}
bool EnSQlist(SQlist &space)
{
space.Len = 10;
for (int i = 0; i < space.Len; ++i)
{
space.arr[i] = rand() % 100;
}
return true;
}
bool Merge(SQlist &space, int left, int mid, int right)
{
int Length = (right - left) + 1;
int *pData = new int[Length];
memset(pData, 0, sizeof(int) * Length);
int low = left;
int hig = mid + 1;
int Index = 0;
while (low <= mid && hig <= right)
{
while (low <= mid && space.arr[low] <= space.arr[hig])
{
pData[Index++] = space.arr[low++];
}
while (hig <= right && space.arr[hig] < space.arr[low])
{
pData[Index++] = space.arr[hig++];
}
}
while (low <= mid)
{
pData[Index++] = space.arr[low++];
}
while (hig <= right)
{
pData[Index++] = space.arr[hig++];
}
memcpy(&space.arr[left], pData, sizeof(int) * Length);
return true;
}
bool Merger_sort(SQlist &space, int left, int right)
{
if (left >= right)
return true;
else
{
int mid = ((right - left) >> 1) + left; //求区间的中间坐标
//还有一种写法就是---------------->(right+left)>>1不过这种写法可能会越界,因此相对保守的就是上面的写法
Merger_sort(space, left, mid);
Merger_sort(space, mid + 1, right);
Merge(space, left, mid, right);
}
return true;
}
bool print(SQlist &space)
{
for (int i = 0; i < space.Len; ++i)
{
if (i == 0)
cout << space.arr[i];
else
cout << " " << space.arr[i];
}
cout << endl;
return true;
}
int main()
{
srand(time(NULL));
SQlist space;
Init_SQlist(space);
EnSQlist(space);
print(space);
Merger_sort(space, 0, space.Len - 1);
print(space);
return 0;
}
*/
bool shellSort(int arr[],int low,int high)
{
for(int gap=high/2;gap>0;--gap)
{
for(int i=gap;i<high;++i)
{
int j=i;
while(j-gap>=0&&arr[i]<arr[j-gap])
{
swap(arr[i],arr[j-gap]);
j-=gap;
}
}
}
return true;
}
bool HeapSort(int arr[],int low,int high)
{
int temp=arr[low];//首先要做的事情就是保存当前的值,理由见插入排序
int index=low;
for(int i=low*2+1;i<high;i=2*i+1)
{
if(i+1<high&&arr[i]<arr[i+1]) //先看左孩子与右孩子那个的值大
i++;
if(temp>=arr[i]) //再将大的值与父亲的值去进行比较
break;
swap(arr[index],arr[i]); //父节点的值如果小于则退出
index=i;
}
return true;
}
bool Quick(int arr[],int low,int high)
{
if(low>high)return false; //当它的左边的序列大于右边的时就退出----因为low-high之间所围成的区域就是我们要求的小于key值的区域
int i=low; //小于的时候说明黎明没有一个元素自然就没有求的必要了,因此直接返回即可
int j=high; //j是用来作为分界点用的--->j以后的都是比key值大的,j以前的都是比key小的
int key=arr[low]; //取最低位为key值
while(i<j)
{
while(i<j&&arr[j]>key)
j--;
while(i<j&&arr[i]<key)
i++;
swap(arr[i],arr[j]);
}
Quick(arr,low,j-1);
Quick(arr,j+1,high);
//寥寥几笔就完成了快速排序------>看起来确实简单,但是中间的过程一定要思考清楚
}
bool BubbleSort()
{
int arr[10];
for(int i=0;i<10;++i)
arr[i]=rand()%100+1;
Sort(arr,0,10);
for(int i=0;i<10;++i)
cout<<arr[i]<<" ";
cout<<endl;
return true;
}
int main()
{
srand(time(0));
BubbleSort();
}