七种排序算法(Java实现)

今天来总结一下七种排序算法。七种分别是,插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序

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);

稳定算法。

好了,七种排序算法已经全部实现完毕,如果代码中或者我的见解中有什么问题,请留言告知,感谢。

不多说了,敲代码去了。

猜你喜欢

转载自blog.csdn.net/volatile_0524/article/details/83312703