七种内置排序算法:
对所有相邻的元素进行排序比较,直到元素不需要比较,重复比较(数组长度-1)轮。
(1)比较相邻的元素,如果第一个数比第二个数大,就交换他们两个。
(2)对每一对相邻的元素做同样的操作,从开始第一到结尾最后一对,在最后的元素就会是最大的数。
(3)针对所有的元素重复以上的步骤。
private static void swap(T[] arr, int index1,int index2){
T temp=arr[index1];
arr[index1]=arr[index2];
arr[index2]=temp;
}
public static> void Bubblesort(T[] arr){
boolean flag;
for(int i=0; i0){
swap(arr,j,j+1);
flag=false;
}
}
if(flag){
break;
}
}
}
public static void main(String[] args){
Integer[] arr=new Integer[]{4,2,7,1,9,3,5,6,8};
Bubblesort(arr);
System.out.println(Arrays.toString(arr));
}
(1)时间复杂度分析
最佳时间复杂度O(n),当序列原本就是升序时,swap方法不执行。
最差情况O(n^2),平均时间复杂度O(n^2)。
(2)空间复杂度分析
平均空间复杂度O(1)。
(3)稳定性:稳定
选择排序的特点:选择排序是不稳定的排序算法之一,因为无论对什么数据进行排序都是O(n^2)的时间复杂度,因为选择排序的事件复杂度较大,所以当使用选择排序的时候,数据规模越小越好。
首先在未排序的序列里找到最小(最大)数,放在序列的起始位置(末尾),然后再继续从剩余的元素里找到最小的元素排在当前剩余元素序列的起始位置(末尾),以此类推,直到所有的元素排序完毕,序列有序为止。
public static void swap(T[] arr,int index1,int index2){
T temp = arr[index1];
arr[index1] = arr[index2];
arr[index2] = temp;
}
public static> void Selectsort(T[] arr) {
if (arr.length == 0) {
return;
}
/*外层for循环推进整个arr数组,把i置为最小的值
*内层for循环遍历未排序数组找到最小的j,赋值给minindex
*交换minindex和i
*/
for(int i = 0; i < arr.length; i++) {
int minindex = i;
for (int j = i; j < arr.length; j++) {
if (arr[j].compareTo(arr[minindex]) < 0) {
minindex = j;
}
}
swap(arr, minindex, i);
}
}
public static void main(String[] args){
Integer[] arr = new Integer[]{2,4,6,1,3,8,5,9};
Selectsort(arr);
System.out.println(Arrays.toString(arr));
}
(1)时间复杂度分析
最有时间复杂度O(n^2),平均时间复杂度O(n^2),最坏时间复杂度O(n^2)。
(2)空间复杂度分析
平均空间复杂度O(1)。
(3)稳定性:不稳定
原因:例如:5,8,,5,,2,,9
第一轮:2,8,,5,,5,,,9(两个5的原有顺序被破坏了)
插入排序的思想是通过构建有序序列,对于未排序的数据,在已排序的序列中从后往前进行查找操作,找到满足条件的元素之后进行位置插入操作。
(1)从序列的第一个元素开始,该元素可以认为是已经排好序的元素。
(2)取出下一个元素,在已经排序好的元素中从后往前查找。
(3)如果已经排序好的元素小于新元素,则移动到下一个元素位置。
(4)重复(3),直到找到排序好的元素大于等于新元素,则进行插入操作。
public static void swap(T[] arr,int index1,int index2){
T temp = arr[index1];
arr[index1]=arr[index2];
arr[index2]=temp;
}
public static> void insertsort(T[] arr){
/*for循环外层正序推进arr数组
*while语句内层逆序比较插入
*/
for(int i =1; i0 && arr[j].compareTo(arr[j-1])<0){
swap(arr,j,j-1);
j--;
}
}
}
public static void main(String[] args){
Integer[] arr=new Integer[]{3,1,2,8,6,7,5};
insertsort(arr);
System.out.println(Arrays.toString(arr));
}
(1)时间复杂度
最优时间复杂度O(n),平均时间复杂度O(n^2),最坏时间复杂度O(n^2).
(2)空间复杂度
平均空间复杂度O(1).
(3)稳定性:稳定
堆是一颗顺序存储的完全二叉数,完全二叉树中所有非终端节点的值均不大于(或不小于)其左右孩子节点的值。其中每个节点的值小于等于其左、右孩子的值,这样的堆称为小根堆;其中每个节点的值大于等于其左右孩子的值,这样的堆称为大根堆。
堆排序是指利用堆这种数据结构进行设计的一种排序算法。堆排序利用了大根堆(或小根堆)对顶记录的关键字最大(最小)这一特征,使得在当前无序区选取最大(或最小)关键字变得简单。
用大根堆排序的基本思想:
(1)先将初始数组建成一个大根堆,此堆为初始的无序区。
(2)将关键字最大堆顶元素R[1]和和无序区的最后一个元素R[n]交换,由此得到新的无序区R[1...n-1]和有序区R[n]。由于新交换的堆顶元素R[1]可能违反大根堆性质。因此将当前无序区R[1...n-1]重新调整为大根堆。然后再次将R[1...n-1]中关键字最大的记录R[1]和该区间的最后一个元素R[n-1]交换,得到新的无序区R[1...n-2]和有序区R[n-1...n],同样将R[1...n-2]调整为大根堆。
(3)直到无序区只有一个元素为止。
堆排序:
堆排序首先是根据元素构建堆,然后将堆的根节点取出(一般是与最后一个节点交换),将前面的len-1个节点继续进行堆调整的过程,然后再将根节点取出,这样一直到所有节点都取出。
public static void main(String[] args) {
int[] arr = new int[]{5, 1, 7, 3, 1, 6, 9, 4};
heapSort(arr);
for (int i : arr) {
System.out.print(i + " ");
}
}
private static void heapSort(int[] arr) {
//创建堆
for (int i = (arr.length - 1) / 2; i >= 0; i--) {
//从第一个非叶子节点从下至上,从左至右依次调整结构。
adjustHeap(arr, i, arr.length);
}
//交换堆顶元素和末尾元素
for (int i = arr.length - 1; i > 0; i--) {
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
//重新对堆进行调整
adjustHeap(arr, 0, i);
}
}
/*调整堆
*/
private static void adjustHeap(int[] arr,int parent,int length){
//保存父节点的值
int temp = arr[parent];
//左孩子
for(int lChild=2*parent+1;lChild
(1)时间复杂度分析:
最有时间复杂度分析O(nlog2n),平均时间复杂度O(nlog2n),最坏时间复杂度O(nlog2n)。
(2)空间复杂度分析:
O(1)
(3)稳定性:不稳定(可能会打乱原有相同数值元素的排列顺序)
希尔排序是按照一定的增量进行分组排序的,对每一组进行直接插入排序,随着分组个数的减少,每组中的元素就会越来越多,当增量减少为1时,排序结束。
选择增量gap=length/2;缩小增量继续以gap=gap/2的方式进行分组。
(1)选择一个增量序列,按照增量序列个数m,进行m趟排序。
(2)每趟排序根据对应的增量次数分别进行元素的分组操作。对组内进行直接插入排序操作。
(3)继续下一个增量,分别进行分组直接插入操作。
(4)重复第三个步骤,直到增量变成1,所有元素在一个分组内,希尔排序结束。
public static> void shellSort(T[] arr,int gap){
int i=0,j=0;
for(i=gap;i=0;j-=gap){
if(temp.compareTo(arr[j])<0){
arr[j+gap]=arr[j];
}else{
break;
}
}
arr[j+gap]=temp;
}
}
public static> void shell(T[] arr){
int[] partition = {5,3,1};
for(int i=0;i
(1)时间复杂度分析
平均时间复杂度O(n^1.3~n^1.5)。
(2)空间复杂度分析
O(1)
(3)稳定性:不稳定
归并排序采用分治法将已有序的子序列合并,得到完全有序的序列。即先让每一小段有序,再让小段之间变得有序。若将两个有序合并成一个有序段,这又成为二路归并。
(1)开始以间隔为1进行归并,即第一个元素和第二个元素归并,第三个和第四个元素归并。
(2)然后再以间隔为2进行归并,1-4进行归并,5-8进行归并。
(3)再以2*2的间隔,同理,直到2*k超过数组长度为止。
(4)当不够两组进行归并时,超过k个元素仍然进行归并;如果剩余元素不超过k个元素,那么直接复制给中间数组。
public static> void merge(T[] arr,int len,int gap){
int low1=0;
int high1=low1+gap-1;
int low2=high1+1;
int high2=low2+gap-1>len-1?len-1:low2+gap-1;
T[] brr=(T[])new Comparable[arr.length];
int i=0;
while(low2len-1?len-1:low2+gap-1;
}
while(low1<=len-1){
brr[i++]=arr[low1++];
}
for(i=0;i> void mergeSort(T[] arr){
for(int i=1;i
(1)时间复杂度分析
平均时间复杂度O(nlog2n),最坏时间复杂度O(nlog2n)。
(2)空间复杂度分析
O(n)
(3)稳定性分析:不稳定