1.冒泡排序:
(1)算法原理:将第一个元素和第二个元素比较,若前者大于后者,则交换两者的位置,再将第二个元素与第三个元素比较,若前者大于后者则交换两者位置,以此类推直到倒数第二个元素与最后一个元素比较,若前者大于后者,则交换两者位置。这样一轮比较下来将会把序列中最大的元素移至序列末尾,这样就安排好了最大数的位置,接下来只需对剩下的(n-1)个元素,重复上述操作即可。
(2)算法:
public static int[] myBubbleSort(int[] arr){
for (int i = 0;i < arr.length-1;i++){
for (int j = 0;j < arr.length-1-i;j++){
if (arr[j]>arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1]= temp;
}
}
}
return arr;
}
(3)优化:
public static int[] myOptimizeBubbleSort(int[] arr){
boolean flag;
for (int i = 0;i < arr.length-1;i++){
flag = false;
for (int j = 0;j < arr.length-i-1;j++){
if (arr[j]>arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
flag = true;
}
}
if (flag==false){
break;
}
}
return arr;
}
2.快速排序
(1)算法原理
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
(2)算法实现
public class QuickSort {
/**
* 快速排序
*/
public static int[] myQuickSort(int[] arr){
quickSortProcess(arr,0,arr.length-1);
return arr;
}
/**
* 快速排序的过程
* @param arr
* @param lowIndex
* @param highIndex
*/
public static void quickSortProcess(int[] arr,int lowIndex,int highIndex){
//区间长度
int size = highIndex-lowIndex+1;
if (size<=1){
return;
}
//找到中间下标
int keyIndex = partition(arr,lowIndex,highIndex);
//对左边区间进行快速排序
quickSortProcess(arr,lowIndex,keyIndex-1);
//对右边区间进行快速排序
quickSortProcess(arr,keyIndex+1,highIndex);
}
/**
* 进行分组
* @param arr
* @param lowIndex 起始下标
* @param highIndex 终止下标
* @return 找到的关键值的下标
*/
private static int partition(int[] arr, int lowIndex, int highIndex) {
int leftIndex = lowIndex;
int rightIndex = highIndex;
//找一个分解值
int key = arr[lowIndex];
//进行区分
while (leftIndex<rightIndex){
//找到右边小于关键值的下标
while (leftIndex<rightIndex && arr[rightIndex]>=key){
rightIndex--;
}
//左边大于关键值的下标
while (leftIndex<rightIndex && arr[leftIndex]<=key){
leftIndex++;
}
//交换
swap(arr,leftIndex,rightIndex);
}
//交换关键值,即key和leftIndex(此时leftIndex和righIndex重合)
swap(arr,leftIndex,lowIndex);
return leftIndex;
}
/**
* 交换
* @param arr
* @param index1
* @param index2
*/
public static void swap(int[] arr,int index1,int index2){
int temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
public static void main(String[] args) {
int[] arr = {3,4,9,8,6,3,4};
myQuickSort(arr);
for (int i = 0 ;i < arr.length;i++){
System.out.print(arr[i]+" ");
}
}
}
3.归并排序
(1)算法思想
一般用于对总体无序,但是各子项相对有序的数列
将两个顺序序列合并成一个顺序序列的方法。
把数组平均分成两份。分别对左右两个区间进行相同的处理,知道区间内个数(size=0/1)
合并左右两个有序数组。
(2)算法实现
public class MergeSort {
public static int[] myMergeSort(int[] arr){
mergeSortPart(arr,0,arr.length);
return arr;
}
/**
* 分区间
* @param arr
* @param lowIndex
* @param highIndex
*/
private static void mergeSortPart(int[] arr, int lowIndex, int highIndex) {
//区间是左闭右开的
int size = highIndex-lowIndex;
if (size<=1){
return;
}
//找到中间下表,把整个数组平均分成两个区间
int midIndex = (highIndex+lowIndex)/2;
mergeSortPart(arr,lowIndex,midIndex);
mergeSortPart(arr,midIndex,highIndex);
//合并两个有序区间
mergeSortProcess(arr,lowIndex,midIndex,highIndex);
}
/**
* 合并两个有序区间
* @param arr 数组
* @param lowIndex 左区间左下标
* @param midIndex 左区间右下标,右区间左下标
* @param highIndex 右区间右下标
*/
private static void mergeSortProcess(int[]arr,int lowIndex,int midIndex,int highIndex){
int size = highIndex - lowIndex;
int leftIndex = lowIndex;
int rightIndex = midIndex;
int[] extraArray = new int[size];
int extraIndex = 0;
//循环,
while (leftIndex<midIndex && rightIndex< highIndex){
//左区间的数小于右区间,则把左区间的数放到额外数组中
if (arr[leftIndex]<=arr[rightIndex]){
extraArray[extraIndex]=arr[leftIndex];
leftIndex++;
}else{
//右区间的数小于左区间,则把右区间的数放到额外数组中
extraArray[extraIndex]=arr[rightIndex];
rightIndex++;
}
extraIndex++;
}
//当右区间结束,把左区间剩余的数全部依次放进额外数组中
if (leftIndex<midIndex){
while (leftIndex<midIndex){
extraArray[extraIndex++]=arr[leftIndex++];
}
}else{
//当左区间结束,把右区间剩余的数全部依次放进额外数组中
while (rightIndex<highIndex){
extraArray[extraIndex++]=arr[rightIndex++];
}
}
//再把排序好的数组搬回到原数组中,完成合并
for (int i = 0;i < size;i++){
arr[i+lowIndex]=extraArray[i];
}
}
public static void main(String[] args) {
int[] arr = {3,4,9,8,6,3,4};
myMergeSort(arr);
for (int i = 0 ;i < arr.length;i++){
System.out.print(arr[i]+" ");
}
}
}
4.堆排序
(1)算法思想
堆排序是一种树形选择排序,在排序过程中可以把元素看成是一颗完全二叉树,每个节点都大(小)于它的两个子节点,当每个节点都大于等于它的两个子节点时,就称为大顶堆,也叫堆有序; 当每个节点都小于等于它的两个子节点时,就称为小顶堆。
1.将长度为n的待排序的数组进行堆有序化构造成一个大顶堆
2.将根节点与尾节点交换并输出此时的尾节点
3.将剩余的n -1个节点重新进行堆有序化
4.重复步骤2,步骤3直至构造成一个有序序列
(2)算法实现
import java.util.Arrays;
public class HeapSort {
public static int[] myHeapSort(int[] arr){
//建大堆
heap(arr,arr.length);
//进行选择,建堆之后,最大的数在0下标,交换0下标和无需区间最后一个下标的数
for(int i = 0;i < arr.length-1;i++){
int temp = arr[0];
arr[0] = arr[arr.length-i-1];
arr[arr.length-i-1] = temp;
//交换之后进行向下调整
adjustDown(arr,arr.length-i-1,0);
}
return arr;
}
private static void heap(int[] arr,int size) {
//从第一个不是叶子结点的结点开始
for (int i = (size-2)/2;i>=0;i--){
adjustDown(arr,size,i);
}
}
/**
* 向下调整
* @param arr 数组
* @param size 需要调整的数组长度
* @param index 调整的下标
*/
private static void adjustDown(int[] arr, int size, int index) {
//有左孩子,即不是叶子结点
while (2*index+1<size){
int maxIndex = 2*index+1;
//如果右孩子存在且右孩子大于左孩子,则最大孩子为右孩子,否则为左孩子
if (maxIndex+1<size && arr[maxIndex]<arr[maxIndex+1]){
maxIndex++;
}
// 如果最大孩子小于父亲,则停止循环
if (arr[maxIndex]<=arr[index]){
break;
}
//否则,交换最大孩子和父亲
int temp = arr[index];
arr[index] = arr[maxIndex];
arr[maxIndex] =temp;
//继续向下一层调整
index = maxIndex;
}
}
public static void main(String[] args) {
int[] arr = {9,8,7,6,5,4};
System.out.println(Arrays.toString(myHeapSort(arr)));
}
}
5.简单选择排序
(1)算法思想
把数组分成两个区间,左边是无序的,右边是有序的。在无序区间中找到最大的数,放到无序区间的最后一个。
(2)算法实现
public static int[] mySelectSort(int[] arr){
for(int i = 0;i < arr.length-1;i++){
//无序区间[0,arr.length-i)
//有序区间[arr.length-i,array.length)
int maxIndex = 0;
//找到无需区间最大数的下标
for (int j = 1;j < arr.length-i;j++){
if (arr[maxIndex]<arr[j]){
maxIndex=j;
}
}
//把无序区间的最大数和无序区间最后一个数进行交换
int m = arr[maxIndex];
arr[maxIndex] = arr[arr.length-i-1];
arr[arr.length-i-1] =m;
}
return arr;
}
6.直接插入排序
(1)算法思想
把数组分成两个区间,左边是有序的,右边是无序的。
每次在无序区间选择第一个元素,在有序区间插入合适的位置。
适用于大部分已有序的排好列。
(2)算法实现
public static int[] myInsertSort(int[] arr){
//有序区间:[0,i]
//无序区间:[i+1,arr.length-1]
for (int i = 0;i < arr.length-1;i++){
int ket = arr[i+1];
int j = 0;
//从有序区间最后一个开始循环,进行比较,如果小于,则往前移,否则插入到第j个位置的后面
for ( j = i;j>=0;j–){
if (ket<arr[j]){
arr[j+1]=arr[j];
}else {
break;
}
}
arr[j+1]=ket;
}
return arr;
}
7.希尔排序
(1)算法思想
希尔发明的,分组插排
(2)算法实现
public static int[] myShellSort(int[] arr){
int gap = arr.length/2;
while(true){
insertSortGap(arr,gap);
if (gap==1){
break;
}
gap = gap/2;
}
return arr;
}
public static void insertSortGap(int[] arr,int gap){
for (int i = gap;i < arr.length;i++){
int key = arr[i];
int j = 0;
for (j = i-gap;j>=0;j = j-gap){
if (arr[j]>key){
arr[j+gap] = arr[j];
}else{
break;
}
}
arr[j+gap]=key;
}
}
8.总结
排序算法
排序算法 | 最好时间复杂度 | 最坏时间复杂度 | 平均时间复杂度 | 空间复杂度 | 稳定性 |
---|---|---|---|---|---|
冒泡排序 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
选择排序 | O(n^2) | O(n^2) | O(n^2) | O(1) | 不稳定 |
插入排序 | O(n) | O(n^2) | O(n^2) | O(1) | 稳定 |
希尔排序 | O(n) | O(n^1.3) | O(n^2) | O(1) | 不稳定 |
堆排序 | O(1) | 不稳定 | |||
归并排序 | O(n) | 稳定 | |||
快速排序 | O(n^2) | O(1) | 不稳定 |