二分排序算法整理(Java版)

二分算法

在有序序列中快速找到某个数
时间复杂度O(logn) 空间复杂度O(1)

public static int commonBinarySearch(int[] arr,int key){
		int low = 0;
		int high = arr.length - 1;
		int middle = 0;			//定义middle
		
		if(key < arr[low] || key > arr[high] || low > high){
			return -1;				
		}
		
		while(low <= high){
			middle = (low + high) / 2;
			if(arr[middle] > key){
				//比关键字大则关键字在左区域
				high = middle - 1;
			}else if(arr[middle] < key){
				//比关键字小则关键字在右区域
				low = middle + 1;
			}else{
				return middle;
			}
		}
		
		return -1;		//最后仍然没有找到,则返回-1
	}

排序算法

快速排序

时间复杂度O(nlogn) 空间复杂度O(logn) 为栈所需辅助空间 不稳定排序
思想:

  1. 确定轴点。对无序序列首先找到一个轴点,一般选序列的第一个元素。
  2. 寻找轴点的正确位置。low指向序列的第一个元素,height指向最后一个元素。先寻找右边第一个比轴点小的元素,如果找不到就height–; 否则把这个比轴点小的值赋给low位置的元素。接着再从左边找第一个比轴点大的元素,找不到就low++;否则就把这个大元素赋值给height位置的元素。直到low>=height; 这样通过轴点把无序序列一分为二,左边都是比轴点小的,右边都是比轴点大的,然后把轴点插入到这个“正确的位置”。
  3. 对轴点左边的无序序列重复1和2操作,直到low>=height;
    代码:
public class Test {
    public static void main(String[] args) {
        try {
            //49 38 65 97 76 13 27 49
            int[] array = new int[]{49,38,65,97,76,13,27,49};
            Test test = new Test();
            test.quickSort(array, 0, array.length - 1);
            System.out.println(array);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //快排
    private void quickSort(int[] array, int low, int height){
        //当前array只有一个元素 无需排序
        if (low < height){
            //获取轴点
            int pivotPoint = pivotPoint(array, low, height);
            //对轴左边快排
            quickSort(array, low, pivotPoint - 1);
            //对轴右边快排
            quickSort(array, pivotPoint + 1, height);
        }
    }

    /**
     * 寻找轴点
     * @param array
     * @param low
     * @param height
     * @return
     */
    private static int pivotPoint(int[] array, int low, int height){
        int pivotPointValue = array[low];
        while (low < height){
            while (low < height && array[height] >= pivotPointValue){
                height--;
            }
            //把高的值赋给低
            array[low] = array[height];

            while (low < height && array[low] <= pivotPointValue){
                low++;
            }
            //把低的值赋给高
            array[height] = array[low];
        }
        array[low] = pivotPointValue;
        return low;
    }
}


归并排序

时间复杂度O(nlogn) 空间复杂度O(n) 所占空间较大 稳定排序
思想:
把一组无序序列先两两一对组成若干个有序序列,然后对这若干个有序序列,再两两组对合并成一个有序序列,直到这若干个有序序列只有一个为止。
在这里插入图片描述
实现:

public class MergeSort {

    public static void merge(int[] a, int low, int mid, int high) {
        int[] temp = new int[high - low + 1];
        int i = low;// 左指针
        int j = mid + 1;// 右指针
        int k = 0;
        // 把较小的数先移到新数组中
        while (i <= mid && j <= high) {
            if (a[i] < a[j]) {
                temp[k++] = a[i++];
            } else {
                temp[k++] = a[j++];
            }
        }
        // 把左边剩余的数移入数组
        while (i <= mid) {
            temp[k++] = a[i++];
        }
        // 把右边边剩余的数移入数组
        while (j <= high) {
            temp[k++] = a[j++];
        }
        // 把新数组中的数覆盖nums数组
        for (int k2 = 0; k2 < temp.length; k2++) {
            a[k2 + low] = temp[k2];
        }
    }

    public static void mergeSort(int[] a, int low, int high) {
        int mid = (low + high) / 2;
        if (low < high) {
            // 左边
            mergeSort(a, low, mid);
            // 右边
            mergeSort(a, mid + 1, high);
            // 左右归并
            merge(a, low, mid, high);
            System.out.println(Arrays.toString(a));
        }

    }

    public static void main(String[] args) {
        int a[] = { 51, 46, 20, 18, 65, 97, 82, 30, 77, 50 };
        mergeSort(a, 0, a.length - 1);
        System.out.println("排序结果:" + Arrays.toString(a));
    }
}

堆排序

时间复杂度O(nlogn) 空间复杂度O(n²) 不稳定排序
思想:
利用大根堆和小根堆的特点,根结点总是数组里最小的那个,因此每次选择堆顶元素,输出到一个新的有序序列即可。

两个核心问题:

  • 输出堆顶元素后,如何重建小根堆?
    在这里插入图片描述

  • 一开始的无序序列如何变成小根堆?
    在这里插入图片描述
    实现:

/**
 * 堆排序演示
 *
 * @author Lvan
 */
public class HeapSort {
    public static void main(String[] args) {
//        int[] arr = {5, 1, 7, 3, 1, 6, 9, 4};
        int[] arr = {16, 7, 3, 20, 17, 8};

        heapSort(arr);

        for (int i : arr) {
            System.out.print(i + " ");
        }
    }


    /**
     * 创建堆,
     * @param arr 待排序列
     */
    private static void heapSort(int[] arr) {
        //创建堆
        for (int i = (arr.length - 1) / 2; i >= 0; i--) {
            //从第一个非叶子结点从下至上,从右至左调整结构
            adjustHeap(arr, i, arr.length);
        }

        //调整堆结构+交换堆顶元素与末尾元素
        for (int i = arr.length - 1; i > 0; i--) {
            //将堆顶元素与末尾元素进行交换
            int temp = arr[i];
            arr[i] = arr[0];
            arr[0] = temp;

            //重新对堆进行调整
            adjustHeap(arr, 0, i);
        }
    }

    /**
     * 调整堆
     * @param arr 待排序列
     * @param parent 父节点
     * @param length 待排序列尾元素索引
     */
    private static void adjustHeap(int[] arr, int parent, int length) {
        //将temp作为父节点
        int temp = arr[parent];
        //左孩子
        int lChild = 2 * parent + 1;

        while (lChild < length) {
            //右孩子
            int rChild = lChild + 1;
            // 如果有右孩子结点,并且右孩子结点的值大于左孩子结点,则选取右孩子结点
            if (rChild < length && arr[lChild] < arr[rChild]) {
                lChild++;
            }

            // 如果父结点的值已经大于孩子结点的值,则直接结束
            if (temp >= arr[lChild]) {
                break;
            }

            // 把孩子结点的值赋给父结点
            arr[parent] = arr[lChild];

            //选取孩子结点的左孩子结点,继续向下筛选
            parent = lChild;
            lChild = 2 * lChild + 1;
        }
        arr[parent] = temp;
    }
}

其他排序的主要思想:

插入排序
打牌时为了保证抓过的牌有顺序,则每抓一张牌,需要插入合适的位置。

  • 直接插入排序
    从无序数组中拿取第一个,插入到新的有序数组中。
  • 折半插入排序
    直接插入排序中,寻找合适位置插入,这个寻找的过程是顺序的。折半插入就把这个插入过程优化为“折半查找”。
  • 希尔排序
    分组插入。

选择排序

  • 简单选择排序
    每次遍历无序数组,从中选择最小的元素插入到有序数组的最后一位。
  • 堆排序
    不断选择堆顶元素输出到有序数组中的最后一位。

交换排序

  • 冒泡排序
    就像湖水中的泡泡从湖底慢慢浮向水面。
    从第一个元素开始,一直向上寻找当前元素和下一个元素中的较大值,并把较大值留下继续于下一个进行比较,直到数组末尾。
    再从第一个元素开始,再次重复上述过程。
  • 快速排序
    利用pivot,low,height对整个无序数组进行递归排序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值