今天来总结一下七种排序算法。七种分别是,插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序
1.插入排序:
插入排序呢,就是把某个元素插入到合适该元素的位置。
打个比方,现在三个同学依照个子高低来排队,现在个子最高的学生和最矮的学生先到操场就先站好了,等到第三个不算高也不算矮的同学来了,老师就将他插入到两个以及排好的同学中间,这个过程就像是插入排序。
下面用以下数据按照从小到大的顺序排列,看看插入排序的过程:
首先,在看图的时候做一个规定:
用待排数列中的第一个数与有序数列中的数进行比较:
接着还是继续做同样的事情,将待排数与有序数列中的数进行比较,寻找合适的位置
以上就是插入排序的主要思想,下面看看由Java实现的排序代码:
public void InsertSort(int[]nums){
if(nums.length<=1)//保证数组中有一个以上的元素才可以排序,
{
return;
}
for(int i=1;i<=nums.length;i++)//从第一个元素往后都是待排序列
{
int temp=nums[i];//记录一下需要插入的元素
if(nums[i]<nums[i-1])//如果待插入的元素比有序数列中的最后一个元素小
{
int j=i-1;//记录一下位置
for (;j>=0&&temp<nums[j];j--)//当位置大于等于0,并且带插入元素比该位置上的元素小
{
nums[j+1]=nums[j];
}
nums[j+1]=temp;//就将该元素插入合适的位置
}
}
}
时间复杂度:O(N^2);
空间复杂度:O(1);
插入排序是稳定排序。
2.希尔排序
希尔排序就是设置设置一个间隔,然后将间隔的数据进行插入排序,而上面的插入排序就相当于间隔为1的希尔排序:
下面看看将以下的数据按从小到大的顺序排序的过程:
上述的数组长度为8,那么就先定间隔为4。
上面颜色相同的就是间隔为4的数,然后再将它们进行插入排序:
可以看到,将他们依次进行排序过后的结果如上,接下来再将间隔减半为2:
当间隔减半之后,上图中颜色相同的就是间隔为2的数据,再将其进行排序:
再将间隔减半为1:
上述过程就为希尔排序的大致过程,下面看看Java代码实现希尔排序:
public void ShellSort(int [] nums){
int h = nums.length/2;//定义步长
while (h>0)//当步长为0时,不做处理
{
for (int i=h;i<=nums.length;i++)//首先从h开始
{
int temp=nums[i];//记录该位置上的值
if (nums[i]<nums[i-h])//如果h步前面的数小于当前位置上的数,就进行寻找位置
{
int j = i-h;
for (;j>=0&&temp<nums[j];j=j-h)
{
nums[j+h]=nums[j];
}
nums[j+h]=temp;
}
}
h/=2;//步长减半
}
}
希尔排序相对于插入排序算是优化的排序算法。
时间复杂度:O(N^2);
空间复杂度:O(1);
不稳定排序算法。
3.选择排序
选择排序就比较简单了,就相当于小猴子的故事,就是小猴子现在手上有一粒芝麻,当他看到葡萄的时候,就将芝麻放在了葡萄的位置上,将葡萄放在了手上,当他看到苹果的时候就将葡萄放在了苹果的位置上,将苹果放在手上,当他看到了西瓜,就将苹果放下,将西瓜放在了手上。
下面还是通过例子看图:
首先,用九进行比较,寻找比九小的数:
当第一个数与数组中的其他数比较完了之后,再比较第二个数:
将21与后面其他数进行比较:
当比较到-30的时候,将-30与21进行交换,然后再用-30接着后面的位置比较,发现比-30小的数再将其进行交换。
重复上述动作得到的结果:
选择排序的思想比较简单,下面来看看Java实现的选择排序
public void SelectSort(int [] nums)
{
if(nums.length<=1){//保证数组中的元素个数大于1
return;
}
for (int i =0;i<nums.length-1;i++)//从第一个数开始比较
{
for (int j=i+1;j<=nums.length-1;j++)//与比较的数的后一个数开始比较
{
if(nums[j]<nums[i])//如果要比较的数大于它后面某个数,就交换他们
{
Swap(nums,j,i);
}
}
}
}
public void Swap(int[] nums,int p,int q){
int temp = nums[p];
nums[p]=nums[q];
nums[q]=temp;
}
时间复杂度:O(N^2);
空间复杂度:O(1);
不稳定排序。
4.堆排序
堆排序就是建立一个大顶(小顶)堆,然后再将堆顶元素与最后一个元素进行交换,交换过后再将其向下调整再变为一个大顶堆,再将其与倒数第二个元素进行交换………………
堆排序的思想还是比较简单的:
1.建堆;
2.将堆顶元素与最后一个元素进行交换。
3.向下调整为堆;
之后重复2,3的动作,直到除堆顶元素都有交换过。
将{9,21,23,30,-49,-30,21,-16}进行排序;
将其按照二叉树的形式按下列形状进行排序:
先找到第一个非叶子节点,将其左右子树中的最大的值与其进行比较,如果比它大就交换其位置,如果不是,就不变。
图中红色的框就表示现在正在建堆的二叉树
接着再找到第二个非叶子节点,将其左右子树中的最大值将其进行比较,如果比他大就交换位置,如果不是,就不变。
图中橙色的数据表示发生过交换的数据
由上面的过程就得到了一个大顶堆,这样就将二叉树中最大元素放到了堆顶的位置。
将数据交换之后,很明显发现此时已经不再是一个堆了,现在就向下调整,重新建成一个大顶堆,此时,最后一个元素不参与接下来的任何过程。
此时再将堆顶元素与当前作用范围内的最后一个叶子节点进行交换
然后再进行向下调整,在进行交换元素,接下来一系列过程为
:
现在堆排序的完成过程已经结束,下面看看Java代码:
public void HeapSort1(int [] nums){
for (int i =0;i<nums.length-1;i++){
BuildMaxHeap(nums,nums.length-1-i);
Swap(nums,0,nums.length-1-i);
}
}
public void BuildMaxHeap(int[] nums,int lastIndex){
for (int i=(lastIndex-1)/2;i>=0;i--){
int k=i;
while (k*2+1<=lastIndex){
int biggerIndex=k*2+1;
if (biggerIndex<lastIndex){
if (nums[biggerIndex]<nums[biggerIndex+1]){
biggerIndex++;
}
}
if (nums[biggerIndex]>nums[k]){
Swap(nums,biggerIndex,k);
k=biggerIndex;
}
}
}
}
public void Swap(int [] nums,int p,int q){
int temp = nums[p];
nums[p]=nums[q];
nums[q]=temp;
}
上面就是堆排序的代码,结合图的演化过程会更容易理解
时间复杂度:O(N*lgN);
空间复杂度:O(1);
不稳定排序。
5.冒泡排序
冒泡排序的思想也很简单,就是通过两两比较,将最大的值放到数组的末尾。
下面看看冒泡思想实现的过程:
首先先假设数组第一个元素为最大值,然后将其与后面的值两两比较;
将最大的元素放到末尾之后,再重头寻找,将次大的放到倒数第二的位置…………
结果为:
冒泡排序思想简单就不用太多的图来展示了,接下来是冒泡排序的代码:
public void Swap(int [] nums,int p,int q){
int temp = nums[p];
nums[p]=nums[q];
nums[q]=temp;
}
public void BubbleSort(int [] nums){
for (int i = 0;i<nums.length-1;i++){
boolean flag = false;//定义一个flag
for (int j=0;j<nums.length-1-i;j++){//从数组第一个元素开始循环
if (nums[j]>nums[j+1]){//两两进行比较
Swap(nums,j,j+1);//交换位置
flag=true;//将flag改为true
}
}
if (!flag){//如果flag为false,就说明数组就没有进行过交换,就说明数组已经有序了
break;
}
}
}
时间复杂度:O(N^2);
空间复杂度:O(1);
稳定算法。
6.快速排序
快速排序是一种比较快的排序方法,其实现思想也比较简单,先找一个基准值,然后将大于此基准值的放在基准值的右边,将大于基准值的数放在基准值的左边。此时,基准值就将数列分为了两个部分,再将左右子序列按照相同的方法进行递归,直到,子序列中只剩下一个数的时候为止。、
首先选首元素为基准值:
然后再定义两个指针:
p:从左往右寻找比基准值大的值,找到就停下,
q:从右往左找比基准值小的值,找到就停下。
如果p在q的左边,就交换两指针所指向的数:
然后再接着往下找:
直到q在p的右边,将q指向的值和基准值交换:
此时基准值就将数组分为了两个子序列,比基准值大的在基准值右边,比它小的在基准值左边;
再将左右子序列按照上述方法进行递归,直到子序列中只剩一个数的时候位置。
此时数列就变得又序了:
下面看看代码:
public void QuickSort(int[] nums){
subSort(nums,0,nums.length-1);
}
private void subSort(int [] nums,int start,int end){
int base = nums[start];//将数组的首元素定位基准值
int p=start;//指针,从左往右
int q=end+1;//指针,从右往左
while (true){
while (p<end&&nums[++p]<base);//当p找到比基准值大的时候记录该位置
while (q>start&&nums[--q]>base);//当q找到比基准值小的时候记录该位置
if (p<q){//当p在q的左边的时候
Swap(nums,p,q);//交换他们所指向的值
}else {
break;
}
}
Swap(nums,start,q);//交换基准值与q所指向的值
subSort(nums,start,q-1);//将基准值右边的序列进行递归
subSort(nums,q+1,end);//将基准值左边的序列进行递归
}
public void Swap(int [] nums,int p,int q){
int temp = nums[p];
nums[p]=nums[q];
nums[q]=temp;
}
时间复杂度:O(N*lgN)
空间复杂度:O(lgN)
不稳定排序。
7.归并排序
归并排序采用的是分治算法,就是先将一个序列分为好多个子序列,让后将其变成有序序列。
将相当于一个班级里面要将每个人的身高进行排序,班长可以将同学分为男女两个群体,再按宿舍分为几个群体,最后宿舍内部再将每个人的身高进行排序,然后再把男,女生宿舍的身高进行排序,最后再将班级将每个人的身高进行排序。
上图就可以完整清晰的体现归并算法的全过程:
1.先将数组分开;
2.再将其合并成有序的序列。
下面直接上代码:
public void MergeSort(int [] nums){
sort1(nums,0,nums.length-1);
}
public void sort(int[] nums,int left,int right){
if (left<right){//用来保证序列中有一个以上的元素
int centerIndex = left+(right-left)/2;//取得序列的中间位置
sort1(nums,left,centerIndex);//将序列中间位置的左边进行递归
sort1(nums,centerIndex+1,right);//递归右边
Merge1(nums,left,centerIndex,right);//将左右序列进行整合为一个有序的序列,
}
}
/**
*
* left:左序列的起始位置
* centerIndex:左序列的结束位置
* right:右序列的结束位置
*/
public void Merge(int[] nums,int left,int centerIndex,int right ){
int [] temp = new int[nums.length];//新开辟了一个与数组大小一样的空间
int mid = centerIndex+1;//这表示右序列的起始位置
int tempIndex = left;//记录temp的第一个空余位置的索引
int cacheIndex = left;
while (left<=centerIndex&&mid<=right){//当左序列比较时没超出范围并且右序列也是如此
if(nums[left]<nums[mid]){//如果左序列的值小于右序列的值
temp[tempIndex++]=nums[left++];//将左序列的值放入temp中,并且left,tempIndex往后移
}
else {
temp[tempIndex++]=nums[mid++];//否则,就将右序列的值放入temp中,mid与tempIndex都往后移
}
}
while (left<=centerIndex){
temp[tempIndex++]=nums[left++];//如果左序列中还有数据,将左序列中的剩余数据按照原顺序放入temp中
}
while (mid<=right){
temp[tempIndex++]=nums[mid++];//如果右序列中还有数据,将右序列中的剩余数据按照原顺序放入temp中
}
while (cacheIndex<=right){
nums[cacheIndex]=temp[cacheIndex++];//将temp中的数据按对应的索引放入nums中
}
}
归并排序的实现思路也比较简单,但是的搞清楚其中的递归过程,我上述代码中的注解很详细,再结合着代码一起读,就很容易弄懂。
时间复杂度:O(N*lgN)
空间复杂度:O(N);
稳定算法。
好了,七种排序算法已经全部实现完毕,如果代码中或者我的见解中有什么问题,请留言告知,感谢。
不多说了,敲代码去了。