数据结构--排序算法

目录

一.排序相关概念

  • 排序:使一串记录按照某个关键字的大小,递增或递减的排列起来
  • 稳定性:在未排序的序列中,存在多个具有相同关键字的记录,如果在经过排序之后,这些记录的相对次序保持不变,则称这种排序算法是稳定的
  • 内部排序:数据元素全部放在内存中的排序
  • 外部排序:相关元素太多不能同时放在内存中,根据排序过程的要求不能在内外存之间移动数据的排序

二.常见排序算法

1.堆排序

  • 基本思想:建立大/小根堆,将堆尾元素(end位置元素)与堆顶元素(堆中最大/小值)交换,调整end位置前的所有结点使其重新满足大/小根堆的性质,同时end位置向前移动,循环这个过程直至end位置移动堆顶位置(每次都将堆的最大/小值移动到堆尾)
  • 时间复杂度:O(NlogN)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
java 复制代码
    public void heapSort(int[] array) {
        createBigHeap(array);
        int end = array.length - 1;
        while (end > 0) {
            swap(array, 0, end);
            siftDown(array, 0, end);
            end--;
        }
    }

    public void createBigHeap(int[] array) {
        for (int parent = (array.length - 1 - 1) / 2; parent >= 0; parent--) {
            siftDown(array, parent, array.length);
        }
    }

    public void siftDown(int[] array, int parent, int end) {
        int child = 2 * parent + 1;
        while (child < end) {
            if (child + 1 < end && array[child] < array[child + 1]) {
                child++;
            }
            if (array[child] > array[parent]) {
                swap(array, child, parent);
                parent = child;
                child = 2 * parent + 1;
            } else {
                break;
            }
        }
    }

    public void swap(int[] array, int i, int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

2.插入排序

  • 基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列
  • 时间复杂度:O(N^2^)
  • 空间复杂度:O(1)
  • 稳定性:稳定
  • 特点:元素集合越接近有序,插入排序算法的时间效率就越
java 复制代码
    public void insertSort(int[] array) {
        for (int i = 1; i < array.length; i++) {
            int tmp = array[i];// 获取插入的元素
            int j = i - 1;
            for (; j >= 0; j--) { // 寻找要插入的位置
                if (array[j] > tmp) {
                    array[j + 1] = array[j];
                } else {
                    break;// 找到了要插入的位置或者没找到(j移动到-1位置)
                }
            }
            array[j + 1] = tmp;
        }
    }

3.希尔排序

  • 基本思想:先选定一个整数gap,把待排序文件中所有记录分成多个组,所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后,重复上述分组和排序的工作。当gap=1时,所有记录排序完成
  • 时间复杂度:约O(N^1.25^)~N(1.6*N^1.25^)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
java 复制代码
    public void shellSort(int[] array) {
        int gap = array.length;
        while (gap > 0) {
            gap = gap / 2;
            shell(array, gap);
        }
    }

    public void shell(int[] array, int gap) {
        for (int i = gap; i < array.length; i++) {
            int tmp = array[i];
            int j = i - gap;
            for (; j >= 0; j--) {
                if (array[j] > tmp) {
                    array[j + gap] = array[j];
                } else {
                    break;
                }
            }
            array[j + gap] = tmp;
        }
    }

4.选择排序

  • 基本思想:每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完
  • 时间复杂度:O(N^2^)
  • 空间复杂度:O(1)
  • 稳定性:不稳定
java 复制代码
    public void selectSort(int[] array) {
        for (int i = 0; i < array.length - 1; i++) {
            int minIndex = i;
            for (int j = i + 1; j < array.length; j++) {
                if (array[j] < array[minIndex]) {
                    minIndex = j;
                }
            }
            swap(array, i, minIndex);
        }
    }

    public void swap(int[] array, int i, int j) {
        int tmp = array[i];
        array[i] = array[j];
        array[j] = tmp;
    }

5.冒泡排序

  • 基本思想:在待排序的数据元素中,将相邻的两个数进行比较,若前面的数比后面的数大就交换两数,否则不交换;循环这个过程,直至最终完成排序(小的数据向前移动,大的数据向后移动)
  • 时间复杂度:O(N^2^)
  • 空间复杂度:O(1)
  • 稳定性:稳定
java 复制代码
    public void bubbleSort(int[] array) {
        for (int i = 0; i < array.length - 1; i++) {
            boolean flag = false;
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j] > array[j + 1]) {
                    swap(array, j, j + 1);
                    flag = true;
                }
            }
            if (!flag) {
                return;
            }
        }
    }

