几种简单排序

package com.shine.sort;
/**
* 排序算法 <br>
* @说明:<br>
* 排序算法分为三种:插入排序、交换排序、选择排序 <br>
* 1.插入排序:直接插入排序、折半插入排序、希尔排序 <br>
* 2.交换排序:冒泡排序、快速排序 <br>
* 3.选择排序: 直接选择排序、堆排序 <br>
*/
public class MySort {

/**
* 直接插入排序<br>
* @描述:<br>
* 1. 直接插入排序是第i趟把第i个元素放的前面已经排好序的序列中去<br>
* 2. 重复上述操作。n个元素共需n-1趟扫描,每趟将一个元素插入到它前面的子序列中 <br>
* @复杂度分析:<br>
* 1.时间复杂度:<br>
* 最好情况:当数据序列已排序,时间复杂度为o(n) <br>
* 最坏情况:数据序列反序,时间复杂度为o(n*n) <br>
* 随机:o(n*n) <br>
* 2.空间复杂度:o(1) <br>
* @稳定性:稳定
*/
public static void insertSort(int[] table){
for(int i=1;i<table.length;i++){//n-1趟扫描
int temp=table[i],j;//每趟将table[i]插入到前面排序序列中
for(j=i-1;j>=0&&temp<table[j];j--)
table[j+1]=table[j];//将较大元素向后移动
table[j+1]=temp;//temp到达插入位置
}
}
/**
* 希尔排序 <br>
* @描述: <br>
* 1.将一个数据序列分成若干组,每组由若干相隔一段距离的元素组成,这段距离称为增量,
* 在一组内采用直接插入排序算法进行排序 <br>
* 2.当增量为1时,只有一组,元素是整个序列,再进行一次直接插入排序即可 <br>
* @复杂度分析:
* 1.时间复杂度:时间复杂度比较复杂,具体时间取决于具体增量的序列 <br>
* 2.空间复杂度:o(1)
* @稳定性:不稳定
*/
public static void shellSort(int[] table){
for(int delta=table.length/2;delta>0;delta/=2)//进行若干趟扫描,控制增量,增量减半
for(int i=delta;i<table.length;i++){//一趟分若干组,每组进行直接插入排序
int temp=table[i],j;
for(j=i-delta;j>=0&&temp<table[j];j-=delta)
table[j+delta] = table[j];
table[j+delta] = temp;
}
}
/**
* 冒泡排序 <br>
* @说明: <br>
* 比较两个相邻元素的关键值,如果反序,则交换。若按升序排序,每一趟将被扫描的数据序列中的最大元素交换到
* 最后位置,就像气泡从水里冒出一样。 <br>
* @复杂度分析:<br>
* 1.时间复杂度分析:o(n)-o(n*n)
* 2.空间复杂度分析:o(1)
* @稳定性:稳定
*/
public static void bubleSort(int[] table){
boolean exchange = true;
for(int i=1;i<table.length&&exchange;i++){//有交换时才进行下一趟排序
exchange = false;//假定元素没有交换
for(int j=0;j<table.length-i;j++)
if(table[j]>table[j+1]){
int temp = table[j];
table[j] = table[j+1];
table[j+1] = temp;
exchange = true;
}
}
}
/**
* 快速排序算法 <br>
* @说明: <br>
* 在数据序列中选择一个值作为比较的基准值,每趟从数据序列的两端开始交替进行,将小于基准值的元素交换到序列
* 前段,大于基准值的元素交换到序列后端,介于两者之间的位置则称为基准值的最终位置。同时序列被划分成两个子
* 序列,在用同样的方法对两个子序列进行排序,直到子序列长度为1,则完成排序。
* @复杂度分析: <br>
* 时间复杂度:<br>
* 最好情况:每趟排序将序列分成长度相近的两个子序列。 o(nlog2n) <br>
* 最坏情况:每趟排序将序列分成长度差异很大的两个子序列 o(n*n) <br>
* 空间复杂度:<br>
* 最好情况: o(log2n)
* 最坏情况: o(n)
* @稳定性:不稳定<br>
*/
public static void quickSort(int[] table){
quickSort(table,0,table.length-1);
}
private static void quickSort(int[] table,int begin,int end){
if(begin<end){
int i=begin,j=end;
int vot=table[i];//基准值
while(i!=j){//一趟排序
while(i<j&&vot<=table[j])
j--;
if(i<j)//如果有小元素(当有table[j]<vot的时候,该条件恒成立)
table[i++]=table[j];//把后面的小元素向前移动
while(i<j&&table[i]<=vot)
i++;
if(i<j)
table[j--]=table[i];//把大元素向后移动
}
table[i]=vot;//基准值到达最终位置
quickSort(table,begin,j-1);//前段子序列进行排序
quickSort(table,i+1,end);//后端子序列进行排序
}
}
/**
* 直接选择排序 <br>
* @说明:<br>
* 第一趟从n个元素的数据中选出关键字最小或最大的元素放到最前或最后位置,下一趟再从n-1个元素……。直接选择排序
* 算法可用顺序表和单链表实现。 <br>
* @复杂度: <br>
* 时间复杂度:o(n*n) <br>
* 空间复杂度:o(1) <br>
* @稳定性:不稳定 <br>
*/
public static void selectSort(int[] table){
for(int i=0;i<table.length-1;i++){
int min=i;
for(int j=i+1;j<table.length;j++)
if(table[j]<table[min])
min=j;
if(min!=i){
int temp=table[i];
table[i]=table[min];
table[min]=temp;
}
}
}
/**
* 堆排序
* @说明: <br>
* 1.堆:这里的堆不是数据结构里面的堆栈,而是一种数据结构。堆可以视为一颗完全二叉树,完全二叉树的一个优秀的性质是
* 除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示,每一个节点对应数组中的一个元素。二叉堆一般分
* 为两种,最小堆和最大堆。<br>
* 2.最大堆:堆中每个父节点的值都大于等于其孩子节点,这样的堆就是一个最大堆。<br>
* 最小堆:对中的每个父节点都小于等于其孩子节点,这样的堆就是一个最小堆。 <br>
* 3.堆排序:堆排序分两个阶段。首先将一个数据序列建成堆序列,根节点值是最小(大)值;然后采用选择排序思路,每趟将
* 根结点值交换到最后,再将其余值调整成堆,依次重复,直到排序完成。<br>
* @复杂度:<br>
* 时间复杂度:o(n*log2n) <br>
* 空间复杂度:o(1) <br>
* @稳定性:不稳定
*/

public static void heapSort(int[] table){//堆排序
int n=table.length;
for(int j=n/2-1;j>=0;j--) //创建最小堆
sift(table,j,n-1);
for(int j=n-1;j>0;j--){ //每趟将最小值交换到后面,再调整成堆
int temp=table[0];
table[0]=table[j];
table[j]=temp;
sift(table,0,j-1);
}
}
//将以begin为根的子树调整成最小堆,begin、end是序列下届和上届
private static void sift(int[] table,int begin,int end){
int i=begin,j=2*i+1;//i为子树的根,j为i节点的左孩子
int temp=table[i];//获得第i个元素的值
while(j<=end){ //沿较小值孩子节点向下筛选
if(j<end&&table[j]>table[j+1]) //数组元素比较
j++; //j为左右孩子的较小者
if(temp>table[j]){ //若父母结点值较大
table[i]=table[j]; //孩子节点中的较小者上移
i=j; //i,j向下一层
j=2*i+1;
}
else break;
}
table[i]=temp;//当前子树的原根值调整后的位置
}
/**
* 归并排序
* @param args
*/
//一次归并方法: m和r分别是两个相邻的两个已排序的子序列的起始下标
private static void merge(int[] X,int[] Y,int m,int r,int n){ //一次归并
int i=m,j=r,k=m;
while(i<r&&j<r+n&&j<X.length){ //将X中两个相邻子序列归并到Y中
if(X[i]<X[j]) //较小值复制到Y中
Y[k++]=X[i++];
else Y[k++]=X[j++];
while(i<r) //将前一个子序列剩余元素复制到Y中
Y[k++]=X[i++];
while(j<r+n&&j<X.length) //将后一个子序列剩余元素复制到Y中
Y[k++]=X[j++];
}
}
//一趟归并
private static void mergepass(int[] X,int[] Y,int n){//一趟归并
int i=0;
while(i<X.length-2*n+1){
merge(X,Y,i,i+n,n);
i+=2*n;
}
if(i+n<X.length)
merge(X,Y,i,i+n,n); //再一次归并
else
for(int j=i;j<X.length;j++) //将X剩余元素复制到Y中
Y[j]=X[j];
}
public static void mergeSort(int[] X){ //归并排序
int[] Y = new int[X.length];
int n=1;
while(n<X.length){
mergepass(X,Y,n);
n*=2;
if(n<X.length){
mergepass(Y,X,n);
n*=2;
}
}
}
public static void main(String[] args) {
int[] table = {22,11,65,33,21,23,10};
System.out.println("直接插入排序:");
insertSort(table);
for(int x: table)
System.out.print(x+"\t");

System.out.println("\n希尔排序:");
shellSort(table);
for(int x: table)
System.out.print(x+"\t");

System.out.println("\n冒泡排序:");
bubleSort(table);
for(int x: table)
System.out.print(x+"\t");

System.out.println("\n快速排序:");
quickSort(table);
for(int x: table)
System.out.print(x+"\t");

System.out.println("\n直接选择排序:");
selectSort(table);
for(int x: table)
System.out.print(x+"\t");

System.out.println("\n堆排序:");
heapSort(table);
for(int x: table)
System.out.print(x+"\t");

System.out.println("\n归并排序:");
mergeSort(table);
for(int x: table)
System.out.print(x+"\t");

}

}
### 选择排序 选择排序是一种简单直观的排序算法。它的基本思想是:每次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(或最大)元素,放到已排序序列的末尾。重复这一过程,直到所有元素排序完成。 该算法通过两个嵌套循环实现。外层循环控制排序的轮数,内层循环负责在未排序部分查找最小值,并将其与当前轮数的第一个元素交换位置。由于每次交换都能将一个元素放到其最终正确的位置,因此总共需要 n-1 轮比较和交换操作(n 为数组长度)。 以下是一个使用 Java 实现的完整示例,包括排序和打印功能: ```java public class SelectSort { public static void main(String[] args) { int[] array = {5, 9, 1, 9, 5, 3, 7, 6, 1}; // 待排序数组 sort(array); print(array); } /** 从小到大 */ public static void sort(int array[]) { int length = array.length; for (int i = 0; i < length; i++) { int minIndex = i; // 查找第(i+1)个最小元素,放到下标为i的位置 for (int j = i + 1; j < array.length; j++) { if (array[j] < array[minIndex]) { minIndex = j; } } // 如果minIndex的值变了,说明有更小的元素,则array[minIndex]和array[i]进行交换 if (minIndex != i) { int temp = array[minIndex]; array[minIndex] = array[i]; array[i] = temp; } } } /** 打印数组 */ public static void print(int array[]) { for (int i = 0; i < array.length; i++) { System.out.print(array[i] + " "); } System.out.println(); } } ``` 选择排序具有以下特点: - **时间复杂度**:无论数据分布如何,选择排序的时间复杂度均为 $O(n^2)$,其中 n 为数组长度。这使其在大规模数据排序中效率较低。 - **空间复杂度**:仅为 $O(1)$,是一种原地排序算法。 - **稳定性**:选择排序是一种**不稳定排序算法**,因为在交换过程中可能打乱相同元素的相对顺序。 - **适用场景**:适用于数据量较小的排序任务,例如嵌入式系统或教学用途。 ### 冒泡排序 冒泡排序是一种简单排序算法,通过重复地遍历要排序的列表,比较相邻的元素,如果它们的顺序错误就交换它们,直到没有需要交换的元素为止。 冒泡排序的基本思想是:每次比较相邻的两个元素,如果前一个元素大于后一个元素,则交换它们,这样每一轮遍历后,最大的元素会“冒泡”到列表的末尾。重复这一过程,直到整个列表有序。 以下是一个使用 Java 实现的冒泡排序示例: ```java public class BubbleSort { public static void main(String[] args) { int[] array = {5, 9, 1, 9, 5, 3, 7, 6, 1}; // 待排序数组 sort(array); print(array); } /** 从小到大 */ public static void sort(int array[]) { int length = array.length; for (int i = 0; i < length - 1; i++) { for (int j = 0; j < length - 1 - i; j++) { if (array[j] > array[j + 1]) { // 交换 int temp = array[j]; array[j] = array[j + 1]; array[j + 1] = temp; } } } } /** 打印数组 */ public static void print(int array[]) { for (int i = 0; i < array.length; i++) { System.out.print(array[i] + " "); } System.out.println(); } } ``` 冒泡排序的特点如下: - **时间复杂度**:平均和最坏情况下的时间复杂度为 $O(n^2)$,其中 n 为数组长度。 - **空间复杂度**:仅为 $O(1)$,是一种原地排序算法。 - **稳定性**:冒泡排序是一种**稳定排序算法**,因为相等元素的相对顺序不会被改变。 - **适用场景**:适用于数据量较小的排序任务,但由于效率较低,实际应用较少。 ### 插入排序 插入排序是一种简单直观的排序算法,其基本思想是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增加1的有序表。插入排序适用于小数据量的排序任务。 以下是一个使用 Java 实现的插入排序示例: ```java public class InsertionSort { public static void main(String[] args) { int[] array = {5, 9, 1, 9, 5, 3, 7, 6, 1}; // 待排序数组 sort(array); print(array); } /** 从小到大 */ public static void sort(int array[]) { int length = array.length; for (int i = 1; i < length; i++) { int key = array[i]; int j = i - 1; while (j >= 0 && array[j] > key) { array[j + 1] = array[j]; j--; } array[j + 1] = key; } } /** 打印数组 */ public static void print(int array[]) { for (int i = 0; i < array.length; i++) { System.out.print(array[i] + " "); } System.out.println(); } } ``` 插入排序的特点如下: - **时间复杂度**:平均和最坏情况下的时间复杂度为 $O(n^2)$,但在部分有序数据中效率较高,最好情况下(数据已经有序)时间复杂度为 $O(n)$。 - **空间复杂度**:仅为 $O(1)$,是一种原地排序算法。 - **稳定性**:插入排序是一种**稳定排序算法**,因为相等元素的相对顺序不会被改变。 - **适用场景**:适用于数据量较小的排序任务,或者作为更复杂排序算法的子过程。 ### 希尔排序 希尔排序(Shell Sort)是插入排序的一种改进版本,其核心思想是先将整个待排序的序列分割成若干个子序列,分别进行插入排序,然后再对整体进行一次插入排序。这种做法使得原本较大的元素能够更快地移动到正确的位置,从而提高了排序效率。 以下是一个使用 Java 实现的希尔排序示例: ```java public class ShellSort { public static void main(String[] args) { int[] array = {5, 9, 1, 9, 5, 3, 7, 6, 1}; // 待排序数组 sort(array); print(array); } /** 从小到大 */ public static void sort(int array[]) { int length = array.length; for (int gap = length / 2; gap > 0; gap /= 2) { for (int i = gap; i < length; i++) { int key = array[i]; int j = i; while (j >= gap && array[j - gap] > key) { array[j] = array[j - gap]; j -= gap; } array[j] = key; } } } /** 打印数组 */ public static void print(int array[]) { for (int i = 0; i < array.length; i++) { System.out.print(array[i] + " "); } System.out.println(); } } ``` 希尔排序的特点如下: - **时间复杂度**:根据步长序列的选择不同,时间复杂度可以在 $O(n^{1.3})$ 到 $O(n^2)$ 之间变化,通常优于普通插入排序。 - **空间复杂度**:仅为 $O(1)$,是一种原地排序算法。 - **稳定性**:希尔排序是一种**不稳定排序算法**,因为在分组排序过程中可能会改变相等元素的相对顺序。 - **适用场景**:适用于中等规模的数据排序任务,特别是在嵌入式系统中。 ### 归并排序 归并排序是一种基于分治法的排序算法,其核心思想是将一个数组分成两个子数组,分别进行排序,然后将两个有序子数组合并为一个有序数组。归并排序是一个稳定的排序算法,适用于大数据量的排序任务。 以下是一个使用 Java 实现的归并排序示例: ```java public class MergeSort { public static void main(String[] args) { int[] array = {5, 9, 1, 9, 5, 3, 7, 6, 1}; // 待排序数组 sort(array); print(array); } /** 从小到大 */ public static void sort(int array[]) { mergeSort(array, 0, array.length - 1); } private static void mergeSort(int array[], int left, int right) { if (left < right) { int mid = (left + right) / 2; mergeSort(array, left, mid); // 排序左半部分 mergeSort(array, mid + 1, right); // 排序右半部分 merge(array, left, mid, right); // 合并左右部分 } } private static void merge(int array[], int left, int mid, int right) { int n1 = mid - left + 1; int n2 = right - mid; int[] leftArray = new int[n1]; int[] rightArray = new int[n2]; for (int i = 0; i < n1; ++i) leftArray[i] = array[left + i]; for (int j = 0; j < n2; ++j) rightArray[j] = array[mid + 1 + j]; int i = 0, j = 0; int k = left; while (i < n1 && j < n2) { if (leftArray[i] <= rightArray[j]) { array[k] = leftArray[i]; i++; } else { array[k] = rightArray[j]; j++; } k++; } while (i < n1) { array[k] = leftArray[i]; i++; k++; } while (j < n2) { array[k] = rightArray[j]; j++; k++; } } /** 打印数组 */ public static void print(int array[]) { for (int i = 0; i < array.length; i++) { System.out.print(array[i] + " "); } System.out.println(); } } ``` 归并排序的特点如下: - **时间复杂度**:始终为 $O(n \log n)$,其中 n 为数组长度。 - **空间复杂度**:为 $O(n)$,需要额外的存储空间。 - **稳定性**:归并排序是一种**稳定排序算法**。 - **适用场景**:适用于大数据量的排序任务,尤其是需要稳定性的场景。 ### 快速排序 快速排序是一种基于分治法的排序算法,其基本思想是选择一个基准元素,将数组分成两个子数组,其中一个子数组的所有元素都小于基准元素,另一个子数组的所有元素都大于基准元素,然后递归地对这两个子数组进行排序。 以下是一个使用 Java 实现的快速排序示例: ```java public class QuickSort { public static void main(String[] args) { int[] array = {5, 9, 1, 9, 5, 3, 7, 6, 1}; // 待排序数组 sort(array); print(array); } /** 从小到大 */ public static void sort(int array[]) { quickSort(array, 0, array.length - 1); } private static void quickSort(int array[], int low, int high) { if (low < high) { int pivotIndex = partition(array, low, high); quickSort(array, low, pivotIndex - 1); // 排序左半部分 quickSort(array, pivotIndex + 1, high); // 排序右半部分 } } private static int partition(int array[], int low, int high) { int pivot = array[high]; // 选择最后一个元素作为基准 int i = low - 1; // 小于基准的元素的索引 for (int j = low; j < high; j++) { if (array[j] < pivot) { i++; // 交换 int temp = array[i]; array[i] = array[j]; array[j] = temp; } } // 将基准元素放到正确的位置 int temp = array[i + 1]; array[i + 1] = array[high]; array[high] = temp; return i + 1; } /** 打印数组 */ public static void print(int array[]) { for (int i = 0; i < array.length; i++) { System.out.print(array[i] + " "); } System.out.println(); } } ``` 快速排序的特点如下: - **时间复杂度**:平均情况下为 $O(n \log n)$,最坏情况下为 $O(n^2)$(当数组已经有序时)。 - **空间复杂度**:为 $O(\log n)$,因为递归调用栈的深度为 $\log n$。 - **稳定性**:快速排序是一种**不稳定排序算法**。 - **适用场景**:适用于大数据量的排序任务,尤其是不需要稳定性的场景。 ### 常见排序算法汇总 常见的基本排序算法包括: 1. **选择排序**:通过重复选择最小元素并将其放置在正确位置来排序数组。时间复杂度为 $O(n^2)$,空间复杂度为 $O(1)$,不稳定排序。 2. **冒泡排序**:通过重复交换相邻元素来排序数组。时间复杂度为 $O(n^2)$,空间复杂度为 $O(1)$,稳定排序。 3. **插入排序**:通过将元素插入到已排序数组的正确位置来排序数组。时间复杂度为 $O(n^2)$,空间复杂度为 $O(1)$,稳定排序。 4. **希尔排序**:插入排序的改进版本,通过分组排序提高效率。时间复杂度为 $O(n^{1.3})$ 到 $O(n^2)$,空间复杂度为 $O(1)$,不稳定排序。 5. **归并排序**:基于分治法的排序算法,适用于大数据量的排序任务。时间复杂度为 $O(n \log n)$,空间复杂度为 $O(n)$,稳定排序。 6. **快速排序**:基于分治法的排序算法,适用于大数据量的排序任务。时间复杂度为 $O(n \log n)$(平均情况),空间复杂度为 $O(\log n)$,不稳定排序。 这些排序算法各有优劣,具体选择应根据数据规模、稳定性需求和性能要求进行权衡。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值