排序算法合集

快速排序

我们可以把快速排序看着三个步骤:
1.选择基准值:在待排序列中,按照某种方式挑出一个元素,作为基准值。
2.分割操作:以该基准值在序列中的实际位置,把序列分成两个子序列,一边是比它大的值,另外一边是比它小的值。
3.递归:对两个子序列进行快排,直到序列为空或者只有一个元素。

过程演示

这是我画的一张图,结合这张图再看下面的代码可能会比较好理解一点,当然在看代码的时候最后可以自己画一张草图,可以熟悉一下整个过程,加深理解!

img

代码实现

public class QuickSort {
public static void main(String[] args) {
    int[] arr = new int[] {9,4,6,8,3,10,4,6};
    quickSort(arr,0,arr.length - 1);
    System.out.println(Arrays.toString(arr));
    
}
public static void quickSort(int[] arr,int low,int high) {
    int p,i,j,temp;
    
    if(low >= high) {
        return;
    }
    //p就是基准数,这里就是每个数组的第一个
    p = arr[low];
    i = low;
    j = high;
    while(i < j) {
        //右边当发现小于p的值时停止循环
        while(arr[j] >= p && i < j) {
            j--;
        }
                        
        //这里一定是右边开始,上下这两个循环不能调换(下面有解析,可以先想想)    

        //左边当发现大于p的值时停止循环
        while(arr[i] <= p && i < j) {
            i++;
        }
        
            temp = arr[j];
            arr[j] = arr[i];
            arr[i] = temp;
    }
    //i=j,指针相遇,把较小的arr[i]赋值给arr[0],较大的p赋值给arr[i];
    arr[low] = arr[i];//这里的arr[i]一定是小于p的,经过i、j交换后i处的值一定是小于p的(j先走)
    //把最后一个交换的array[i]赋值给arr[low],即arry[0];
    arr[i] = p; //把较大的p赋值给arr[i]
    quickSort(arr,low,j-1);  //对左边快排
    quickSort(arr,j+1,high); //对右边快排
    
    }
}

一些问题

1.什么是基准值:
其实就是在数组里面找一个数,一般选择数组的第一个数作为基准值,当然这不一定是最佳的基准值,但这不妨碍我们做快速排序。本篇只讲标配版的快排,所以就选第一位作为基准值,以后有机会再更新高配版的~


2.快排中为什么一定是右边先开始循环?
从右边先开始的前提是我们选择序列中最左边的元素最为基准值。
先从右边开始可以保证i,j相等的时候,arr[i] = arr[j] 小于基准值p。这样交换之后才能保证基准值左右两边分别小于和大于它的值。
我们图片演示一下:

img

可以发现如果左边先走的话将导致分组不成功,即左边的元素并不是都小于基准值。

冒泡排序

相邻的两个元素进行比较,交换

public static void main(String[] args) {
    int[] score ={7,5,4,2,3,8}for (int i = 0; i < score.length - 1; i++) {
        // 比较相邻两个元素,较大的数往后冒泡
        for (int j = 0; j < score.length - 1 - i; j++) {
            if (score[j] > score[j + 1]) {
                double temp = score[j + 1]; // 把第一个元素值保存到临时变量中
                score[j + 1] = score[j]; // 把第二个元素值转移到第一个元素变量中
                score[j] = temp; // 把临时变量(第一个元素的原值)保存到第二个元素中
            }
        }
    }
}

优化版

public static void main(String[] args) {
        int[] arr = { 1, 3, 4, 2, 6, 7, 8, 0, 5 };
        int i = 0;
        int tmp = 0;
        Boolean flag = true;
        int pos = 0;// 用来记录最后一次交换的位置
        int k = arr.length - 1;
        for (i = 0; i < arr.length - 1; i++) {
            pos = 0;
            int j = 0;
            flag = true;
            for (j = 0; j < k; j++) {
                if (arr[j] > arr[j + 1]) {
                    // 交换
                    tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                    flag = false;// 加入标记
                    pos = j;// 交换元素,记录最后一次交换的位置
                }
            }
            if (flag == true)// 如果没有交换过元素,则已经有序
            {
                continue;
            }
            k = pos;// 下一次比较到记录位置即可
        }
        long endTime = System.nanoTime(); // 获取结束时间
    }

选择排序

从第一个位置的元素开始,与他后边的每一个元素进行比较,交换,把当前认为最小的元素放在该位置(第一个位置)

int[] nums = {84,83,88,87,61};
for(int i = 0;i< nums.length-1;i++){
for(int j=i;j< nums.length-1;j++){
if(nums[i]  > nums[j+1]){
           int temp ;
           temp = nums[i];
           nums[i] = nums[j+1];
           nums[j+1] = temp;
		}
	}
}

堆排序

堆排序(Heapsort) 是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

实现:
根据传进来的数组创建一个最小堆,最小堆的第一个元素一定是最小的,所以我们每次删除根节点(数组第一个元素),把根节点放入我们新建的数组中,最后得到的新数组就是一个排好序的。
注意:每次删除操作不仅仅是删除根节点,删除之后要调整数组,使数组重新满足最小堆的要求
java提供了一个数据类型PriorityQueue-优先级队列,使用动态数组实现了最小堆,我们可以直接用
//数据结构-最小堆
static int[] Heap_Sort(int[] array){
		int len = array.length;
		
		//创建最小堆,PriorityQueue内部维护的数组默认长度是11
		//为防止给的数组长度大于11,进堆时要扩展内部数组的长度
		//所以初始化时,确定内部数组的长度等于我们传入的数组的长度
		Queue<Integer> minHeap = new PriorityQueue<>(len);
		for(int e : array){
			minHeap.add(e);
		}
		
		//把堆顶(最小元素)投出,存在新数组中
		for(int i = 0; i < len; i++){
			array[i] = minHeap.poll();
		}
		return array;
	}

