数据结构的排序算法实现demo(详解)

1、冒泡排序(Bubble Sort):将相邻的两个元素进行比较,如果前面的元素大于后面的元素,则交换它们的位置,直到没有任何一对相邻元素需要交换为止。

具体实现步骤如下:

  1. 比较相邻的元素。如果第一个元素比第二个元素大,就交换它们的位置。
  2. 对每一对相邻元素做同样的工作,从开始的第一对到结尾的最后一对。这一步完成后,最后的元素会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上述步骤,直到没有任何一对相邻元素需要交换为止。 冒泡排序的时间复杂度为O(n^2),因此对于大量数据的排序效率较低。但是,由于其实现简单,代码量少,容易理解等优点,冒泡排序仍然是一种常用的排序算法。

public static int[] bubbleSort(int[] arr) {
    int n = arr.length;
    // 遍历所有数组元素
    for (int i = 0; i < n-1; i++) {
        // 每次遍历都将最大元素移到末尾
        for (int j = 0; j < n-i-1; j++) {
            // 如果当前元素大于下一个元素,则交换它们的位置
            if (arr[j] > arr[j+1]) {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
    return arr;
}
// 示例输入
int[] arr = {5, 2, 8, 12, 9, 3, 1, 7};
// 输出结果
System.out.println(Arrays.toString(bubbleSort(arr)));

以上代码中,bubbleSort函数接收一个整型数组作为参数,返回一个排序后的整型数组。

首先,获取数组的长度n,然后使用两个嵌套的for循环来遍历所有的数组元素。

在内层循环中,比较相邻的两个元素,如果当前元素大于下一个元素,就交换它们的位置。这样,每次遍历都会将最大元素移到末尾。

最后,返回排序后的数组。

使用以上代码示例,输入{5, 2, 8, 12, 9, 3, 1, 7}作为参数,

                                输出结果为[1, 2, 3, 5, 7, 8, 9, 12]

2、选择排序(Selection Sort):将数组分为已排序区间和未排序区间,每次从未排序区间中选取最小的元素,将其放入已排序区间的末尾。

具体实现步骤如下:

  1. 首先在未排序的序列中找到最小(或最大)的元素。
  2. 将其存放到已排序序列的末尾。
  3. 对剩余未排序元素重复以上步骤,直到所有元素均排序完毕。 选择排序的时间复杂度为O(n^2),因此对于大量数据的排序效率也较低。但是,选择排序仍然具有简单易懂,实现容易等优点,适合用于小规模数据的排序。

public static int[] selectionSort(int[] arr) {
    int n = arr.length;
    // 遍历所有数组元素
    for (int i = 0; i < n; i++) {
        // 找到未排序序列中最小元素的位置
        int min_idx = i;
        for (int j = i+1; j < n; j++) {
            if (arr[j] < arr[min_idx]) {
                min_idx = j;
            }
        }
        // 将最小元素移到已排序序列的末尾
        int temp = arr[i];
        arr[i] = arr[min_idx];
        arr[min_idx] = temp;
    }
    return arr;
}
// 示例输入
int[] arr = {5, 2, 8, 12, 9, 3, 1, 7};
// 输出结果
System.out.println(Arrays.toString(selectionSort(arr)));

以上代码中,selectionSort函数接收一个整型数组作为参数,返回一个排序后的整型数组。

首先,获取数组的长度n,然后使用两个嵌套的for循环来遍历所有的数组元素。

在外层循环中,每次找到未排序序列中最小元素的位置min_idx,在内层循环中,遍历未排序序列中的所有元素,如果找到比当前元素更小的元素,则更新min_idx

这样,每次遍历都会找到未排序序列中的最小元素,然后将其移到已排序序列的末尾。最后,返回排序后的数组。

使用以上代码示例,输入{5, 2, 8, 12, 9, 3, 1, 7}作为参数,

                                输出结果为[1, 2, 3, 5, 7, 8, 9, 12]

3、插入排序(Insertion Sort):将数组分为已排序区间和未排序区间,每次从未排序区间中选取第一个元素,将其插入到已排序区间中的合适位置。

插入排序的步骤如下:

  1. 从第二个元素开始遍历数组。
  2. 将当前元素保存到变量key中。
  3. 在已排序的序列中,从后往前比较元素大小。
  4. 如果已排序序列中的元素比当前元素更大,则将它们向右移动一个位置,直到找到插入位置。
  5. 将当前元素插入到已排序序列的正确位置。
  6. 重复步骤2~5,直到遍历完整个数组。
  7. 返回有序的数组。 以上步骤可以保证每次遍历都能将一个元素插入到已排序序列中,并且保证已排序序列始终有序。最终得到的序列为完全有序的数组。

public static int[] insertionSort(int[] arr) {
    int n = arr.length;
    // 从第二个元素开始遍历
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        // 在已排序序列中找到插入位置
        while (j >= 0 && arr[j] > key) {
            arr[j+1] = arr[j];
            j--;
        }
        arr[j+1] = key;
    }
    return arr;
}
// 示例输入
int[] arr = {5, 2, 8, 12, 9, 3, 1, 7};
// 输出结果
System.out.println(Arrays.toString(insertionSort(arr)));

以上代码中,insertionSort函数接收一个整型数组作为参数,返回一个排序后的整型数组。

首先,获取数组的长度n,然后从第二个元素开始遍历所有的数组元素。

在每次遍历时,将当前元素key保存下来,然后在已排序序列中查找插入位置。

如果已排序序列中的元素比当前元素更大,则将它们向右移动一个位置,直到找到插入位置。

最后,将当前元素插入到已排序序列的正确位置。

这样,每次遍历都会将一个元素插入到已排序序列中,最终得到一个有序的数组。

使用以上代码示例,输入{5, 2, 8, 12, 9, 3, 1, 7}作为参数,

                                输出结果为[1, 2, 3, 5, 7, 8, 9, 12]

4、快速排序(Quick Sort):以一个数为基准(pivot),将数组分为小于基准的部分和大于基准的部分,再对这两部分分别进行快速排序。

快速排序的步骤如下:

  1. 选择一个枢轴元素,将数组分为左右两个子数组。
  2. 对左右两个子数组分别递归调用快速排序算法。
  3. 直到子数组长度为1或0时停止递归。
  4. 将已排序的左右两个子数组合并成一个有序的数组。
  5. 返回有序的数组。 以上步骤的关键是选取枢轴元素和将数组分为左右两个子数组的过程。选取枢轴元素有多种方法,一般有三种方式:选择第一个元素、选择最后一个元素和选择中间的元素。在将数组分为左右两个子数组时,可以使用双指针法或者挖坑法来实现。快速排序的核心思想是分治法,将大问题分为小问题处理,最后将结果合并起来。

public static void quickSort(int[] arr, int left, int right) {
    if (left >= right) {
        return;
    }
    int pivot = partition(arr, left, right);
    quickSort(arr, left, pivot-1);
    quickSort(arr, pivot+1, right);
}
public static int partition(int[] arr, int left, int right) {
    int pivot = arr[right];
    int i = left - 1;
    for (int j = left; j < right; j++) {
        if (arr[j] < pivot) {
            i++;
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    int temp = arr[i+1];
    arr[i+1] = arr[right];
    arr[right] = temp;
    return i+1;
}
// 示例输入
int[] arr = {5, 2, 8, 12, 9, 3, 1, 7};
quickSort(arr, 0, arr.length-1);
// 输出结果
System.out.println(Arrays.toString(arr));


以上代码中,quickSort函数接收一个整型数组、左边界和右边界作为参数,实现快速排序算法。

首先,判断左边界是否大于等于右边界,如果是,则返回。

然后,选择partition函数返回的中间位置pivot,将左右两个子数组分别递归调用quickSort函数,直到左右边界相等。

partition函数接收一个整型数组、左边界和右边界作为参数,实现选取枢轴元素并将数组分为左右两个子数组的过程

。首先,选择最右边的元素作为枢轴元素pivot,然后从左到右遍历整个数组,将小于枢轴元素的元素交换到左边,将大于等于枢轴元素的元素交换到右边。

最后,将枢轴元素交换到正确的位置,将数组分为左右两个子数组。返回枢轴元素所在的位置。

使用以上代码示例,输入{5, 2, 8, 12, 9, 3, 1, 7}作为参数,

                                输出结果为[1, 2, 3, 5, 7, 8, 9, 12]

5、归并排序(Merge Sort):将数组分为两半,分别对左半边和右半边进行归并排序,然后将两个有序的子数组合并成一个有序的数组。

归并排序的基本思想是将一个待排序数组分成两个子数组,对这两个子数组分别进行递归排序,最后将排好序的两个子数组合并成一个有序数组。具体步骤如下:

  1. 将待排序数组从中间位置分成两个子数组,左子数组包含原数组左半部分,右子数组包含原数组右半部分,直到子数组只包含一个元素为止。
  2. 对左子数组和右子数组进行递归排序,直到子数组只包含一个元素为止。
  3. 合并排好序的左子数组和右子数组,得到一个有序数组。
  4. 重复步骤3,直到所有子数组都合并成一个有序数组。 归并排序的时间复杂度为O(nlogn),比较稳定,但是需要额外的空间来存储已排序的子数组。

public static void mergeSort(int[] arr, int left, int right) {
    if (left >= right) {
        return;
    }
    int mid = (left + right) / 2;
    mergeSort(arr, left, mid);
    mergeSort(arr, mid+1, right);
    merge(arr, left, mid, right);
}
public static void merge(int[] arr, int left, int mid, int right) {
    int[] temp = new int[right-left+1];
    int i = left;
    int j = mid+1;
    int k = 0;
    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
        }
    }
    while (i <= mid) {
        temp[k++] = arr[i++];
    }
    while (j <= right) {
        temp[k++] = arr[j++];
    }
    for (int p = 0; p < temp.length; p++) {
        arr[left+p] = temp[p];
    }
}
// 示例输入
int[] arr = {5, 2, 8, 12, 9, 3, 1, 7};
mergeSort(arr, 0, arr.length-1);
// 输出结果
System.out.println(Arrays.toString(arr));


以上代码中,mergeSort函数接收一个整型数组、左边界和右边界作为参数,实现归并排序算法。

首先,判断左边界是否大于等于右边界,如果是,则返回。

然后,将数组分为左右两个子数组,分别递归调用mergeSort函数,直到左右边界相等。

最后,将已排序的左右两个子数组合并成一个有序的数组。

merge函数接收一个整型数组、左边界、中间位置和右边界作为参数,实现将已排序的左右两个子数组合并成一个有序的数组的过程。

首先,创建一个临时数组temp,然后从两个子数组的开头开始比较元素大小,将较小的元素放到temp数组中,直到其中一个子数组已经遍历完了。

最后,将剩余的元素放到temp数组中,将temp数组中的元素复制回原数组中。

这样,就将两个有序的子数组合并成一个有序的数组了。

使用以上代码示例,输入{5, 2, 8, 12, 9, 3, 1, 7}作为参数,

                                输出结果为[1, 2, 3, 5, 7, 8, 9, 12]

6、堆排序(Heap Sort):将数组看成一棵完全二叉树,将其转化为一个最大堆,然后将堆顶元素与最后一个元素交换,再重新调整堆,直到堆中只剩下一个元素。

堆排序的基本思想是将待排序的数组构建成一个大根堆或小根堆,然后将堆顶元素与堆底元素交换,再重新构建堆,重复这个过程直到所有元素都排好序。具体步骤如下:

  1. 将待排序的数组构建成一个大根堆或小根堆。
    • 大根堆的定义是每个节点的值都大于或等于其左右孩子节点的值。
    • 小根堆的定义是每个节点的值都小于或等于其左右孩子节点的值。
    • 一般情况下,我们使用大根堆进行堆排序。
  2. 将堆顶元素(最大值或最小值)与堆底元素交换。
  3. 重新构建大根堆或小根堆。
    • 对于大根堆,从堆顶开始向下调整堆,保证所有子树都是大根堆。
    • 对于小根堆,从堆顶开始向下调整堆,保证所有子树都是小根堆。
  4. 重复步骤2和3,直到所有元素都排好序。 堆排序的时间复杂度为O(nlogn),比较稳定,但需要额外的空间来存储堆。堆排序适用于数据量较大的情况,对于数据量较小的情况,其时间复杂度可能会比插入排序和快速排序还要高。

public static void heapSort(int[] arr) {
    // 建立大根堆
    buildMaxHeap(arr);
    // 排序过程
    for (int i = arr.length-1; i > 0; i--) {
        swap(arr, 0, i);
        adjustHeap(arr, 0, i-1);
    }
}
public static void buildMaxHeap(int[] arr) {
    int len = arr.length;
    // 从最后一个非叶子节点开始向下调整堆
    for (int i = len/2-1; i >= 0; i--) {
        adjustHeap(arr, i, len-1);
    }
}
public static void adjustHeap(int[] arr, int i, int len) {
    int left = 2*i+1;
    int right = 2*i+2;
    int maxIndex = i;
    // 找到i节点、左孩子节点和右孩子节点中的最大值,将最大值交换到i节点
    if (left <= len && arr[left] > arr[maxIndex]) {
        maxIndex = left;
    }
    if (right <= len && arr[right] > arr[maxIndex]) {
        maxIndex = right;
    }
    if (maxIndex != i) {
        swap(arr, i, maxIndex);
        adjustHeap(arr, maxIndex, len);
    }
}
public static void swap(int[] arr, int i, int j) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
// 示例输入
int[] arr = {5, 2, 8, 12, 9, 3, 1, 7};
heapSort(arr);
// 输出结果
System.out.println(Arrays.toString(arr));


以上代码中,heapSort函数接收一个整型数组作为参数,实现堆排序算法。

首先,调用buildMaxHeap函数建立一个大根堆。

接下来,进行排序过程,将堆顶元素(即最大值)与最后一个元素交换,然后调整剩余元素的大根堆,重复这个过程直到所有元素都排好序。

buildMaxHeap函数接收一个整型数组作为参数,实现建立一个大根堆的过程。

从最后一个非叶子节点开始向下调整堆,保证所有子树都是大根堆。

adjustHeap函数接收一个整型数组、一个节点索引和一个堆的长度作为参数,实现将一个节点和它的左右孩子节点中的最大值交换的过程。

首先,找到节点、左孩子和右孩子中的最大值,并将最大值交换到节点。

然后,递归调用adjustHeap函数,调整交换后的节点。

swap函数接收一个整型数组和两个索引作为参数,实现交换两个元素的过程。

使用以上代码示例,输入{5, 2, 8, 12, 9, 3, 1, 7}作为参数,

                                输出结果为[1, 2, 3, 5, 7, 8, 9, 12]

7、希尔排序(Shell Sort):将数组分为若干个子序列,对每个子序列进行插入排序,然后再将所有子序列合并成一个完整的序列,重复上述操作直到整个序列有序。

希尔排序的基本思想是将待排序的数组按照一定的增量分成若干个子序列,对每个子序列进行插入排序,随着增量逐渐减少,每个子序列包含的元素越来越多,当增量减至1时,整个序列恰好被分成一个子序列,此时对整个序列进行插入排序,排序完成。具体步骤如下:

  1. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1。
  2. 按增量序列个数k,对序列进行k趟排序。
    • 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m的子序列,分别对每个子序列进行插入排序。
  3. 重复步骤2,直到增量序列变为1,即最后一次排序为对整个序列进行插入排序。 希尔排序的时间复杂度取决于所选的增量序列,一般情况下希尔排序的时间复杂度为O(nlogn)。希尔排序是一种不稳定的排序算法,因为相同的元素可能会被分到不同的子序列中排序。

public static void shellSort(int[] arr) {
    int len = arr.length;
    int gap = len/2;
    while (gap > 0) {
        for (int i = gap; i < len; i++) {
            int temp = arr[i];
            int j = i - gap;
            while (j >= 0 && arr[j] > temp) {
                arr[j+gap] = arr[j];
                j -= gap;
            }
            arr[j+gap] = temp;
        }
        gap /= 2;
    }
}
// 示例输入
int[] arr = {5, 2, 8, 12, 9, 3, 1, 7};
shellSort(arr);
// 输出结果
System.out.println(Arrays.toString(arr));


以上代码中,shellSort函数接收一个整型数组作为参数,实现希尔排序算法。首先,确定希尔增量gap的初值,一般取数组长度的一半。然后,对于每个增量gap,从第gap个元素开始,按照插入排序的方式对元素进行排序。重复这个过程直到gap=1,即所有元素都已排好序。 使用以上代码示例,输入{5, 2, 8, 12, 9, 3, 1, 7}作为参数,输出结果为[1, 2, 3, 5, 7, 8, 9, 12]。 希尔排序的时间复杂度取决于所选的增量序列,一般情况下希尔排序的时间复杂度为O(nlogn)。希尔排序是一种不稳定的排序算法,因为相同的元素可能会被分到不同的子序列中排序。


创作不易,点赞收藏,一起交流学习哟+++++

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值