剑指Offer算法题(十)排序

目录

BubbleSort

InsertionSort

SelectionSort

QuickSort

MergeSort

HeapSort

[21. 调整数组顺序使奇数位于偶数前面](#21. 调整数组顺序使奇数位于偶数前面)

[45. 把数组排成最小的数](#45. 把数组排成最小的数)

[51. 数组中的逆序对](#51. 数组中的逆序对)


BubbleSort

java 复制代码
package Theoretical_foundation.Sort;

// ====================== 核心思路 ======================
// 题目:冒泡排序
// 思想:每一轮把当前最大的数"冒泡"到末尾
// 优化:如果某一轮没有交换,说明已经有序,提前结束
//
// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
// 稳定排序
// ======================================================

import java.util.Arrays;

public class BubbleSort {
    public int[] sort(int[] nums) {
        int length = nums.length;
        for (int i = 0; i < length - 1; i++) {
            boolean notSwapFlag = true;
            for (int j = 0; j < length - 1 - i; j++) {
                if (nums[j] > nums[j + 1]) {
                    swap(nums, j, j + 1);
                    notSwapFlag = false;
                }
            }
            if (notSwapFlag) break;
        }
        return nums;
    }

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

    }

    public static void main(String[] args) {
        BubbleSort test = new BubbleSort();
        int[] nums = new int[]{2, 5, 9, 3, 7, 4};
        System.out.println(Arrays.toString(test.sort(nums)));
    }
}

InsertionSort

java 复制代码
package Theoretical_foundation.Sort;

// ====================== 核心思路 ======================
// 类似打扑克牌插牌,左边始终有序,每次把当前元素插入正确位置
//
// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
// 稳定排序
// ======================================================

import java.util.Arrays;

public class InsertionSort {
    public void sort(int[] nums) {
        for (int i = 1; i < nums.length; i++) {
            int cur = nums[i];
            int j = i - 1;
            while (j >= 0 && nums[j] > cur) {
                nums[j + 1] = nums[j];
                j--;
            }
            nums[j + 1] = cur;
        }
    }

    public static void main(String[] args) {
        InsertionSort test = new InsertionSort();
        int[] nums = new int[]{2, 5, 9, 3, 7, 4};
        test.sort(nums);
        System.out.println(Arrays.toString(nums));
    }
}

SelectionSort

java 复制代码
package Theoretical_foundation.Sort;

// ====================== 核心思路 ======================
// 每轮选择最小值,放到当前位置
//
// 时间复杂度:O(n^2)
// 空间复杂度:O(1)
// 不稳定
// ======================================================

import java.util.Arrays;

public class SelectionSort {
    public int[] sort(int[] nums) {
        for (int i = 0; i < nums.length - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < nums.length; j++) {
                if (nums[j] < nums[minIndex]) minIndex = j;
            }
            if (minIndex != i) swap(nums, minIndex, i);
        }
        return nums;
    }

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

    public static void main(String[] args) {
        SelectionSort test = new SelectionSort();
        int[] nums = new int[]{2, 5, 9, 3, 7, 4};
        System.out.println(Arrays.toString(test.sort(nums)));
    }
}

QuickSort

java 复制代码
package Theoretical_foundation.Sort;

// ====================== 核心思路 ======================
// 快速排序:分治思想
// 选择一个基准值 pivot
// 左边都小于 pivot,右边都大于 pivot
// 递归左右区间
//
// 时间复杂度:平均 O(nlogn) 最坏 O(n^2)
// 空间复杂度:O(logn)
// ======================================================

import java.util.Arrays;
import java.util.Scanner;

public class QuickSort {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] nums = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = sc.nextInt();
        }
        QuickSort test = new QuickSort();
        test.quickSort(nums, 0, n - 1);
        System.out.println(Arrays.toString(nums));
    }

    private void quickSort(int[] nums, int start, int end) {
        if (start >= end) return;
        int pivot = partition(nums, start, end);
        quickSort(nums, start, pivot - 1);
        quickSort(nums, pivot + 1, end);
    }

    private int partition(int[] nums, int start, int end) {
        int pivot = nums[start];
        int first = start;
        while (start < end) {
            while (start < end && nums[end] >= pivot) end--; // 控制右指针向左移动,找到小于基准元素的那个数
            while (start < end && nums[start] <= pivot) start++; // 控制左指针向右移动,找到大于基准元素的那个数
            if (start < end) {
                int temp = nums[start];
                nums[start] = nums[end];
                nums[end] = temp;
            }
        }
        nums[first] = nums[start];
        nums[start] = pivot;
        return start;
    }
}