6.快速排序

  • 基本思想:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上
  • 时间复杂度:O(NlogN)
  • 空间复杂度:O(logN)
  • 稳定性:不稳定

1.快速排序--递归(未优化)

java 复制代码
    public void quickSort(int[] array) {
        quick(array, 0, array.length - 1);
    }

    public void quick(int[] array, int start, int end) {
        if (start >= end) {
            return;
        }
        int pivot = partition1(array, start, end);// 基准值下标
        // 左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值
        quick(array, start, pivot - 1);// 递归左子序列
        quick(array, pivot + 1, end);// 递归右子序列
    }

    // 将区间按照基准值划分为左右两半部分的常见方式:Hoare法和挖坑法
    // Hoare法
    public int partition1(int[] array, int left, int right) {
        // left和right分别为array子序列的最左和最右元素下标
        int tmp = array[left];// 基准值
        int start = left;
        while (left < right) {
            // right向前走,寻找<=基准值的元素下标
            while (left < right && array[right] >= tmp) {
                right--;
            }
            // left向后走,寻找>=基准值的元素下标
            while (left < right && array[left] <= tmp) {
                left++;
            }
            // 交换left和right下标的元素
            swap(array, left, right);
        }
        // left和right相遇,将基准值与相遇位置元素交换
        swap(array, start, left);
        return left;
    }

    // 挖坑法
    public int partition2(int[] array, int left, int right) {
        int tmp = array[left];
        // left下标元素填入tmp中,留下一个坑(left下标)
        while (left < right) {
            // right向前走,寻找<=基准值的元素下标
            while (left < right && array[right] >= tmp) {
                right--;
            }
            array[left] = array[right];// 填入坑(left下标)中,留下一个坑(right下标)
            // left向后走,寻找>=基准值的元素下标
            while (left < right && array[left] <= tmp) {
                left++;
            }
            array[right] = array[left];// 填入坑(right下标)中,留下一个坑(left下标)
        }
        // left和right相遇,将tmp填入相遇位置这个坑中
        array[left] = tmp;
        return left;
    }

2.快速排序--递归(优化)

  • 三数取中法选key(减少划分区间时没有左/右子序列的情况)
  • 递归到小的区间时,使用插入排序
java 复制代码
    public void quickSort(int[] array) {
        quick(array, 0, array.length - 1);
    }

    public void quick(int[] array, int start, int end) {
        if (start >= end) {
            return;
        }
        if (start - end + 1 <= 10) { // 子序列长度较短时,使用插入排序
            insertSortSTE(array, start, end);
            return;
        }
        int mid = middle(array, start, end);// 获取中间值的下标
        swap(array, start, mid);// 交换start和mid下标元素,使得基准值为mid元素
        int pivot = partition1(array, start, end);
        quick(array, start, pivot - 1);
        quick(array, pivot + 1, end);
    }

    public int middle(int[] array, int left, int right) { // 三数取中法(left,mid,right)
        int mid = left + ((right - left) >> 1);
        int[] num = {array[left], array[mid], array[right]};
        Arrays.sort(num);
        if (array[left] == num[1]) {
            return left;
        } else if (array[right] == num[1]) {
            return right;
        } else {
            return mid;
        }
    }
    
    // 指定区间插入排序
    public void insertSortSTE(int[] array, int start, int end) {
        for (int i = start + 1; i <= end; i++) {
            int tmp = array[i];
            int j = i - 1;
            for (; j >= start; j--) {
                if (array[j] > tmp) {
                    array[j + 1] = array[j];
                } else {
                    break;
                }
            }
            array[j + 1] = tmp;
        }
    }

3.快速排序--非递归

