排序算法归纳总结

排序算法归纳总结

一、排序算法分类

(C:\Users\ASUS\AppData\Roaming\Typora\typora-user-images\image-20220727163456106.png)]

cite: https://github.com/hustcc/JS-Sorting-Algorithm

二、排序原理及代码实现

1、冒泡排序

1.1 原理

冒泡排序(Bubble Sorting)的基本思想是:通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。

优化:
因为排序的过程中,各元素不断接近自己的位置,如果一趟比较下来没有进行过交换,就说明序列有序,因此要在排序过程中设置一个标志flag判断元素是否进行过交换。从而减少不必要的比较。(这里说的优化,可以在冒泡排序写好后,在进行)

1.2 代码实现
//冒泡排序,从小到大
public void bubbleSort(int[] arr) {
      //优化:标记第几轮有没有进行交换,没有交换则说明已经有序,提前结束
      boolean flag = false;
      for (int i=0;i<arr.length-1;i++){ 
          //第i+1轮排序后得到第i大的值,下一轮比较下标为0-arr.length-1-i的数,后面的已经有序。
          flag = false;
          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;
                  flag = true;
              }
          }
          if(!flag){//没有交换,提前结束
              break;
          }
      }
 }

2、选择排序

2.1 原理

img

2.2 代码实现
//选择排序,从小到大
//法一:
public void SelectSort(int[] arr) {
      for (int i=0;i<arr.length;i++){
          //确定arr[i]的值为第i+1小的
          for (int j=arr.length-1;j>i;j--){
              if (arr[j]<arr[i]){//如果i+1 - arr.length-1中的值比当前arr[i]小,交换
                  int temp = arr[j];
                  arr[j] = arr[i];
                  arr[i] = temp;
              }
          }
      }
 }

//法二:
public void SelectSort(int[] arr) {
      for (int i=0;i<arr.length;i++){
          //确定arr[i]的值为第i+1小的
          int min = arr[i];
          int minIndex = i;
          for (int j=arr.length-1;j>i;j--){
              if (arr[j] < min){//如果i+1 - arr.length-1中的值比当前arr[i]小,交换
                  min = arr[j];
                  minIndex = j;
              }
          }
          arr[minIndex] = arr[i];
          arr[i] = min;
      }
 }

3、插入排序

3.1 原理

img

3.2 代码实现
//插入排序,从小到大
//法一:
public void InsertSort(int[] arr) {
    //控制拿去每一个元素
     //注意这里从1开始
    for(int i=1;i<arr.length;i++){
        //比较次数
        for (int j=i;j>0;j--){
            //是否小于前面的元素,和前面大于当前元素的交换,直到前一个元素小于当前元素
            if (arr[j] < arr[j-1]){
                int temp = arr[j];
                arr[j] = arr[j-1];
                arr[j-1] = temp;
            }else {
                break;
            }
        }
    }
 }

//法二:插入时,可以二分查找插入,提高效率。
public void InsertSort(int[] arr) {
    //控制拿去每一个元素
     //注意这里从1开始
    for(int i=1;i<arr.length;i++){
        //比较次数
        for (int j=i;j>0;j--){
            //是否小于前面的元素,和前面大于当前元素的交换,直到前一个元素小于当前元素
            if (arr[j] < arr[j-1]){
                int temp = arr[j];
                arr[j] = arr[j-1];
                arr[j-1] = temp;
            }else {
                break;
            }
        }
    }
 }

4、快速排序

4.1 原理

原理:分治法。每次选出一个基准数据(一般认为是第一个数据)。将基准插入到合适位置(这个位置左边的数都小于基准,右边的数都大于基准)。递归实现。

4.2 代码实现
class Solution {
    Random random = new Random();
    public int[] sortArray(int[] nums) {
        quickSort(nums,0,nums.length-1);
        return nums;
    }
    public void quickSort(int[] nums,int start,int end){
        if(start < end){
            int center = partition(nums,start,end);
            quickSort(nums,start,center-1);
            quickSort(nums,center+1,end);
        }
        
    }
    public int partition(int[] nums, int l, int r) {
        int ran = random.nextInt(r - l + 1)+l;
        swap(nums,r,ran);
        int pivot = nums[r];
        int i = l - 1;
        for (int j = l; j <= r - 1; ++j) {
            if (nums[j] <= pivot) {
                i = i + 1;
                swap(nums, i, j);
            }
        }
        swap(nums, i + 1, r);
        return i + 1;
    }