MergeSort

java 复制代码
package Theoretical_foundation.Sort;

import java.util.Arrays;
import java.util.Scanner;

// ====================== 核心思路 ======================
// 归并排序:分治 + 合并
// 不断二分,分别排序左右区间,合并两个有序数组
//
// 时间复杂度:O(nlogn)
// 空间复杂度:O(n)
// 稳定排序
// ======================================================

public class MergeSort {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] nums = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = sc.nextInt();
        }
        MergeSort test = new MergeSort();
        test.mergeSort(nums, 0, n - 1);
        System.out.println(Arrays.toString(nums));
    }

    private void mergeSort(int[] nums, int start, int end) {
        if (start >= end) return;
        int mid = (start + end) / 2;
        mergeSort(nums, start, mid);
        mergeSort(nums, mid + 1, end);
        merge(nums, start, mid, end);
    }

    private void merge(int[] nums, int start, int mid, int end) {
        int[] res = new int[end - start + 1];
        int s1 = start, s2 = mid + 1;
        int k = 0;
        while (s1 <= mid && s2 <= end) {
            if (nums[s1] <= nums[s2]) {
                res[k++] = nums[s1++];
            } else {
                res[k++] = nums[s2++];
            }
        }
        while (s1 <= mid) res[k++] = nums[s1++];
        while (s2 <= end) res[k++] = nums[s2++];

        for (int i = 0; i < res.length; i++) {
            nums[start + i] = res[i];
        }
    }
}

HeapSort

java 复制代码
package Theoretical_foundation.Sort;

// ====================== 核心思路 ======================
// 堆排序:大根堆
// 建堆,堆顶最大值交换到末尾,调整剩余堆
//
// 时间复杂度:O(nlogn)
// 空间复杂度:O(1)
// ======================================================

import java.util.Arrays;
import java.util.Scanner;

public class HeapSort {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] nums = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = sc.nextInt();
        }
        HeapSort test = new HeapSort();
        test.heapSort(nums);
        System.out.println(Arrays.toString(nums));
    }

    private void heapSort(int[] nums) {
        if (nums == null || nums.length == 0) {
            return;
        }
        int n = nums.length;

        // 从最后一个非叶子节点开始,逐步构建大根堆
        for (int i = n / 2 - 1; i >= 0; i--) {
            heapify(nums, n, i);
        }

        // 逐一交换堆顶元素和末尾元素,调整堆
        for (int i = n - 1; i > 0; i--) {
            swap(nums, 0, i);
            heapify(nums, i, 0);// 顶部元素向下沉
        }
    }

    private void heapify(int[] nums, int n, int i) {
        int largest = i;
        int left = i * 2 + 1;
        int right = i * 2 + 2;

        // 选出子节点里最大的记录在 largest 里,供后面交换
        if (left < n && nums[left] > nums[largest]) {
            largest = left;
        }
        if (right < n && nums[right] > nums[largest]) {
            largest = right;
        }

        // 交换后,继续对交换的位置进行判断
        if (largest != i) {
            swap(nums, i, largest);
            heapify(nums, n, largest);// 递归
        }
    }


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

21. 调整数组顺序使奇数位于偶数前面

java 复制代码
package Sort;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

public class OddBeforeEven {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine());
        String[] str = br.readLine().split(" ");
        int[] nums = new int[n];
        for (int i = 0; i < n; i++) {
            nums[i] = Integer.parseInt(str[i]);
        }
        OddBeforeEven test = new OddBeforeEven();
        System.out.println(Arrays.toString(test.reOrderArray(nums)));
    }

    public int[] reOrderArray(int[] array) {
        // write code here
        int oddNumber = 0;
        int n = array.length;
        for (int i = 0; i < n; i++) {
            if (array[i] % 2 == 1) oddNumber++;
        }
        int[] res = new int[n];
        for (int i = 0, j = oddNumber, k = 0; k < n; k++) {
            if (array[k] % 2 == 1) res[i++] = array[k];
            else res[j++] = array[k];
        }
        return res;
    }

}

45. 把数组排成最小的数

java 复制代码
package Sort;

import java.util.Arrays;

// ====================== 核心思路 ======================
// 贪心思想:自定义排序
//
// 对于两个数字 x 和 y:不比较 x 和 y 本身大小,而是比较 x+y 和 y+x
// 如果 x+y < y+x,那么 x 应该排在 y 前面
//
// 举例:3 和 32
// 比较:"332" 和 "323",因为"323" 更小,所以 32 应该排在 3 前面
//
// 时间复杂度:O(nlogn)
// 空间复杂度:O(n)
// ======================================================

