1.插入排序:
(1)直接插入排序:
思路:
1)将i个位置的数据插入前i-1个已经排好序的数据中,即找到恰当的位置,找的过程中可以顺便往后移数据。
2)在这里a[0]是哨兵,存的是当前要插入的数据a[i],作用是可以在防止出边界的情况下减少比较的次数,即减少了一个if判断边界的语句。
package com.company;
public class Main {
public static void main(String[] args) {
//直接插入排序
int []a={10,-10,9,-2,4,10,7,9,-11};
DirInsertSet(a);
show(a);
}
static void DirInsertSet(int[] a){
for(int i=2;i<a.length;i++){
a[0]=a[i];
int j;
for(j=i-1;a[j]>a[0];j--){
a[j+1]=a[j];
}
a[j+1]=a[0];
}
}
static void show(int[] a){
for(int i=0;i<a.length;i++){
System.out.print(a[i]+" ");
}
System.out.println();
}
}
性能分析:
1.时间复杂度:O(n.^2)
2.空间复杂度:O(1)
3.稳定性:好 (因为每次比较是从后往前,必须比前面的小才会移动,相等不会变动
(2)折半插入排序:
先通过折半查找找到要插入的位置,再统一移动。
package com.company;
public class Main {
public static void main(String[] args) {
int []a={0,10,-10,9,-2,4,10,7,9,-11};
//折半插入排序:即再找位置时利用前i-1个数据已排好序,可以利用折半查找的方法找位置。
binaryInsertSort(a);
show(a);
}
public static void binaryInsertSort(int[] a){
for(int i=2;i<a.length;i++){
int high=i-1;
int low=1;
a[0]=a[i];
while(high>=low){
int mid=(high+low)/2;
if(a[mid]>a[0]){
high=mid-1;
}
else{
low=mid+1;
}
}
for(int j=i-1;j>=high+1;j--) {
a[j + 1] = a[j];
}
a[high+1]=a[0];
}
}
性能分析:
1.时间复杂度:查找位置阶段用折半查找将时间复杂度降低至log2(n)但是移动元素的事件不变,故整体时间复杂度为:nlog2(n)
2.空间复杂度:O(1)
3.稳定性:好*
(3)希尔排序:
又称为是缩小增量排序,
package com.company;
public class Main {
public static void main(String[] args) {
int []a={0,10,-10,9,-2,4,10,7,9,-11};
int []dk={4,2,1};
ShellSort(a,dk);
show(a);
}
static void ShellSort(int[] a,int[] DK){
for(int dk:DK){
for(int i=dk+1;i<a.length;i++){
a[0]=a[i];
int j;
for(j=i-dk;j>0&&a[j]>a[0];j-=dk){
a[j+dk]=a[j];
}
a[j+dk]=a[0];
}
}
}
性能分析:
1.时间复杂度:O(n.^1.3)~ O(n.^2)之间
2.空间复杂度:O(1)
3.稳定性:不稳定,因为可能存在相同的数值的数据杯被分在不同组的情况,会产生不稳定的现象
2.交换排序:
(1)冒泡排序:
普通冒泡排序:
排序n-1次,每次比较相邻项,符合即移动,每轮可以确定一个元素的最终位置。
public static void BubbleSort(int[] a){
//主要思路是对换位置的值
for(int i=1;i<a.length;i++){
for(int j=1;j<a.length-i;j++){
if(a[j]>a[j+1]){
int temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
}
改进版算法:如果某一轮排序没有移动说明已经排好了。
用flag标记判断本次有没有元素移动
public static void BubbleSort(int[] a){
//主要思路是对换位置的值
boolean flag=true;
for(int i=1;i<a.length;i++){
flag=false;
for(int j=1;j<a.length-i;j++){
if(a[j]>a[j+1]){
flag=true;
int temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
if(flag=false){
break;
}
}
}
性能分析:
1.时间复杂度:平均=最坏=O(n.^2)
2.空间复杂度:O(1)
3.稳定性:因为每次都是相邻的比较,然后符合条件移动,故是稳定的
(2)快速排序:
快排最大难点就是在于边界情况的认定!!!但是只要认清自己每次调用函数pivot(a,low,high)都会确定一个中枢的最终位置,而这个位置是本次最终low == high的位置!!!所以多有的判断条件里只能是low<high,而值得大小问题,我们只需要最终中枢的位置那个值不小于它左边的,不大于它右边的即可。
public static void main(String[] args) {
int []a={0,10,-10,9,-2,4,10,7,9,-11};
quickSort(a,1,a.length-1);
show(a);
}
public static int pivot(int[] a,int low,int high){
int pivotal=a[low];
while(low<high){
while(low<high&&a[high]>=pivotal){
high--;
}
a[low]=a[high];
while(low<high&&a[low]<=pivotal){
low++;
}
a[high]=a[low];
}
a[high]=pivotal;
return high;
}
public static void quickSort(int[] a,int low,int high){
if(high>low){
int pivotal=pivot(a,low,high);
quickSort(a,low,pivotal-1);
quickSort(a,pivotal+1,high);
}
}
性能分析:
1.时间复杂度:O(n*log(n)),是内部排序算法中平均性能最优的
2.空间复杂度:O(log(n))~O(n)
3.稳定性:不稳定,假如右端有两个值相同且均小于中枢,那么最右边的会被先移到相对来说更左边的位置。
快排如果是有序的情况下,就会递归n次,但要是很无序的情况下或许只需要log2(n)次。这里说的递归,是真正进入if中的才叫做递归。
3.选择排序:
(1)简单选择排序:
注意边界值即可。
public static void simpleSelectSort(int[]a){
for(int i=1;i<a.length;i++){
int k=0;
for(int j=0;j<=a.length-i;j++){
if(a[j]>a[k]){
k=j;
}
}
int temp=a[k];
a[k]=a[a.length-i];
a[a.length-i]=temp;
}
}
性能分析:
1.时间复杂度:O(n.^2)
2.空间复杂度:O(1)
3.稳定性:假设从小到大排,每次找当前最大的排到后面去。因为值从前往后找最大的,放到后面,那么值相同大小的前面的那个就会被先放到靠后的位置,不稳定。
(2)堆排序:
public static void main(String[] args) {
int []a={0,10,-10,9,-2,4,10,7,9,-11};
HeadSort(a);
System.out.println();
}
public static void HeadAdjust(int[] a,int k,int n){ //调整以k为根的子树(大根堆)
a[0]=a[k];
for(int i=k*2;i<=n;i=i*2){
if(a[i]<a[i+1])i++;
if(a[0]>a[i]){
break;
}
else{
a[k]=a[i];
k=i;
}
}
a[k]=a[0];
}
public static void BuildMaxHead(int[] a,int low,int high){
for(int i=high/2;i>=low;i--){
HeadAdjust(a,i,high);
}
}
public static void HeadSort(int[] a){
BuildMaxHead(a,1,a.length-1);
for(int i=1;i<=a.length-1;i++){
System.out.print(a[1]+" ");
a[1]=a[a.length-i];
if(i<a.length-1){
HeadAdjust(a,1,a.length-i-1);
}
}
}
性能分析:
1.时间复杂度:排序要输出n次,每次要调整差不多是当前树的高度h,即n*log2(n)
2.空间复杂度:O(1)
3.稳定性:不稳定,因为排序的过程涉及到最后一个元素与当前根元素交换,可能原本后面的被交换到前面了。
4.归并排序(二路归并):
分治思想:
先分成左右两个子序列,子序列分别排序,再合并,子序列也按照这个思路一直分下去,然后在合并,用递归可以很好地解决。在这里合并用的是最简单的思想,就是借用了之前两个有序数组合并的想法。在这里必须要额外开辟空间放置要合并的数据。
public static void Merge(int[]a,int low,int mid,int high){
int[] b=new int[a.length];
for(int i=0;i<a.length;i++){
b[i]=a[i];
}
int k=low;
int i;
int j;
for(i=low,j=mid+1;i<=mid&&j<=high;){
if(b[i]<=b[j]){
a[k++]=b[i++];
}
else{
a[k++]=b[j++];
}
}
while(i<=mid){
a[k++]=b[i++];
}
while(j<b.length){
a[k++]=b[j++];
}
}
public static void MergeSort(int[] a,int low,int high){
if(low<high){
int mid=(low+high)/2;
MergeSort(a,low,mid);
MergeSort(a,mid+1,high);
Merge(a,low,mid,high);
}
}
性能分析:
1.时间复杂度:O(nlog2(n))
2.空间复杂度:O(n)
3.稳定性:不会更改相同关键字的相对顺序,故事稳定的*
5.基数排序
有多个不同优先级的关键字,排序是稳定的,假设有d个关键字,n个数据,那么排序需要进行d次分配和回收,每次分配需要O(n),回收需要(基数为r)O( r ),则一次分配+回收一共需要O(n+r),d次即O(d(n+r))