排序算法 -- 待增加


数据结构----排序

排序算法

堆排序

堆、优先队列的实现

图解

在这里插入图片描述

解决方案

package com.company;

import java.util.Arrays;

public class HeapSort {
    public static void main(String[] args)
    {
        int arr[] ={19,5,6,4,1,2,88,99};
        System.out.println(Arrays.toString(arr)); // [19, 5, 6, 4, 1, 2, 88, 99]
        sort(arr);
        System.out.println(Arrays.toString(arr)); // [1, 4, 2, 19, 5, 6, 88, 99]
    }

    public static void sort(int arr[])
    {
        int n = arr.length;
        // int i=(n-1)/2
        for(int i=(n-1)/2; i>=0;i--){
            adjust(arr, i);
        }
    }

    public static void adjust(int arr[], int cur)
    {
        int n = arr.length;
        int val = arr[cur];
        for(int i=2*cur+1; i<n ; i=2*i+1)
        {
            if(i+1 < n && arr[i] > arr[i+1])
                i++;
            if(arr[i] < val){
                arr[cur] = arr[i];
                arr[i] = val;
                cur = i;
            }
        }
    }
}

归并排序

  1. 分解(一分到底)
  2. 排序(排序)
  3. 合并

图解

图片来源

解决方案

递归方式:
从图片看,可以使用遍历树的时候的 后序遍历 的思想;
注意:

  1. 分解
  2. 合并
  3. 辅助数组
public class Main {
    public static  void main(String[] args)
    {
        Main man = new Main();
        int[] arr = {5,8,1,4,3,2,9,71,45,23,468,15,5661};
        int[] ret = new int[arr.length];
        System.out.println(Arrays.toString(arr)); // [5, 8, 1, 4, 3, 2, 9, 71, 45, 23, 468, 15, 5661]
        man.divide(arr, ret, 0, arr.length-1);
        System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 8, 9, 15, 23, 45, 71, 468, 5661]
        System.out.println(Arrays.toString(ret)); // [1, 2, 3, 4, 5, 8, 9, 15, 23, 45, 71, 468, 5661]

    }
    
    public void divide(int[] arr, int[] result, int start, int end)
    {// arr 是原始数组,每次合并会进行更改 // result 在合并时候作为辅助数组 // [start, end] 表示arr中的当前段
        if( start >= end) return;
        // 1.  分
        int mid = start + ((end - start)>>1);       // 1.1 计算中间下标位置
        divide(arr, result, start, mid);            // 1.2 分左边
        divide(arr, result,mid+1, end);        // 1.3 分右边
        // 2.  治
        int start1 = start, start2 = mid+1;      // 2.1 设置开始、结束位置 开始合并 [start1 ... mid]  [mid+1 ... end]
        int r_start = start;
        while(start1 <= mid && start2 <= end)    // 2.2 考虑长短不一的情况    [start1 .......  mid]  [mid+1 ... end]
            result[r_start++] = arr[start1] < arr[start2] ? arr[start1++]:arr[start2++];
        while (start1<= mid)                        // 2.2.1 左边长一点时候
            result[r_start++] = arr[start1++] ;
        while (start2 <= end)                       // 2.2.2 右边边长一点时候
            result[r_start++] = arr[start2++] ;
        for(int i = start; i<=end; i++)         // 2.3 数据从 result 拷贝回 arr
            arr[i] = result[i]; //  在主函数main()中,arr 和 ret 一致,可以删除这两行代码嘛?  不行,因为计算过程中使用到了 arr[i], result主要起辅助存储的作用。
            // 若删除最后两行,则:主函数中会输出 
            // arr = [5, 8, 1, 4, 3, 2, 9, 71, 45, 23, 468, 15, 5661] 
            // ret = [5, 8, 1, 4, 3, 2, 9, 71, 45, 23, 468, 15, 5661]
    }
}

解决具体问题:剑指 Offer 51. 数组中的逆序对

迭代方式:
下次增加

排序算法之归并排序