归并排序

img

package algorithms;

public class myMergeSort {
    static int number=0;
    public static void main(String[] args) {
        int[] a = {26, 5, 98, 108, 28, 99, 100, 56, 34, 1 };
        printArray("排序前:",a);
        MergeSort(a);
        printArray("排序后:",a);
    }

    private static void printArray(String pre,int[] a) {
        System.out.print(pre+"\n");
        for(int i=0;i<a.length;i++)
            System.out.print(a[i]+"\t");    
        System.out.println();
    }

    private static void MergeSort(int[] a) {
        // TODO Auto-generated method stub
        System.out.println("开始排序");
        Sort(a, 0, a.length - 1);
    }

    private static void Sort(int[] a, int left, int right) {
        if(left>=right)
            return;
    
        int mid = (left + right) / 2;
        //二路归并排序里面有两个Sort,多路归并排序里面写多个Sort就可以了
        Sort(a, left, mid);
        Sort(a, mid + 1, right);
        merge(a, left, mid, right);

    }


    private static void merge(int[] a, int left, int mid, int right) {
    
        int[] tmp = new int[a.length];
        int r1 = mid + 1;
        int tIndex = left;
        int cIndex=left;
        // 逐个归并
        while(left <=mid && r1 <= right) {
            if (a[left] <= a[r1]) 
                tmp[tIndex++] = a[left++];
            else
                tmp[tIndex++] = a[r1++];
        }
            // 将左边剩余的归并
            while (left <=mid) {
                tmp[tIndex++] = a[left++];
            }
            // 将右边剩余的归并
            while ( r1 <= right ) {
                tmp[tIndex++] = a[r1++];
            }
            
            System.out.println("第"+(++number)+"趟排序:\t");
            // TODO Auto-generated method stub
            //从临时数组拷贝到原数组
             while(cIndex<=right){
                    a[cIndex]=tmp[cIndex];
                    //输出中间归并排序结果
                    System.out.print(a[cIndex]+"\t");
                    cIndex++;
                }
             System.out.println();
        }
    }

插入排序

public static void main(String[] args){
    int[] nums = buildArray();
    System.out.print("亲,您输入的初始数组是 : ");
    printArray(nums);
    //默认构造从左→右依次递增的序列
    for(int i=1; i<nums.length; i++){
        int j;
        int temp = nums[i];                      //temp是本趟待插入的数,之前从0~i-1的数全是从左→右有序递增。
        for(j=i-1; j>=0&&nums[j]>temp; j--){
            nums[j+1] = nums[j];
        }
        nums[j+1] = temp;
        System.out.print("第"+i+"次直接插入排序后的数组:");          
        printArray(nums);                                
    }
}

希尔排序

希尔排序是在插入排序的基础上优化而来的,原理就是将一个数组按一定间隔分割成若干个小区间,针对每个区间使用插入排序。当各个区间都有序时再针对整体进行插入排序。这样整个数组就是有序的,而且效率很高
  • 以 32 43 24 18 49 28 10 59 18 41 序列为例
  • 第一次 h= 10 / 2 = 5;则从第6个元素开始,依次与前面相距间隔为5的元素比较

32 43 24 18 49 *28* 10 59 18 41 —> *28* 43 24 18 49 32 10 59 18 41
28 43 24 18 49 32 *10* 59 18 41 —> 28 *10* 24 18 49 32 *43* 59 18 41
28 10 24 18 49 32 43 59 18 41 —> 28 10 *24* 18 49 32 43 59 18 41
28 10 24 *18* 49 32 43 59 18 41 —> 28 10 24 18 49 32 43 59 *18* 41
28 10 24 18 *49* 32 43 59 18 *41* —> 28 10 24 18 *41* 32 43 59 18 *49*

  • 这样第一趟就得到 28 10 24 18 41 32 43 59 18 49 的序列,后面再缩小 h = h/ 2 = 2,同上述步骤一样依次与前面相距间隔为 2 的元素就行比较。

希尔排序很适合用于大数组排序,h的取值也是提高希尔排序效率的关键因子。

import java.util.Arrays;
//  希尔排序对于大型数组排序效率很高,不需要额外的内存空间
public class 希尔排序 {

    public static void shellSort(int[] a) {
        int n = a.length;
        // 设置增量,增量的取法有很多,这里是推荐取法
        int h = 1;
        while (h < n / 3) {
            h = 3 * h + 1;
        }
        // 增量最小为 1 ,也就是相邻的两个元素比较
        while (h >= 1) {

            // 对相距 h 的两个元素插入排序
            for (int i = h; i < n; i++) {
                for (int j = i; j >= h && less(a[j], a[j-h]); j -= h) {
                    exch(a, j, j-h);
                }
            }
            // 排序结束后,缩小增量,继续排序
            h /= 3;
        }
    }
    private static void exch(int[] a, int i, int j) {
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }

    private static boolean less(Comparable v, Comparable w) {
        // 若 v 小于 w 则返回负数
        return v.compareTo(w) < 0;
    }

    public static void main(String[] args) {
        int[] s = {4, 6, 1, 2, 3, 0};
        System.out.println(Arrays.toString(s));
        shellSort(s);
        System.out.println(Arrays.toString(s));

    }
}
  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值