【算法基础】java 排序算法

Java中的经典算法之冒泡排序(Bubble Sort)

原理:比较两个相邻的元素,将值大的元素交换至右端。

思路:依次比较相邻的两个数,将小数放在前面,大数放在后面。即在第一趟:首先比较第1个和第2个数,将小数放前,大数放后。然后比较第2个数和第3个数,将小数放前,大数放后,如此继续,直至比较最后两个数,将小数放前,大数放后。重复第一趟步骤,直至全部排序完成。

举例说明:要排序数组:int[] arr={6,3,8,2,9,1};

第一趟排序:

    第一次排序:6和3比较,6大于3,交换位置: 3 6 8 2 9 1

    第二次排序:6和8比较,6小于8,不交换位置:3 6 8 2 9 1

    第三次排序:8和2比较,8大于2,交换位置: 3 6 2 8 9 1

    第四次排序:8和9比较,8小于9,不交换位置:3 6 2 8 9 1

    第五次排序:9和1比较:9大于1,交换位置: 3 6 2 8 1 9

    第一趟总共进行了5次比较, 排序结果: 3 6 2 8 1 9

---------------------------------------------------------------------

第二趟排序:

    第一次排序:3和6比较,3小于6,不交换位置:3 6 2 8 1 9

    第二次排序:6和2比较,6大于2,交换位置: 3 2 6 8 1 9

    第三次排序:6和8比较,6大于8,不交换位置:3 2 6 8 1 9

    第四次排序:8和1比较,8大于1,交换位置: 3 2 6 1 8 9

    第二趟总共进行了4次比较, 排序结果: 3 2 6 1 8 9

---------------------------------------------------------------------

第三趟排序:

    第一次排序:3和2比较,3大于2,交换位置: 2 3 6 1 8 9

    第二次排序:3和6比较,3小于6,不交换位置:2 3 6 1 8 9

    第三次排序:6和1比较,6大于1,交换位置: 2 3 1 6 8 9

    第二趟总共进行了3次比较, 排序结果: 2 3 1 6 8 9

---------------------------------------------------------------------

第四趟排序:

    第一次排序:2和3比较,2小于3,不交换位置:2 3 1 6 8 9

    第二次排序:3和1比较,3大于1,交换位置: 2 1 3 6 8 9

    第二趟总共进行了2次比较, 排序结果: 2 1 3 6 8 9

---------------------------------------------------------------------

第五趟排序:

    第一次排序:2和1比较,2大于1,交换位置: 1 2 3 6 8 9

    第二趟总共进行了1次比较, 排序结果: 1 2 3 6 8 9

---------------------------------------------------------------------

最终结果:1 2 3 6 8 9

---------------------------------------------------------------------

由此可见:N个数字要排序完成,总共进行N-1趟排序,每i趟的排序次数为(N-i)次,所以可以用双重循环语句,外层控制循环多少趟,内层控制每一趟的循环次数,即