快速排序

  • 从序列中,选一个记录k作为轴值pivot
    • 选择策略:第一个元素、最后一个元素、中间元素、随机选择
  • 将剩余的元素,分割成 左子序列 L 和 右子序列 R
    • L 中所有元素都 < k, R 中所有元素都 > k
  • 对 L 和 R递归进行快排,直到子序列中有 0 个 或者 1 个元素,退出

图解

在这里插入图片描述

代码

public class Main {
    public static  void main(String[] args)
    {
        int[] arr2 = {5,2,9,4,6,1,7,6,8};
        System.out.println(Arrays.toString(arr2));
        quickSort( arr2, 0, arr2.length-1); // 一般来说,在快排中使用 [start, end] 更好一点
        System.out.println(Arrays.toString(arr2));
    }

    public static void quickSort(int[] arr, int start, int end){
        if(start < end)
        {
            int mid = partition(arr, start, end);
            System.out.println(start+ " -- " + mid + " -- " + end );
            quickSort(arr, start, mid-1);
            quickSort(arr, mid + 1, end);
        }
    }
    public static int partition(int[] arr, int start, int end){
        int val = arr[start];                               // 选择第一个为基准
        while(start < end) // 当start == end   则退出
        {
            while(start< end && arr[end] >= val) end--;     //右边的指针移动  这儿的 >= 符号可以保持排序的稳定性
            arr[start] = arr[end];
            while(start< end && arr[start] <= val) start++; //左边的指针移动   这儿的 <= 符号可以保持排序的稳定性
            arr[end] = arr[start];
        }
        arr[start] = val;                                   // 重新将值填进去
        return start;
    }
}

快速排序不稳定
例子: 5 1 4 4 8 6 2 9 4 —> 4 1 4 4 2 5 6 9 8
例子2: 5 1 4 4 —> 1 4 4 5


排序总结

选择 直接选择 堆排序 直接选择 堆排序 大根堆、小根堆 :找出最小(大)的Key 选择 直接选择 堆排序
交换 冒泡 快速 冒泡 大的下沉,小的上浮 快速 基于比较(两两比较) 交换 冒泡 快速
插入 简单插入 shell 简单插入 插入到有序序列中 、【有序序列】[current key]【无序序列】 shell 插入 简单插入 shell

总结表

排序算法时间复杂度空间复杂度稳定性特征1:特征2:
最好平均最坏
直接插入 O(n) O(n^2) O(n^2) O(1)1每趟确定一个最值特征2:
冒泡 O(n) O(n^2) O(n^2) O(1)1每趟确定一个最值可能循环完成之前就排好序了
简单选择 O(n^2) O(n^2) O(n^2) O(1)0每趟确定一个最值特征2:
希尔 O(n^(1.3)) O(1)0最后一趟才有序最小增量排序
快速 O(nlog2^n) O(nlog2^n) O(n^2) O(log2^n)0最后一趟才有序分治法,需要一个枢轴
堆排序 O(nlog2^n) O(nlog2^n) O(nlog2^n) O(1)0每趟确定一个最值建堆-取根-调整堆
归并排序 O(nlog2^n) O(nlog2^n) O(nlog2^n) O(n)1最后一趟才有序1、划分 2、归并
基数排序 O(d(n+r)) O(d(n+r)) O(d(n+r)) O(r)1每一趟按不同关键字有序分配、收集

最后一趟才有序:最后一趟之前,整体都是无序的(不存在连续的顺序子序列)
每趟确定一个最值:在最后一趟前,存在连续的顺序子序列

在这里插入图片描述

是否稳定方法稳定Or不稳定 的条件
稳定冒泡排序相等的时候不进行交换
直接插入排序相等的时候则跳过
折半插入排序(同直插)
归并排序归并的时候,左边与右边相等的时候先放左边
不稳定堆排序 、希尔排序、直接选择排序 why、快速排序


基数排序

LSD: 先排次要的关键字,最后排主要的关键字
MSD:先排主关键字,最后排最低层次的关键字

基数排序中LSD方法:若有N种关键字类型(就是存在个位、十位、百位。。。)那么对于Key(i)【0<= i <= N-2】时候要用稳定的排序方式。

代码

