算法:排序算法

本文介绍了包括冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序、堆排序、计数排序、桶排序和基数排序在内的多种排序算法,详细阐述了它们的排序原理、时间复杂度和空间复杂度,并提供了相应的Java代码实现。
摘要由CSDN通过智能技术生成

排序算法特点

排序算法时间复杂度(平均)空间复杂度稳定性
冒泡排序O(n^2)O(1)稳定
选择排序O(n^2)O(1)不稳定
插入排序O(n^2)O(1)稳定
希尔特排序O(n^2) < T < O(nlogn)O(1)不稳定
快速排序O(nlogn)O(nlogn)不稳定
归并排序O(nlogn)O(n)稳定
堆排序O(nlogn)O(1)不稳定
桶排序O(n)
计数排序O(n)
基数排序O(n)

冒泡排序

排序原理:

将相邻的元素两两比较,当一个元素大于右侧相邻元素时,就交换他们的位置;当一个元素小于或等于右侧相邻元素时,位置保持不变。

时间复杂度: O(n^2),进行两次for循环。
空间复杂度: O(1)

代码实现:

public int[] sort(int[] arr) {
    int size = arr.length;
    // i代表最右侧已经有i个元素已经有序了。
    for (int i = 0; i < size - 1; i++) {
    	// j代表每次从第1个元素开始比较,已知比较到 size-i-1 个元素位置,后面已经是有序的了,所以不需要比较。
        for (int j = 0; j < size - i - 1; j++) {
        	// 这里都使用j来进行数据的两两交换。
            if (arr[j] > arr[j+1]) {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
    return arr;
}

选择排序

排序原理:

从数组的 [i, len-1] 区间中选出最小元素的下标k,并与第 i 个位置做置换;接下去从第i+1个元素开始,以此类推。

时间复杂度: O(n^2)。
空间复杂度: O(1)

代码实现:

public int[] sort(int[] arr) {
    if (arr == null || arr.length <= 1) {
        return arr;
    }
    int length = arr.length;
    // 从左往右依次将第i小的数字插入到arr[i]的位置上。
    for (int i = 0; i < length; i++) {
        int k = i; //从第i个位置开始遍历,用k记录[i,length-1]区间内的最小数字的下标。
        for (int j = i; j <length; j++) {
            if (arr[j] < arr[k]) {
                k = j; //找到比arr[k]还小的值后,将下标赋值给k。
            }
        }
        // 将arr[i]与arr[k]的值进行互换。
        int temp = arr[i];
        arr[i] = arr[k];
        arr[k] = temp;
    }
    return arr;
}

插入排序

排序原理:

将arr[i+1]元素与arr[0]到arr[i]个对比,如果arr[0]<arr[i+1]<arr[i],则将arr[i+1]元素按顺序大小插入。

时间复杂度: O(n^2)。
空间复杂度: O(1)

代码实现:

public int[] sort(int[] arr) {
    if (arr == null || arr.length <= 1) {
        return arr;
    }
    int length = arr.length;
    for (int i = 1; i < length; i++) {
        int temp = arr[i]; //将要比对元素
        int j = i;
        while (j > 0 && temp < arr[j-1]) {
            arr[j] = arr[j-1];
            j--;
        }
        arr[j] = temp;
    }
    return arr;
}

希尔排序

排序原理:

时间复杂度: O(n^2)。
空间复杂度: O(1)

代码实现:


快速排序

排序原理:

1.先从数列中取出一个数作为基准数。
2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
3.再对左右区间重复第二步,直到各区间只有一个数。

时间复杂度: O(nlogn)
空间复杂度: O(nlogn)

代码实现:

void quickSort(int[] A, int left, int right) {
    if (left < right) {
        int pos = partition(A, left, right);
        quickSort(A, left, pos - 1);
        quickSort(A, pos+1, right);
    }
}

int partition(int[] A, int left, int right) {
    int temp = A[left];
    while (left < right) {
        // 右侧元素>temp,则right指针左移
        while (left < right && A[right] > temp) {
            right--;
        }
        A[left] = A[right];
        // 左侧元素<=temp,则left指针右移
        while (left < right && A[left] <= temp) {
            left++;
        }
        A[right] = A[left];
    }
    A[left] = temp;
    return left;
}

归并排序

排序原理:

  1. 把长度为n的输入序列分成两个长度为n/2的子序列;
  2. 对这两个子序列分别采用归并排序;
  3. 将两个排序好的子序列合并成一个最终的排序序列。

时间复杂度: O(nlogn)
空间复杂度: O(n)

代码实现:

void mergeSort(int[] A, int left, int right) {
   if (left < right) {
        int mid = (left + right) / 2;
        mergeSort(A, left, mid);
        mergeSort(A, mid + 1, right);
        mergeTwoArr(A, left, mid, mid+1, right);
    }
}

private void mergeTwoArr(int A[], int L1, int R1, int L2, int R2) {
    // 需要借助另外的空间来临时存放已排序的数据。
    int[] mergeArr = new int[A.length];
    int arr1Index = L1;
    int arr2Index = L2;
    int newIndex = 0;
    while (arr1Index <= R1 && arr2Index <= R2) {
        if (A[arr1Index] <= A[arr2Index]) {
            mergeArr[newIndex++] = A[arr1Index++];
        } else {
            mergeArr[newIndex++] = A[arr2Index++];
        }
    }
    while (arr1Index <= R1) {
        mergeArr[newIndex++] = A[arr1Index++];
    }
    while (arr2Index <= R2) {
        mergeArr[newIndex++] = A[arr2Index++];
    }
	
	// 将L1到R2的已排序的数据回写到原数组A中。
    for (int i = 0; i < newIndex; i++) {
        A[L1 + i] = mergeArr[i];
    }
	// 打印数组
    printArray(mergeArr, "新数组:");
}

堆排序

排序原理: 二叉堆

代码实现:

// 将数组转化为最小堆
private void buildHeap(int[] arr) {
    int size = arr.length;
    for (int i = (size/2)-1; i>=0 ; i--) {
        siftDown(arr, i, size);
    }
}

private void siftUp(int[] arr) {
    int childIndex = arr.length-1;
    while (childIndex > 0) {
        int parentIndex = (childIndex - 1) >> 1;
        if (arr[parentIndex] < arr[childIndex]) {
            break;
        }
        swap(arr, parentIndex, childIndex);
        childIndex = parentIndex;
    }
}

private void siftDown(int[] arr, int parentIndex, int size) {
    int childIndex = leftChildIndex(parentIndex);
    while (childIndex < size) {
        int rightChildIndex = childIndex + 1;
        if (rightChildIndex < size && arr[childIndex] > arr[rightChildIndex]) {
            childIndex = rightChildIndex;
        }
        if (arr[childIndex] > arr[parentIndex]) {
            break;
        }
        swap(arr, childIndex, parentIndex);
        parentIndex = childIndex;
        childIndex = leftChildIndex(parentIndex);
    }
}

private void swap(int[] arr, int l, int r) {
    int temp = arr[l];
    arr[l] = arr[r];
    arr[r] = temp;
}
// 根据父节点找到左子节点的位置。
private int leftChildIndex(int parentIndex) {
    return 2 * parentIndex + 1;
}

计数排序

排序原理:

代码实现:


桶排序

排序原理:

在我们可以确定需要排列的数组的范围时,可以在该数值范围内生成有限个桶去对应数组中的数,然后我们将扫描的数值放入匹配的桶里的行为,可以看作是分类,在分类完成后,我们需要依次按照桶的顺序输出桶内存放的数值,这样就完成了桶排序。

例如,学生考试分数排序(总分100分)。我们可以分配100个桶,然后遍历学生成绩,并将成绩添加到对应序号的桶中(即50分就添加到50号桶),遍历结束后我们按照桶的顺序进行输出,就会输出有序列表。

缺点:

如果数据分布稀疏,就会浪费很多空间。如有10个数,但是最大的数为1000,则我们需要申请1000个桶去存放数据,此时就会浪费990个桶的空间。

代码实现:

基数排序

排序原理:

代码实现:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值