java 复制代码
    public void quickSortNor(int[] array) {
        int left = 0;
        int right = array.length - 1;
        int pivot = partition1(array, left, right);
        Stack<Integer> stack = new Stack<>();
        if (pivot - 1 > left) {
            stack.push(left);
            stack.push(pivot - 1);
        }
        if (pivot + 1 < right) {
            stack.push(pivot + 1);
            stack.push(right);
        }
        while (!stack.isEmpty()) {
            right = stack.pop();
            left = stack.pop();
            pivot = partition1(array, left, right);
            if (pivot - 1 > left) {
                stack.push(left);
                stack.push(pivot - 1);
            }
            if (pivot + 1 < right) {
                stack.push(pivot + 1);
                stack.push(right);
            }
        }
    }

7.归并排序

  • 基本思想:归并排序是建立在归并操作上的一种有效的排序算法,为分治法的一个典型应用,即将待排序的数据分段排序合并
  • 时间复杂度:O(NlogN)
  • 空间复杂度:O(N)
  • 稳定性:稳定

1.归并排序--递归

java 复制代码
    public void mergeSort(int[] array) {
        branch(array, 0, array.length - 1);
    }

    public void branch(int[] array, int left, int right) {
        if (left >= right) {
            return;
        }
        int mid = left + ((right - left) >> 1);
        branch(array, left, mid);// 分解左子序列
        branch(array, mid + 1, right);// 分解右子序列
        merge(array, left, mid, right); // 合并子序列
    }

    public void merge(int[] array, int left, int mid, int right) {
        int s1 = left;
        int e1 = mid;
        int s2 = mid + 1;
        int e2 = right;
        int index = 0;
        int[] ans = new int[right - left + 1];
        while (s1 <= e1 && s2 <= e2) {
            if (array[s1] <= array[s2]) {
                ans[index++] = array[s1++];
            } else {
                ans[index++] = array[s2++];
            }
        }
        while (s1 <= e1) {
            ans[index++] = array[s1++];
        }
        while (s2 <= e2) {
            ans[index++] = array[s2++];
        }
        for (int i = left; i <= right; i++) {
            array[i] = ans[i - left];
        }
    }

2.归并排序--非递归

java 复制代码
    public void merge(int[] array, int left, int mid, int right) {
        int s1 = left;
        int e1 = mid;
        int s2 = mid + 1;
        int e2 = right;
        int index = 0;
        int[] ans = new int[right - left + 1];
        while (s1 <= e1 && s2 <= e2) {
            if (array[s1] <= array[s2]) {
                ans[index++] = array[s1++];
            } else {
                ans[index++] = array[s2++];
            }
        }
        while (s1 <= e1) {
            ans[index++] = array[s1++];
        }
        while (s2 <= e2) {
            ans[index++] = array[s2++];
        }
        for (int i = left; i <= right; i++) {
            array[i] = ans[i - left];
        }
    }

    public void mergeSortNor(int[] array) {
        int gap = 1;
        while (gap < array.length) {
            for (int i = 0; i < array.length; i++) {
                int left = i;
                int mid = left + gap - 1;
                if (mid >= array.length) {
                    mid = array.length - 1;
                }
                int right = mid + gap;
                if (right >= array.length) {
                    right = array.length - 1;
                }
                merge(array, left, mid, right);
            }
            gap *= 2;
        }
    }
相关推荐
前端郭德纲9 分钟前
浏览器是加载ES6模块的?
javascript·算法
SoraLuna18 分钟前
「Mac玩转仓颉内测版10」PTA刷题篇1 - L1-001 Hello World
算法·macos·cangjie
杨哥带你写代码30 分钟前
网上商城系统:Spring Boot框架的实现
java·spring boot·后端
camellias_31 分钟前
SpringBoot(二十一)SpringBoot自定义CURL请求类
java·spring boot·后端
布川ku子32 分钟前
[2024最新] java八股文实用版(附带原理)---Mysql篇
java·mysql·面试
白-胖-子1 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-成绩排序
c++·算法·蓝桥杯·真题·蓝桥等考
向阳12181 小时前
JVM 进阶:深入理解与高级调优
java·jvm
背水1 小时前
初识Spring
java·后端·spring
ahadee1 小时前
蓝桥杯每日真题 - 第11天
c语言·vscode·算法·蓝桥杯
用屁屁笑1 小时前
Java:JVM
java·开发语言·jvm