import java.util.*;
public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     * 将给定数组排序
     * @param arr int整型一维数组 待排序的数组
     * @return int整型一维数组
     */
    public int[] MySort (int[] arr) {
        // write code here
        // 冒泡
        // return bubble(arr); 
        // 选择
        // return choise(arr);
        // 插入
        // return insert(arr); 
        // 快排(最快)
        quickSort(arr, 0, arr.length-1);
        return arr;
        // 堆排
        // heapSort(arr);
        // return arr;
        // 归并
        // mergeSort(arr);
        // return arr;
    }
    // 归并排序
    public void mergeSort(int[] arr)
    {
        int[] tmp = new int[arr.length];
        merge(arr, 0, arr.length-1, tmp);
    }
    
    public void divide(int[] arr, int left, int right, int[] tmp)
    {
        if(left >= right)   
            return;
        int mid = left + ((right - left) >>> 1);
        divide(arr, left, mid, tmp);
        divide(arr, mid + 1, right, tmp);
        
        int x = left, y = mid + 1;
        int ind = left;
        while(x <= mid && y <= right){
            tmp[ind++] = arr[x] < arr[y] ? arr[x++] : arr[y++];
        }
        while(x<=mid)
        {
            tmp[ind++] = arr[x++];
        }
        while(y<=right)
        {
            tmp[ind++] = arr[y++];
        }
        for(int i = left; i<= right; i++)
            arr[i] = tmp[i];
        
    }
    // 堆排序
    public void heapSort(int[] arr)
    {
        int n= arr.length;
        for(int i= (n-1)/2; i>=0;i--)
            adjust(arr, i, n);
        for(int j=arr.length-1; j > 0;j--)
        {
            int tmp = arr[0];
            arr[0] = arr[j];
            arr[j] = tmp;
            adjust(arr, 0, j);
        }
    }
    
    public void adjust(int[] arr, int x, int len)
    {
        int t = x;
        for(int i=2*x+1; i< len; i=2*i+1)
        {
            if(i+1 < len && arr[i] < arr[i+1])
                i++;
            if(arr[i] > arr[t]){
                int tmp = arr[t];
                arr[t] = arr[i];
                arr[i] = tmp;
            }
            t = i;
        }
    }
     // 快速排序
    public void quickSort(int[] arr, int left, int right)
    {
        if(left >=right)
            return;
        int mid = getMid(arr, left, right);
        quickSort(arr, left, mid-1);
        quickSort(arr, mid+1, right);
    }
        
    public int getMid(int[] arr, int left, int right)
    {
        int val = arr[left];
        while(left < right){
            while( left < right && arr[right] >= val) 
                right--;
            if(left < right ) 
                arr[left++] = arr[right];
            while( left < right && arr[left] < val) 
                left++;
            if(left < right ) 
                arr[right--] = arr[left];
        }
        arr[left] = val;
        return left;
    }
    // 插入排序
    public int[] insert(int[] arr){

        for(int i =0; i<arr.length; i++)
        {
            int val = arr[i];
            for(int j=0; j<i; j++)
            {
                if(val < arr[j])
                {
                    for(int x = i; x > j;x--)
                    {
                        arr[x] = arr[x-1];
                    }
                    arr[j] = val;
                    break;   
                }
            }
        }
        return arr;
    }
    
    // 选择排序
    public int[] choise(int[] arr)
    {
        for(int i =0; i<arr.length; i++)
        {
            int min = i;
            for(int j=i+1;j<arr.length; j++)
            {
                if(arr[j] < arr[min])
                {
                    min = j;
                }
            }
            int temp = arr[min];
            arr[min] = arr[i];
            arr[i] = temp;
        }
        return arr;
    }
    // 冒泡排序
    public int[] bubble(int[] arr)
    {
        for(int i=0; i<arr.length;i++)
        {
            for(int j=1;j<arr.length-i; j++)
            {
                if(arr[j-1] > arr[j])
                {
                    int temp = arr[j-1];
                    arr[j-1] = arr[j];
                    arr[j] = temp;
                }
            }
        }
        return arr;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值