    public void swap(int[] nums,int i,int j){
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}


//单链表快排实现
public class Solution {
    /**
     * 
     * @param head ListNode类 the head node
     * @return ListNode类
     */
    public ListNode sortInList (ListNode head) {
        // write code here
        if(head == null || head.next == null){
            return head;
        }
        return quickSort(head);
    }
    public ListNode quickSort(ListNode head){
        if(head == null || head.next == null){
            return head;
        }
        ListNode lhead = new ListNode(-1);
        ListNode mhead = new ListNode(-1);
        ListNode rhead = new ListNode(-1);
        ListNode p = head;
         //将链表分成三段,第一段都比枢轴小,第二段等于枢轴,第三段都大于枢轴
        ListNode left = lhead;
        ListNode mid = mhead;
        ListNode right = rhead;
        
        int pivot = head.val;
        while(p != null){
            if(p.val < pivot){
                left.next = p;
                left = left.next;
            }else if(p.val == pivot){
                mid.next = p;
                mid = mid.next;
            }else{
                right.next = p;
                right = right.next;
            }
            p = p.next;
        }
        left.next = null;
        right.next = null;
        mid.next = null;
        //递归对第一段和第三段进行排序;因为第二段各元素相等,不用再排序
        lhead.next = quickSort(lhead.next);
        rhead.next = quickSort(rhead.next);
        left = getTail(lhead);//获取链表最后一个元素
        if(left != null){
            left.next = mhead.next;
        }
        mid = getTail(mhead);//获取链表最后一个元素
        if(mid != null){
            mid.next = rhead.next;
        }
        return lhead.next;
    }
    public ListNode getTail(ListNode head){
        if(head == null){
            return null;
        }
        ListNode cur = head;
        while(cur.next != null){
            cur = cur.next;
        }
        return cur;
    }
}

5、堆排序

5.1 原理

原理:生成一个大顶堆(或小顶堆),每次将堆顶与最后一个数据交换位置,取出堆顶,剩余的数据重新调整堆结构。这里调整堆结构不太好理解。

5.2 代码实现
//堆排序,实现的是小顶堆,即从小到大排序
public class HeapSort {
    public static int[] sort(int[] arr){ //用数组来表示二叉树树结构
        int[] arr2 = new int[arr.length+1];
        //向后移动一位,形成二叉树的位置关系,此时,开始的root=1;left = 2*root;right=2*root+1;
        for (int i = 1; i < arr.length+1; i++) {
            arr2[i] = arr[i-1];
        }
        int len = arr2.length;
        while(len != 1){
            arr2 = shiftHeapByInsert(arr2,len);    //对数组[1,len]的位置形成大顶堆
            swap(arr2,1,len-1);              //将大顶堆的堆顶放入尾部(并非最后)
            len--;
        }
        for (int i = 0; i < arr2.length-1; i++) {
            arr[i] = arr2[i+1];
        }
        return arr;
    }

    public static int[] shiftHeapByInsert(int[] arr,int len){
        int[] arr2 = arr;
        //注意这里从下标2开始,下标为1的是树的根,即堆顶
        for (int i = 2; i < len; i++) {
            int k = i; //代表插入arr中的位置
            arr2[k] = arr[i];
            //每个元素通过与父节点对比,上浮找到自己的位置
            while(k != 1){
                int j = k/2; //arr[k] 的父节点为arr[k/2],即arr[j]
                if(arr2[k] > arr2[j]){//当父节点的值小于子节点时,交换,直到去到对应的层数
                    swap(arr2,k,j);
                    k = j;
                }else{
                    break;
                }
            }
        }
        return arr2;
    }
}

6、希尔排序

6.1 原理

原理:将小于n的整数d1作为第一个距离增量,第一次让所有距离为d1的数据排序。第二次让所有为d2的数据排序(d2<d1),,,直到最后一次让所有距离为1的数据排序,也就是所有数据排序。这里的距离增量可以人为设置。其中每次让所有距离为d的数据排序的算法为直接插入排序。

6.2 代码实现
//希尔排序
public class ShellSort {
    public static int[] sort(int[] arr){
        int h=1;	//增量
        int len = arr.length;
        while (h < len/3)
            h = 3*h + 1; //可以调整,比如每次是两倍
        while(h >= 1){
            for (int i = h; i <len ; i++) {
                for(int j = i; j >= h && arr[j]<arr[j-h] ; j -= h )
                    swap(arr, j, j-h);
            }
            h = h/3;
        }
        return arr;
    }
}

//法二:
public void shellSort(int[] arr) {
    int len = arr.length;
    for(int gap=len/2;gap>=1;gap = gap/2){
        for(int i=gap;i<len;i++){
            for(int j=i;j>=gap && arr[j]<arr[j-gap];j -= gap){
                swap(arr,j,j-gap);
            }
        }
    }
}

7、归并排序

7.1 原理

原理:分治法。相当于将数组分成两份,将左右两边分别排好序之后再归并起来。用递归的方法实现。

7.2 代码实现
//归并排序,递归,分治法
//从小到大排序
public class MergeSort {
    public static void sort(int[] arr){
        mergeSort(arr,0,arr.length-1);
    }