public class MinNumberFromArray {
    public String PrintMinNumber(int[] numbers) {
        // write code here
        if (numbers == null || numbers.length == 0) return "";
        String[] numberString = new String[numbers.length];
        for (int i = 0; i < numbers.length; i++) {
            numberString[i] = numbers[i] + "";
        }
        Arrays.sort(numberString, (o1, o2) -> (o1 + o2).compareTo(o2 + o1));
        StringBuilder sb = new StringBuilder();
        for (String s : numberString) {
            sb.append(s);
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        MinNumberFromArray test = new MinNumberFromArray();
        int[] numbers = new int[]{3, 32, 321};
        System.out.println(test.PrintMinNumber(numbers));
    }
}

51. 数组中的逆序对

java 复制代码
package Sort;

// ====================== 核心思路 ======================
// 题目:数组中的逆序对
// 解法:归并排序 + 逆序对统计
// 核心思想:在 merge 过程中统计跨区间逆序对
//
// 当 nums[l1] > nums[l2],由于左右两部分已经分别有序,说明:
// nums[l1...mid] 都 > nums[l2]
// 所以一次性增加mid - l1 + 1
//
// 时间复杂度:O(n log n)
// 空间复杂度:O(n)
// ======================================================

public class InversePairs {
    private long cnt = 0;

    public int inversePairs(int[] nums) {
        // write code here
        mergeSort(nums, 0, nums.length - 1);
        return (int) (cnt % 1000000007);
    }

    private void mergeSort(int[] nums, int left, int right) {
        if (left >= right) return;
        int mid = (left + right) / 2;
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);
        merge(nums, left, right, mid);
    }

    private void merge(int[] nums, int left, int right, int mid) {
        int l1 = left, l2 = mid + 1, k = 0;
        int[] res = new int[right - left + 1];
        while (l1 <= mid && l2 <= right) {
            if (nums[l1] <= nums[l2]) {
                res[k++] = nums[l1++];
            } else {
                res[k++] = nums[l2++];
                cnt += mid - l1 + 1;
            }
        }
        while (l1 <= mid) res[k++] = nums[l1++];
        while (l2 <= right) res[k++] = nums[l2++];

        for (int i = 0; i < res.length; i++) {
            nums[left + i] = res[i];
        }
    }

    public static void main(String[] args) {
        int[] nums = new int[]{364, 637, 341, 406, 747, 995, 234, 971, 571, 219, 993, 407, 416, 366, 315, 301, 601, 650, 418, 355, 460, 505, 360, 965, 516, 648, 727, 667, 465, 849, 455, 181, 486, 149, 588, 233, 144, 174, 557, 67, 746, 550, 474, 162, 268, 142, 463, 221, 882, 576, 604, 739, 288, 569, 256, 936, 275, 401, 497, 82, 935, 983, 583, 523, 697, 478, 147, 795, 380, 973, 958, 115, 773, 870, 259, 655, 446, 863, 735, 784, 3, 671, 433, 630, 425, 930, 64, 266, 235, 187, 284, 665, 874, 80, 45, 848, 38, 811, 267, 575};
        InversePairs test = new InversePairs();
        System.out.println(test.inversePairs(nums));
    }
}
相关推荐
handsomethefirst2 小时前
【算法与数据结构】【面试经典150题】【题36-题40】
数据结构·算法·面试
不知名的老吴2 小时前
深刻理解“程序 = 算法 + 数据结构”
数据结构
寒月小酒2 小时前
3.29+3.30
数据结构·算法
Rabitebla2 小时前
排序算法专题(一):插入排序 & 希尔排序
数据结构·算法·排序算法
问好眼12 小时前
《算法竞赛进阶指南》0x04 二分-1.最佳牛围栏
数据结构·c++·算法·二分·信息学奥赛
会编程的土豆12 小时前
【数据结构与算法】优先队列
数据结构·算法
minji...14 小时前
Linux 进程信号(二)信号的保存,sigset_t,sigprocmask,sigpending
linux·运维·服务器·网络·数据结构·c++·算法
罗湖老棍子14 小时前
最大数(信息学奥赛一本通- P1549)(洛谷-P1198)
数据结构·算法·线段树·单点修改 区间求最大值
垫脚摸太阳17 小时前
第 36 场 蓝桥·算法挑战赛·百校联赛---赛后复盘
数据结构·c++·算法