1.插入排序
直接插入排序
直接插入排序(Insertion Sort)是一种简单的排序算法,其主要思想是把待排序的元素插入到已经排好序的部分中,从而得到一个新的有序序列。具体来说,它的排序过程如下:
- 从第二个元素开始,将其与前面的元素进行比较,如果比前面的元素小,则将其插入到合适的位置,使得前面的部分仍然有序。
- 对于第三个元素到最后一个元素,重复上述过程。
直接插入排序的时间复杂度为 ,其中 n 表示数组的长度。当数组已经有序时,时间复杂度为 O(n)。
关于比较次数,最坏情况下,即待排序的序列为逆序时,直接插入排序的比较次数最多。具体来说,对于长度为 n 的数组,最坏情况下的比较次数为 ,即大约为 。在最好情况下,即待排序的序列已经有序时,直接插入排序的比较次数仅为 n-1。
public void insertOrder(int arr[]){
int i,j;
int temp;//用于交换
for(i=1;i<arr.length;i++){
temp=arr[i];
for(j=i-1;j>=0&&arr[j]>temp;j--){
arr[j+1]=arr[j];
}
arr[j+1]=temp;
}
//while实现
int n = arr.length;
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
// 将比 key 大的元素往后移动
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
希尔排序
希尔排序(Shell Sort)是一种改进的插入排序算法,也被称为缩小增量排序。它通过将整个待排序序列分割成若干个子序列来进行排序,逐步减小子序列的长度,最终完成排序。
希尔排序的基本思想是先将待排序序列按照一定间隔(增量)分割成若干个子序列,对每个子序列进行插入排序。然后逐步缩小增量,重复上述的子序列分割和排序过程,直到最后增量为1,即对整个序列进行插入排序。
希尔排序的核心在于选取合适的增量序列。常用的增量序列是采用 Knuth 提出的增量序列:h = 3*h + 1,其中 h 是增量,初始值可以是数组长度的一半。通过不断缩小增量的方式,可以使得希尔排序更高效地进行插入排序,因为较小的元素会快速地向前移动。
希尔排序的时间复杂度取决于增量序列的选择,一般情况下是 O(nlogn)。尽管希尔排序的性能不如快速排序或归并排序,但它对于中等规模的数据集仍然表现良好,并且相对于其他的排序算法而言,希尔排序的实现较为简单。
public void shareOrder(int arr[]){
int i, j, temp, d;
for(d = arr.length / 2; d >= 1; d /= 2){
for(i = d; i < arr.length; i += d){
temp = arr[i];
for(j = i - d; j >= 0 && arr[j] > temp; j -= d){
arr[j + d] = arr[j];
}
arr[j + d] = temp;
}
}
}
2.选择排序
简单选择排序
简单选择排序(Selection Sort)是一种简单直观的排序算法,其基本思想是通过不断地选择未排序部分的最小元素,将其放到已排序部分的末尾,从而逐步构建有序序列。具体来说,它的排序过程如下:
- 首先,找到未排序部分中的最小元素,并将其与未排序部分的第一个元素进行交换,将该最小元素放到已排序部分的末尾。
- 然后,再从未排序部分中找到剩余元素的最小元素,并将其与未排序部分的第一个元素进行交换,将该最小元素放到已排序部分的末尾。
- 重复以上步骤,直到未排序部分为空。
简单选择排序的时间复杂度为,其中 n 表示数组的长度。无论输入数据的初始顺序如何,它的比较次数都是一样的。但是,选择排序每次只需要进行一次交换操作,因此相对于其他交换类排序算法(如冒泡排序),它的交换次数相对较少。
public void selectOrder(int arr[]){
int i,j;
int temp;
int min;
for(i=0;i<arr.length;i++){
min=i;
//找到最小元素的下标
for(j=1;j<arr.length;j++){
if(arr[j]<arr[min]){
min=j;
}
}
//与未排序的第一个元素交换位置
temp=arr[min];
arr[min]=arr[i];
arr[i]=temp;
}
}
堆排序
堆排序是一种基于堆数据结构的排序算法。在堆排序中,我们使用二叉堆(通常是最大堆)来进行排序操作。
堆是一种特殊的完全二叉树,其中每个节点的值都大于等于(或小于等于)其子节点的值。最大堆是指根节点的值大于等于其子节点的值,而最小堆是指根节点的值小于等于其子节点的值。
堆排序的基本概念如下:
- 构建最大堆:将待排序的数组看作一个完全二叉树,并按照从右至左、从下至上的顺序,对每个非叶子节点进行调整,使得该节点的值大于等于其子节点的值。
- 排序:将最大堆的堆顶元素(即数组的第一个元素)与堆的最后一个元素交换位置,然后将堆的大小减1,并对新的堆顶元素进行调整,以满足最大堆的性质。重复这个过程,直到堆的大小为1,即数组已经有序。
- 最终得到的数组就是按照升序排列(或降序排列)的结果。
堆排序的优点是具有较好的时间复杂度和空间复杂度:
- 时间复杂度:堆排序的平均时间复杂度为O(nlogn),其中n是待排序数组的长度。
- 空间复杂度:堆排序的空间复杂度为O(1),即原地排序。
需要注意的是,堆排序是一种不稳定的排序算法,即相等元素的相对顺序可能会发生改变。
private void adjustheap(int arr[], int p) {
int temp;
int child = p * 2 + 1; // 左孩子
while (child < arr.length) {
if (child + 1 < arr.length && arr[child] < arr[child + 1]) {
child = child + 1; // 若右孩子更大,则改为右孩子
}
if (arr[child] > arr[p]) {
temp = arr[child];
arr[child] = arr[p];
arr[p] = temp;
p = child; // 更新p,则将从此下标继续上述操作
child = p * 2 + 1; // 更新child
} else {
return; // 若此时父节点更大则不用交换
}
}
}
private void heapsort(int arr[]) {
int temp;
// 建堆从序号最大的分支节点开始向下调整
for (int i = (arr.length - 1) / 2; i >= 0; i--) {
adjustheap(arr, i);
}
for (int i = arr.length - 1; i >= 1; i--) {
// 交换最大值与末尾的值
temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
// 重新调整最大堆
adjustheap(arr, 0);
}
}
3.交换排序
冒泡排序
冒泡排序(Bubble Sort)是一种简单直观的排序算法,其基本思想是重复地遍历要排序的数列,依次比较相邻的两个元素,如果它们的顺序错误就交换它们的位置,这样每一轮遍历后,最大的元素会被移动到数列的末尾。重复以上过程,直到整个数列都有序。
具体来说,它的排序过程如下:
- 对于长度为 n 的数列,进行 n - 1 轮遍历。
- 在每一轮遍历中,从第一个元素开始,依次比较相邻的两个元素,如果它们的顺序错误就交换它们的位置。
- 每一轮遍历结束后,最大的元素就会被移动到数列的末尾。
- 重复以上步骤,直到整个数列都有序。
冒泡排序的时间复杂度为,其中 n 表示数组的长度
public void bubbleOrder(int arr[]){
int i,j;
int temp;
Boolean isswap;
for(i=arr.length-1;i>=0;i--){
isswap=Boolean.FALSE;
for(j=0;j<=i;j++){
if(arr[j]>arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
isswap=Boolean.TRUE;
}
}
if(!isswap) return;
}
}