for(int i=1;i<arr.length;i++){

    for(int j=1;j<arr.length-i;j++){

    //交换位置

}    

冒泡排序的优点:每进行一趟排序,就会少比较一次,因为每进行一趟排序都会找出一个较大值。如上例:第一趟比较之后,排在最后的一个数一定是最大的一个数,第二趟排序的时候,只需要比较除了最后一个数以外的其他的数,同样也能找出一个最大的数排在参与第二趟比较的数后面,第三趟比较的时候,只需要比较除了最后两个数以外的其他的数,以此类推……也就是说,没进行一趟比较,每一趟少比较一次,一定程度上减少了算法的量。

用时间复杂度来说:

  1.如果我们的数据正序,只需要走一趟即可完成排序。所需的比较次数和记录移动次数均达到最小值,即:Cmin=n-1;Mmin=0;所以,冒泡排序最好的时间复杂度为O(n)。

  2.如果很不幸我们的数据是反序的,则需要进行n-1趟排序。每趟排序要进行n-i次比较(1≤i≤n-1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:

冒泡排序的最坏时间复杂度为:O(n2) 。

综上所述:冒泡排序总的平均时间复杂度为:O(n2) 。

代码实现:

/*
 * 冒泡排序
 */
public class BubbleSort {
  public static void main(String[] args) {
    int[] arr={6,3,8,2,9,1};
    System.out.println("排序前数组为:");
    for(int num:arr){
      System.out.print(num+" ");
    }
    for(int i=0;i<arr.length-1;i++){//外层循环控制排序趟数
      for(int j=0;j<arr.length-1-i;j++){//内层循环控制每一趟排序多少次
        if(arr[j]>arr[j+1]){
          int temp=arr[j];
          arr[j]=arr[j+1];
          arr[j+1]=temp;
        }
      }
    } 
    System.out.println();
    System.out.println("排序后的数组为:");
     for(int num:arr){
       System.out.print(num+" ");
     } 
  }
 }

java实现插入排序

一、基本思想

通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应的位置并插入。

插入排序非常类似于整扑克牌。

在开始摸牌时,左手是空的,牌面朝下放在桌上。接着,一次从桌上摸起一张牌,并将它插入到左手一把牌中的正确位置上。为了找到这张牌的正确位置,要将它与手中已有的牌从右到左地进行比较。无论什么时候,左手中的牌都是排好序的。

如果输入数组已经是排好序的话,插入排序出现最佳情况,其运行时间是输入规模的一个线性函数。如果输入数组是逆序排列的,将出现最坏情况。平均情况与最坏情况一样,其时间代价是Θ(n2)。

也许你没有意识到,但其实你的思考过程是这样的:现在抓到一张7,把它和手里的牌从右到左依次比较,7比10小,应该再往左插,7比5大,好,就插这里。为什么比较了10和5就可以确定7的位置?为什么不用再比较左边的4和2呢?因为这里有一个重要的前提:手里的牌已经是排好序的。现在我插了7之后,手里的牌仍然是排好序的,下次再抓到的牌还可以用这个方法插入。编程对一个数组进行插入排序也是同样道理,但和插入扑克牌有一点不同,不可能在两个相邻的存储单元之间再插入一个单元,因此要将插入点之后的数据依次往后移动一个单元。

二、算法描述

假定n是数组的长度,

首先假设第一个元素被放置在正确的位置上,这样仅需从1-n-1范围内对剩余元素进行排序。对于每次遍历,从0-i-1范围内的元素已经被排好序,

每次遍历的任务是:通过扫描前面已排序的子列表,将位置i处的元素定位到从0到i的子列表之内的正确的位置上。

将arr[i]复制为一个名为target的临时元素。

向下扫描列表,比较这个目标值target与arr[i-1]、arr[i-2]的大小,依次类推。

这个比较过程在小于或等于目标值的第一个元素(arr[j])处停止,或者在列表开始处停止(j=0)。

在arr[i]小于前面任何已排序元素时,后一个条件(j=0)为真,

因此,这个元素会占用新排序子列表的第一个位置。

在扫描期间,大于目标值target的每个元素都会向右滑动一个位置(arr[j]=arr[j-1])。

一旦确定了正确位置j,

目标值target(即原始的arr[i])就会被复制到这个位置。

与选择排序不同的是,插入排序将数据向右滑动,并且不会执行交换。

三、示例代码

public static void InsertSort(int[] arr)
{
    int i, j;
    int n = arr.Length;
    int target;
 
    //假定第一个元素被放到了正确的位置上
    //这样,仅需遍历1 - n-1
    for (i = 1; i < n; i++)
    {
        j = i;
        target = arr[i];
 
        while (j > 0 && target < arr[j - 1])
        {
            arr[j] = arr[j - 1];
            j--;
        }
 
        arr[j] = target;
    }
}

Java中的经典算法之选择排序(SelectionSort)

a) 原理:每一趟从待排序的记录中选出最小的元素,顺序放在已排好序的序列最后,直到全部记录排序完毕。也就是:每一趟在n-i+1(i=1,2,…n-1)个记录中选取关键字最小的记录作为有序序列中第i个记录。基于此思想的算法主要有简单选择排序、树型选择排序和堆排序。(这里只介绍常用的简单选择排序)

b) 简单选择排序的基本思想:给定数组:int[] arr={里面n个数据};第1趟排序,在待排序数据arr[1]~arr[n]中选出最小的数据,将它与arrr[1]交换;第2趟,在待排序数据arr[2]~arr[n]中选出最小的数据,将它与r[2]交换;以此类推,第i趟在待排序数据arr[i]~arr[n]中选出最小的数据,将它与r[i]交换,直到全部排序完成。

c) 举例:数组 int[] arr={5,2,8,4,9,1};

-------------------------------------------------------

第一趟排序: 原始数据:5 2 8 4 9 1

最小数据1,把1放在首位,也就是1和5互换位置,

排序结果:1 2 8 4 9 5

-------------------------------------------------------

第二趟排序:

第1以外的数据{2 8 4 9 5}进行比较,2最小,

排序结果:1 2 8 4 9 5

-------------------------------------------------------

第三趟排序:

除1、2以外的数据{8 4 9 5}进行比较,4最小,8和4交换

排序结果:1 2 4 8 9 5

-------------------------------------------------------

第四趟排序:

除第1、2、4以外的其他数据{8 9 5}进行比较,5最小,8和5交换

排序结果:1 2 4 5 9 8

-------------------------------------------------------

第五趟排序:

除第1、2、4、5以外的其他数据{9 8}进行比较,8最小,8和9交换

排序结果:1 2 4 5 8 9

-------------------------------------------------------

注:每一趟排序获得最小数的方法:for循环进行比较,定义一个第三个变量temp,首先前两个数比较,把较小的数放在temp中,然后用temp再去跟剩下的数据比较,如果出现比temp小的数据,就用它代替temp中原有的数据。具体参照后面的代码示例,相信你在学排序之前已经学过for循环语句了,这样的话,这里理解起来就特别容易了。

代码示例:

//选择排序
public class SelectionSort {
    public static void main(String[] args) {
        int[] arr={1,3,2,45,65,33,12};
        System.out.println("交换之前:");
        for(int num:arr){
            System.out.print(num+" ");
        }        
        //选择排序的优化
        for(int i = 0; i < arr.length - 1; i++) {// 做第i趟排序
            int k = i;
            for(int j = k + 1; j < arr.length; j++){// 选最小的记录
                if(arr[j] < arr[k]){ 
                    k = j; //记下目前找到的最小值所在的位置
                }
            }
            //在内层循环结束,也就是找到本轮循环的最小的数以后,再进行交换
            if(i != k){  //交换a[i]和a[k]
                int temp = arr[i];
                arr[i] = arr[k];
                arr[k] = temp;
            }    
        }
        System.out.println();
        System.out.println("交换后:");
        for(int num:arr){
            System.out.print(num+" ");
        }
    }

}

运行结果截图:

选择排序的时间复杂度:简单选择排序的比较次数与序列的初始排序无关。 假设待排序的序列有 N 个元素,则比较次数永远都是N (N - 1) / 2。而移动次数与序列的初始排序有关。当序列正序时,移动次数最少,为 0。当序列反序时,移动次数最多,为3N (N - 1) / 2。

所以,综上,简单排序的时间复杂度为 O(N2)。

java实现的快速排序算法

快速排序的原理:选择一个关键值作为基准值。比基准值小的都在左边序列(一般是无序的),比基准值大的都在右边(一般是无序的)。一般选择序列的第一个元素。

一次循环:从后往前比较,用基准值和最后一个值比较,如果比基准值小的交换位置,如果没有继续比较下一个,直到找到第一个比基准值小的值才交换。找到这个值之后,又从前往后开始比较,如果有比基准值大的,交换位置,如果没有继续比较下一个,直到找到第一个比基准值大的值才交换。直到从前往后的比较索引>从后往前比较的索引,结束第一次循环,此时,对于基准值来说,左右两边就是有序的了。

接着分别比较左右两边的序列,重复上述的循环。

public class FastSort{

     public static void main(String []args){
        System.out.println("Hello World");
        int[] a = {12,20,5,16,15,1,30,45,23,9};
        int start = 0;
        int end = a.length-1;
        sort(a,start,end);
        for(int i = 0; i<a.length; i++){
             System.out.println(a[i]);
         }
        
     }
     
     public void sort(int[] a,int low,int high){
         int start = low;
         int end = high;
         int key = a[low];
         
         
         while(end>start){
             //从后往前比较
             while(end>start&&a[end]>=key)  //如果没有比关键值小的,比较下一个,直到有比关键值小的交换位置,然后又从前往后比较
                 end--;
             if(a[end]<=key){
                 int temp = a[end];
                 a[end] = a[start];
                 a[start] = temp;
             }
             //从前往后比较
             while(end>start&&a[start]<=key)//如果没有比关键值大的,比较下一个,直到有比关键值大的交换位置
                start++;
             if(a[start]>=key){
                 int temp = a[start];
                 a[start] = a[end];
                 a[end] = temp;
             }
         //此时第一次循环比较结束,关键值的位置已经确定了。左边的值都比关键值小,右边的值都比关键值大,但是两边的顺序还有可能是不一样的,进行下面的递归调用
         }
         //递归
         if(start>low) sort(a,low,start-1);//左边序列。第一个索引位置到关键值索引-1
         if(end<high) sort(a,end+1,high);//右边序列。从关键值索引+1到最后一个
     }
     
}

上面最后一句不是基准值的意思是,不是直接用基准值交换,是用基准值所在的索引交换。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券