常用排序算法Java版

最近跟着韩顺平老师的Java版数据结构,也把排序这一大块讲完了,排序也算是学习算法的基础,很多人对算法的兴趣也是从了解排序算法开始的。以下是我听完课后对几个常用排序算法的简单介绍,思路及代码实现。

注意:本文中排序的最终结果是升序,从小到大。

目录

一、冒泡排序

二、选择排序

三、插入排序

四、希尔排序

五、快速排序

六、归并排序


一、冒泡排序

介绍:非常的经典,必会排序算法之一。

思路:如其名,就像水底的气泡一个一个向上冒,从下标较小的元素开始,逐步比较它相邻元素的值,如果发现前面比后面大,就交换位置,然后继续比较,最终大的数到后边。然后进行新一轮的比较。

代码实现:

public void bubbleSort(int[] array){
    //倒数第二趟走完的时候,已经排序完毕了,最后一轮不用进行。所以是 array.length-1
    for (int i= 0;i< array.length-1;i++){ 
        //每一趟,将目前最大的放到最后。
        int temp = 0; //用来交换时
        boolean flag = false; //退出标识
        //每一轮都筛出来过一个最大的了并且放在了最后面,所以就应该-i。
        for (int j=0; j< array.length-1-i;j++){  
            //如果前面的数比后面大,就交换
            if (array[j] > array[j+1]) {
                flag = true;
                temp = array[j];
                array[j] = array[j+1];
                array[j+1] = temp;
            }
        }
        //如果一轮里发现一次也没有交换,说明已经有序了,不用再循环了。
        if (!flag){
            break;
        }
    }
}

二、选择排序

介绍:与冒泡不同,冒泡一轮中可能交换很多次,选择一轮中只交换一次。交换次数较少。

思路:规定一个数是最小数,然后进入循环中,查看有没有数字比规定的最小数还小,如果有就重新确定最小值并且记录这个数字的位置,等该轮循环结束后交换。总共需要比较 arr.length-1轮。

代码实现:

public void selectSort(int[] arr){
    for (int i = 0;i<arr.length-1;i++){
        //每一轮都假定,最小值和最小值下标的位置。因为i前面的都已经是最小。所以就从i开始。
        int minIndex = i; //最小值的索引位置
        int min = arr[i]; //最小值
        //从i + 1开始找最小值的位置
        for (int j =i + 1;j<arr.length;j++){
            //先找位置
            if (min>arr[j]){
                minIndex = j;
                min = arr[j]; //重置最小值
            }
        }
        //循环结束就交换
        arr[minIndex] = arr[i];
        arr[i] = min;
    }
}

三、插入排序

介绍:就是把数组里的数,找位置插入,形成有序数组

思路:目标排序数组分成一个有序表(只有一个元素)和无序表(剩余元素)。从无序表中每一轮都拿出一个数和有序表中的元素比较,确定位置,插入成为新的有序表。

代码实现:

public void insertSort(int[] arr) {
    //从1开始,因为arr[0]是有序的,要从1及后面的数组中把数据依次加入到有序数组中来。
    for (int i = 1; i < arr.length; i++) {
        //定义待插入的数
        int insertVal = arr[i];
        //定义待插入的位置,即arr[i]的前面这个数的下标
        int insertIndex = i - 1; 
        //给insertVal找到插入的位置
        //insertIndex >= 0 保证insertVal找插入位置,不越界
        //insertVal < arr[insertIndex] 说明待插入的数找到位置了,但是前面数还没动
        while (insertIndex >= 0 && insertVal < arr[insertIndex]){
            //这里是在给待插入的数腾位置
            arr[insertIndex + 1] = arr[insertIndex];
            insertIndex --;
        }
        //当退出while循环时,说明插入位置找到,位置是insertIndex + 1
        //这里是把待插入的数放到腾出的位置上。
        arr[insertIndex + 1] = insertVal;
    }
}

四、希尔排序

介绍:插入排序有弊端在,当最小的数放在了目标排序数组的最后,插入排序需要挪很多个位置来放最小的数。希尔排序解决了这种问题。

思想:按照一定的增量将数组平均分成几个组,然后针对这几个组进行插入排序,通过不断减小增量,分的组越来越少,最终增量为1,分成了一个组。此时数组就有序了。

代码实现;