    public static void mergeSort(int[] arr,int start,int end){
        if(end - start == 1){
            if(arr[start] > arr[end]){
                swap(arr,start,end);
            }
            return;
        }
        if(end == start) return;

        int center = (start+end)/2;
        mergeSort(arr,start,center);
        mergeSort(arr,center+1,end);
        merge(arr,start,center,end);
    }

    //归并,相当于归并两个数组
    public static void merge(int[] arr,int start,int center,int end){
        int[] newArr = new int[end-start+1];
        int i = start;
        int j = center+1;
        int k = 0;
        while(i<=center && j<=end){
            if(arr[i] < arr[j]){
                newArr[k++] = arr[i++];
            }else{
                newArr[k++] = arr[j++];
            }
        }

        if(i<=center){
            for(;i<=center;i++)
                newArr[k++] = arr[i];
        }
        if(j<=end){
            for(;j<=end;j++)
                newArr[k++] = arr[j];
        }
        k = 0;
        for (int l = start; l <= end; l++) {
            arr[l] = newArr[k++];
        }
    }

}

8、计数排序

8.1 原理

在这里插入图片描述

缺点/局限性:

  • 当数据最大值和最小值差距过大时,并不适用于计数排序
  • 当数列元素不是整数时,并不适用于计数排序。
8.2 代码实现
/**
 * @author Ling
 * @date 2022/7/28 11:54
 */
public class CountSort {
    public static void main(String[] args) {
        int[] arr = new int[]{3,8,-2,5,-7,21,6,3};
        System.out.println("计数排序前:");
        System.out.println(Arrays.toString(arr));
        countSort(arr);
        System.out.println("计数排序后:");
        System.out.println(Arrays.toString(arr));
    }

