参考文章:https://blog.csdn.net/hellozhxy/article/details/79911867
(各种排序的比较)https://blog.csdn.net/mengyue000/article/details/77505666
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
内排序:所有排序操作都在内存中完成;
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
时间复杂度: 一个算法执行所耗费的时间。
空间复杂度:运行完一个程序所需内存的大小。
注解:In-place: 占用常数内存,不占用额外内存
Out-place: 占用额外内存
思路:
第一步:从第一个元素开始,比较相邻元素后判断是否交换,直到最后一个元素。
第二步:针对所有的元素重复第一步,直到遍历中没有出现交换则结束排序。
性能分析:
时间复杂度:
2.1. 最好情况:O(n)
2.2. 平均情况:O(n^2)
2.3. 最坏情况:O(n^2)
空间复杂度:O(1)
稳定性:稳定(相同元素的相对位置不会改变)
适用场景
int [] bubbleSort(int [] data){
int flag=0;
int temp=0;
for (int i = 0; i <data.length ; i++) {
flag=0;
for (int j = 0; j <data.length-i-1 ; j++) {
if(data[j]>data[j+1]){
temp =data[j];
data[j]=data[j+1];
data[j+1]=temp;
flag=1;
}
}
if (flag==0){
break;
}
}
return data;
}
思路:类似于斗地主时候整理手牌,一步一步把最小的牌放在最左边,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
性能分析:
最佳情况:T(n) = O(n)
最坏情况:T(n) = O(n2)
平均情况:T(n) = O(n2)
空间复杂度:O(1)
稳定性:稳定(相同元素的相对位置不会改变)
适用:适用于数量较少的排序。
特点:同为稳定性排序算法,时间复杂度也一样,插入排序的效率比冒泡排序要好,尤其是数据量大的时候,冒泡排序移动数据的操作更多,只要是小于后一个元素,就要再遍历一次。所以它的效率低。(一般情况)
实现
//由于是从第一个元素开始比较,可以知道current前面都是有序递增的,注意后移要从最后开始
int [] insertSort(int[] target){
int temp;
int current=0;
for (int i = 1; i <target.length ; i++) {
temp=target[i];
current=i-1;
while(current>=0&&temp<target[current]){
target[current+1]=target[current];
current--;
}
target[current+1]=temp;
}
return target;
}
思路:在选择排序中,不再只比较两个相邻的数据。因此需要记录下某一个数据的下标,进行选择排序就是把所有的数据扫描一遍,从中挑出(按从小到大排序)最小的一个数据,这个最小的数据和最左端下标为0的数据交换位置。之后再次扫描数据,从下标为1开始,还是挑出最小的然后和1号位置进行交换,这个过程一直持续到所有的数据都排定。而程序中需要有一个标识变量来标识每次挑出最小数据的下标。
特点:选择排序改进了冒泡排序,将必要的交换次数从 O(N2)减少到 O(N)次。不幸的是比较次数仍然保持为 O(N2)。然而,选择排序仍然为大记录量的排序提出了一个非常重要的改进,因为这些大量的记录需要在内存中移动,这就使交换的时间和比较的时间相比起来,交换的时间更为重要。(一般来说,在 Java 语言中不是这种情况,Java 中只是改变了引用位置,而实际对象的位置并没有发生改变。)
性能分析:
2.4 算法分析
最佳情况:T(n) = O(n2)
最差情况:T(n) = O(n2)
平均情况:T(n) = O(n2)
稳定性:不稳定。
图解
int [] selectSort(int [] target){
int current;
int temp;
for (int i = 0; i <target.length ; i++) {
current=i;
for (int j = i; j <target.length ; j++) {
if(target[current]>target[j]){
current=j;
}
}
temp=target[current];
target[current]=target[i];
target[i]=temp;
}
return target;
}
参考文章:https://blog.csdn.net/u013249965/article/details/52575324
除非手边没有算法可以参考,一般情况几乎不太使用冒泡排序算法。它过于简单了,以至于可以毫不费力地写出来。然而当数据量很小的时候它会有些应用的价值。
选择排序虽然把交换次数降到了最低,但比较的次数仍然很大。当数据量很小,并且交换数据相对于比较数据更加耗时的情况下,可以应用选择排序。
在大多情况下,假设当数据量比较小或者基本上有序时,插入排序算法是三种简单排序算法中最好的选择。对于更大数据量的排序来说,快速排序通常是最快的方法:之后会有介绍。
参考文章:https://blog.csdn.net/morewindows/article/details/6684558
先从数列中取出一个数作为基准数。
分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
再对左右区间重复第二步,直到各区间只有一个数。
性能:
最佳情况:T(n) = O(nlogn)
最差情况:T(n) = O(n2)
平均情况:T(n) = O(nlogn)
稳定性:不稳定
特点:采用分治法,速度很快,但是是不稳定,不适合对象排序,我这里采用的是第一个数字作为基准数,实际情况尽量不用这样适用。
实现
void quickSort(int[] target,int min,int max){
if(max>min){
int i=min,j=max;
int x=target[min];
//保存基准数后,找到最后的位置,将参照数放到指定位置上就行。
while(i<j){
while (i<j&&target[j]>=x){
j--;
}
target[i]=target[j];
while(i<j&&target[i]<=x){
i++;
}
target[j]=target[i];
}
target[i]=x;
quickSort(target,i+1,max);
quickSort(target,min,i-1);
}
}
参考文章:https://blog.csdn.net/MoreWindows/article/details/6678165#commentBox
思路:和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是O(n log n)的时间复杂度。代价是需要额外的内存空间。
特点: 也是分治法的思想,速度较快,次于快速排序, 缺点是辅存很大,但是是稳定的排序算法,适合对象排序。
性能:
最佳情况:T(n) = O(n)
最差情况:T(n) = O(nlogn)
平均情况:T(n) = O(nlogn)
稳定性:稳定
.
实现
void mergeSort(int [] target){
int [] temp=new int[target.length];
mergeSort(target,0,target.length-1,temp);
}
void mergeSort(int []target, int left,int right,int[] temp){
if(left<right){
int mid=left+(right-left)/2;
mergeSort(target,left,mid,temp);
mergeSort(target,mid+1,right,temp);
mergeSort(target,left,mid,right,temp);
}
}
//合并两个数组的操作
void mergeSort(int []target, int left,int mid,int right,int[] temp){
int k=0;
//两个数组的第一个值
int left_frist=left;
int right_frist=mid+1;
while (left_frist<=mid&&right_frist<=right){
if(target[left_frist]>target[right_frist]){
temp[k++]=target[right_frist++];
}else {
temp[k++]=target[left_frist++];
}
}
//一个合并好,另外一个还没有
while (left_frist<=mid){
temp[k++]=target[left_frist++];
}
while (right_frist<right){
temp[k++]=target[right_frist++];
}
//最后改变target数组,记得是改变left开头的
for (int i = 0; i < k; i++)
target[left+ i] = temp[i];
}
例如:数组1,2,3,4,5,6,7,0有(1,0);(2.0)。。。七个逆序对。
思路,归并排序时,在合并的时候前后两部分都是已经排序好的,我们把则start到mid和mid+1到end中,如果后半部分的其中一个数小于前面的,则小于前面数组start_frist到mid+1的所有数,所以count+=mid+1-start-frist。
public class Solution {
int count=0;
public int InversePairs(int [] array) {
int [] temp=new int[array.length];
getCount(array,0,array.length-1,temp);
return count%1000000007;
}
void getCount(int [] array,int start,int end,int [] temp){
if(start=1000000007)//数值过大求余
{
count%=1000000007;
}
//一旦小于,则代表start_Frist到mid部分都大于end_frist这个值,所以全部要加上去,再加上我的前部分是包括mid,所以要先mid+1再减区start_frist
count+=(mid+1-start_frist);
temp[k++]=array[end_frist++];
}
}
while(start_frist<=mid){
temp[k++]=array[start_frist++];
}
while(end_frist<=end){
temp[k++]=array[end_frist++];
}
for(int i=0;i
参考文章:https://www.cnblogs.com/Java3y/p/8639937.html
定义: 利用堆这种数据结构所设计的一种排序算法,堆积是一个近似于完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于它的父节点。
大概思路:
1)将初始待排序关键字序列构建为大顶堆,此堆为初始的无序区;
2)将堆顶元素与最后一个元素交换,此时得到新的无序区R(1…n-1)和新的有序区R(n),且满足R[1,2,…n-1]<=R[n]
3)由于交换后新的堆顶R(1)可能违反堆的性质,因此需要对当前无序区R(1…n-1)调整为新的堆,
代码实现
//开始建堆,使当前堆的最大值在堆顶
static void heapsort(int [] target ){
int size=target.length-1;
//第一次建堆,使整个数组为一个未排序的堆
fristheap(target);
size--;
for (int i = 0; i =0 ; i--) {
heapify(target,i,target.length-1);
}
}
//对以该节点为根的树进行建堆,使最大的max在最上面,
// 因为我们是从下到上排序子树的,所以不需要考虑子树的子树是否还未排序
static void heapify(int [] target,int curr,int size){
//如果当前节点大于数组长度,则没有子节点,该节点必然是最大值
if(currtarget[max]){
max=left;
}
}
if (righttarget[max]){
max=right;
}
}
if(max!=curr){
//如果不同则交换值
swap(target,max,curr);
//因为max节点值变了,所以以该节点为根的树也同时改变
heapify(target,max,size);
}
}
}
static void swap(int [] target,int i,int j){
int temp=target[i];
target[i]=target[j];
target[j]=temp;
}