public void ShellSort(int[]arr){
    //gap 增量
    for (int gap = arr.length / 2; gap > 0; gap/=2){
        //从第gap个元素开始,逐个对其所在的组进行直接插入
        for (int i = gap; i < arr.length; i++) {
            int insertIndex = i - gap; //要插入的位置
            int insertVal = arr[i]; //要插入的值
            while(insertIndex >=0 && insertVal < arr[insertIndex]) {
                 arr[insertIndex + gap] = arr[insertIndex];
                 insertIndex -= gap;
            }
            arr[insertIndex + gap] = insertVal;
        }
    }
}

五、快速排序

介绍:对冒泡排序的一种改进,非常常用的一种排序算法,当数据大量重复的时候不太友好。

思路:找一个基准值,然后按照左边都小于基准值,右边都大于基准值的规则来排序。当一轮排序完成后,会根据条件从左右半边都进行递归。最终使得整个数组变有序。

代码实现:

//固定基准数法 基准数在中间,交换类型  
public static void quickSortTest(int[]arr,int start,int end){
    //定义一个基数
    int pivot = arr[(end-start)/2+start];
    int left = start;
    int right = end;
    int temp = 0;
    //条件不满足后就说明一轮排序结束了,要左右进行递归了
    while(left < right){
        //从右向左找,直到找到比pivot小的数
        while(arr[right] > pivot){
            right--;
        }
        //从左向右找,直到找到比pivot大的数
        while(arr[left] < pivot){
            left++;
        }
        //指针重合了,直接跳出。
        if (left == right){
            break;
        }
        //找到就换
        temp = arr[left];
        arr[left] = arr[right];
        arr[right] = temp;
        //换完考虑left 和 right 的数是否和pivot相等,防止死循环
        if (arr[left] == pivot){
            right--;
        }
        if (arr[right] == pivot){
            left++;
        }
    }
    //这个条件必须有,上面的循环还有可能出现left>right,是left=0和right=1 的情况,而arr[left]=pivot的情况,当这个情况出现时,right--,arr[right]=pivot,left又++,导致left>right;此时不满足这个条件。
    if (left == right){
        left += 1;
        right -= 1;
    }
    //一轮排序完,直接进行下一轮
    if (start < right){
        quickSortTest(arr,start,right);
    }
    if (end > left){
        quickSortTest(arr,left,end);
    }
}

六、归并排序

介绍:采用分治的思想,先把数组分分分,然后根据一定的顺序合起来。归并排序时间复杂度是线性的,是稳定排序算法。

思路:先把目标排序数组一轮一轮拆分,最终拆成剩下一个元素,然后开始合并,合并过程需要一个中间下标和中间数组接收,然后比较两个序列下标对应值的大小来确定加入中间数组的顺序。直到把两个序列里的数据全部读取完,然后将中间数组拷贝到目标排序数组。自此排序结束。

代码实现:

//分+合方法
public static void mergeSort(int[]arr,int left,int right,int[] temp){
    if (left < right) {
        int mid = (right - left)/2 + left; //中间的索引
        //向左递归进行分解
        mergeSort(arr,left,mid,temp);
        //右分解
        mergeSort(arr,mid+1,right,temp);
        //合并
        merge(arr,left,mid,right,temp);
    }
}
//合并的方法
/**
 *
 * @param arr 排序的原始数组
 * @param left 左边有序序列的初始索引
 * @param mid 中间索引
 * @param right 右边索引
 * @param temp 做中转的数组
 */
public static void merge(int[]arr,int left,int mid,int right, int[]temp) {
    int i = left; //表示左边有序序列的初始索引
    int j = mid + 1; //表示右边有序序列的初始索引
    int t = 0; //指向temp数组的当前索引,中转数组的下标
    //先把左右两边数据(已经有序)按规则填充到temp数组中,直到两边有序序列有一边处理完毕为止
    while(i <= mid && j <= right) { //终止条件
        if(arr[i] < arr[j]){
            temp[t++] = arr[i++];
        } else {
            temp[t++] = arr[j++];
        }
    }
    //把有剩余数据的一边的数据依次全部填充到temp去
    while(i <= mid){ //说明左边有序序列还有剩余的元素,全部填充到temp
        temp[t++] = arr[i++];
    }
    while(j <= right){ //说明右边有序序列还有剩余的元素,全部填充到temp
        temp[t++] = arr[j++];
    }
    //将temp数组的元素拷贝到arr
    //并不是每次都拷贝所有的元素,前面递归的不是所有
    t = 0; //temp数组索引挪到开始位置,准备赋值了
    while(left <= right){ //第一次合并,templeft = 0 而right = 1  最后一次templeft = 0 而left = 7
        arr[left++] = temp[t++];
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值