    private static void countSort(int[] arr) {
        // 找到数组中的最大最小值---> max:8
        int min = arr[0];
        int max = arr[0];
        for(int i=1;i<arr.length;i++){
            max = Math.max(max,arr[i]);
            min = Math.min(min,arr[i]);
        }
        //计数
        int[] count = new int[max - min + 1];
        for(int i=0;i<arr.length;i++){
            count[arr[i]-min]++;
        }
        for(int i=1;i<count.length;i++){
            count[i] += count[i-1];
        }
        //通过计数确定当前数的位置,得到排序结果
        int[] newArr = new int[arr.length];
        for(int i=0;i<arr.length;i++){
            newArr[count[arr[i]-min]-1] = arr[i];
            count[arr[i]-min]--;
        }
        for(int i=0;i<arr.length;i++){
            arr[i] = newArr[i];
        }
    }
}

9、桶排序

9.1 原理

桶排序可以看成是计数排序的升级版,它将要排的数据分到多个有序的桶里,每个桶里的数据再单独排序,再把每个桶的数据依次取出,即可完成排序。
桶排序:将值为i的元素放入i号桶,最后依次把桶里的元素倒出来。

9.2 代码实现
public class BucketSort{
    public static void sort(int[] arr){
        //最大最小值
        int max = arr[0];
        int min = arr[0];
        int length = arr.length;
		//得到最大最小值
        for(int i=1; i<length; i++) {
            if(arr[i] > max) {
                max = arr[i];
            } else if(arr[i] < min) {
                min = arr[i];
            }
        }
        //最大值和最小值的差
        int diff = max - min;

        //桶列表
        ArrayList<ArrayList<Integer>> bucketList = new ArrayList<>();
        for(int i = 0; i < length; i++){
            bucketList.add(new ArrayList<>());
        }

        //每个桶的存数区间
        float section = (float) diff / (float) (length - 1);

        //数据入桶
        for(int i = 0; i < length; i++){
            //当前数除以区间得出存放桶的位置 减1后得出桶的下标
            int num = (int) (arr[i] / section) - 1;
            if(num < 0){
                num = 0;
            }
            bucketList.get(num).add(arr[i]);
        }

        //桶内排序
        for(int i = 0; i < bucketList.size(); i++){
            //jdk的排序速度当然信得过
            Collections.sort(bucketList.get(i));
        }

        //写入原数组
        int index = 0;
        for(ArrayList<Integer> arrayList : bucketList){
            for(int value : arrayList){
                arr[index] = value;
                index++;
            }
        }
    }
}

10、基数排序

10.1 原理

我们假设有一个待排序数组[53,3,542,748,14,214],那么如何使用基数排序对其进行排序呢?
首先我们有这样的十个一维数组,在基数排序中也叫桶。用桶排序实现。
img

一轮,以元素的个位数进行区分:[542,53,3,14,214,748]

在这里插入图片描述

第二轮,以元素的十位数进行区分:[3,14,214,542,748,53]

在这里插入图片描述

第三轮,以元素的百位数进行区分:[3,14,53,214,542,748]

在这里插入图片描述

10.2 代码实现
//法一:只能实现正数,负数会溢出
public class RaixSort{
	public static void main(String[] args) {
		int[] arr = { 53, 3, 542, 748, 14, 214 };

		// 得到数组中最大的数
		int max = arr[0];// 假设第一个数就是数组中的最大数
		for (int i = 1; i < arr.length; i++) {
			if (arr[i] > max) {
				max = arr[i];
			}
		}
		// 得到最大数是几位数
		// 通过拼接一个空串将其变为字符串进而求得字符串的长度,即为位数
		int maxLength = (max + "").length();

		// 定义一个二维数组,模拟桶,每个桶就是一个一维数组
		// 为了防止放入数据的时候桶溢出,我们应该尽量将桶的容量设置得大一些
		int[][] bucket = new int[10][arr.length];
		// 记录每个桶中实际存放的元素个数
		// 定义一个一维数组来记录每个桶中每次放入的元素个数
		int[] bucketElementCounts = new int[10];

		// 通过变量n帮助取出元素位数上的数
		for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
			for (int j = 0; j < arr.length; j++) {
				// 针对每个元素的位数进行处理
				int digitOfElement = arr[j] / n % 10;
				// 将元素放入对应的桶中
				// bucketElementCounts[digitOfElement]就是桶中的元素个数,初始为0,放在第一位
				bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
				// 将桶中的元素个数++
				// 这样接下来的元素就可以排在前面的元素后面
				bucketElementCounts[digitOfElement]++;
			}
			// 按照桶的顺序取出数据并放回原数组
			int index = 0;
			for (int k = 0; k < bucket.length; k++) {
				// 如果桶中有数据,才取出放回原数组
				if (bucketElementCounts[k] != 0) {
					// 说明桶中有数据,对该桶进行遍历
					for (int l = 0; l < bucketElementCounts[k]; l++) {
						// 取出元素放回原数组
						arr[index++] = bucket[k][l];
					}
				}
				// 每轮处理后,需要将每个bucketElementCounts[k]置0
				bucketElementCounts[k] = 0;
			}
		}
		System.out.println(Arrays.toString(arr));//[3, 14, 53, 214, 542, 748]
	}
}

//法二:
public void bucketSort(int[] arr){//重写bucketSort方法
    int i = 0;
    int j = arr.length-1;
    //将负数都移到数组前面
    while(i<j){
        while(i<j && arr[i] <0){
            i++;
        }
        while(i<j && arr[j] >= 0){
            j--;
        }
        if (i < j){
            swap(arr,i,j);
        }
    }
    i--;
    j = arr.length-1;
    //上面的代码用于将arr分成两部分,由于负数总是比非负数小,从小到大排序也就是负数在前面
    int index = 0;
    for (int k = 0;k<=i;k++){
        if (arr[k] == Integer.MIN_VALUE){
            swap(arr,index++,k);
        }
    }
    for (int k = index;k<=i;k++){
        arr[k] *=-1;
    }
    //这一段我们处理负数部分,先将值为int类型中的最小值移动到最前端,将其余的部分都×-1使他 
    们变成正数

        bucketSort(arr,index,i);//变成正数的负数组排序
    for (int k = index;k<=i;k++){
        arr[k]*=-1;
    }
    //将所有变成正数的负数重现变成负数
    int temp = 0;
    for (int k = index;k<(i-index+1)/2+index;k++){
        swap(arr,k,i-temp);
        temp++;
    }
    //整体交换负数组的元素

    if (j>i){
        bucketSort(arr,i+1,j);//排序正数组
    }
}

参考

  1. java实现10种排序算法_努力努力再努力²的博客-CSDN博客_java排序

  2. Java十大经典排序算法的实现图解_java_脚本之家 (jb51.net)

  3. 常见排序算法整理(附Java代码)_suelta_th的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值