目录
一、排序中的概念
二、常见排序算法--升序
1、插入排序
(1)直接插入排序
(2)希尔排序
2、选择排序
(1)单指针选择排序
(2)双指针选择排序
(3)堆排序
3、交换排序
(1)冒泡排序
(2)双指针快速排序
(3)挖洞快速排序
(4)前后指针快速排序
(5)优化版快速排序
(6)非递归快速排序
4、归并排序
(1)归并排序
(2)归并排序非递归
5、计数排序
6、桶排序
7、基数排序
一、排序中的概念
1、稳定性:在待排序的记录中,存在多个相同的关键字记录,若在排序前和排序后这些相同关键字的相对次序保持不变,则这种排序算法是稳定的,否则称为不稳定的。
二、常见排序算法--升序
1、插入排序
(1)直接插入排序
①思想:将要插入的数据和前面比较,要是前面的小,结束比较,要是前面的大,元素后移,再次比较,直到遇到较小的,元素插入。
②代码实现:
public static void insertSort(int[] a){
int size=a.length;
for(int i=1;i<size;i++){
int j=i-1;
int temp=a[i]; //后面会被覆盖
for(;j>=0;j--){
if(a[j]>temp){
a[j+1]=a[j];
}else {
break;
}
}
a[j+1]=temp;
}
}
③特性:
元素越接近有序,时间效率越高;
时间复杂度:O(n^2);空间复杂度:O(1);稳定性:稳定。
(2)希尔排序
①思想:对元素进行分组,如有10个元素,先分成5组(元素间隔为5),对着5组进行插入排序;再分成2组(元素间隔为2),对这2组进行插入排序;最后分成1组(元素间隔为1),对着1组进行插入排序,此时插入排序时元素已经接近有序了。
②代码实现:
public static void shellSort(int[] a){
int size=a.length;
while(size/2>=1){
size/=2;
shell(a,size);
}
}
public static void shell(int[] a,int group){
int size=a.length;
for(int i=1;i<size;i++){
int temp=a[i];
int j=i-group;
for(;j>=0;j-=group){
if(a[j]>temp){
a[j+group]=a[j];
}else{
break;
}
}
a[j+group]=temp;
}
}
③特性:
是直接插入排序的优化。
时间复杂度:O(n^1.25)到O(1.6n^1.25);空间复杂度:O(1);稳定性:不稳定。
2、选择排序
(1)单指针选择排序
①思想:
从第一个数开始,每次找到待排序数的最小值,与第一个数交换位置;然后开始第二个数,每次找到待排序数的最小值,与第二个数交换位置......直到最后一个数结束。
②代码实现:
public static void selectSort(int[] a){
int size=a.length;
for(int i=0;i<size-1;i++){
int min=i;
for(int j=i+1;j<size;j++){
if(a[j]<a[min]){
min=j;
}
}
if(i!=min){
swap(a,i,min);
}
}
}
public static void swap(int[] a,int m,int n){
int temp=a[m];
a[m]=a[n];
a[n]=temp;
}
③特性:
时间复杂度:O(n^2);空间复杂度:O(1);稳定性:不稳定。
(2)双指针选择排序
①思想:
一个指针指向要排序数的最小值,一个指针指向要排序数的最大值,最小值放左边,最大值放右边,往中间遍历,但需注意:最大值在要排序数第一个的情况。
②代码实现:
public static void selectSort2(int[] a){
int size=a.length;
int left=0;
int right=size-1;
while(left<right){
int max=right;
int min=left;
for(int i=left;i<=right;i++){
if(a[i]>a[max]){
max=i;
}else if(a[i]<a[min]){
min=i;
}
}
if(max==left){
max=min;
}
if(min!=left){
swap(a,min,left);
}
if(max!=right){
swap(a,max,right);
}
left++;
right--;
}
}
public static void swap(int[] a,int m,int n){
int temp=a[m];
a[m]=a[n];
a[n]=temp;
}
(3)堆排序
①思想:
若是升序的话,建立大根堆,每次将堆顶元素与最后一个元素交换,前n-1个元素,以第一个父结点进行向下调整......直到交换结束。
若是降序的话,建立小根堆,每次将堆顶元素与最后一个元素交换,前n-1个元素,以第一个父结点进行向下调整......直到交换结束。
②代码实现:
public static void buildPile(int[] a){
int size=a.length;
int parent=(size-2)/2;
for(int i=parent;i>=0;i--){
buildChildPile(a,i,size);
}
}
public static void buildChildPile(int[] a,int parent,int size){
int child=2*parent+1;
while(child<size){
if(child+1<size&&a[child]<a[child+1]){
child=child+1;
}
if(a[child]>a[parent]){
swap(a,child,parent);
}
parent=child;
child=2*parent+1;
}
}
public static void pileSort(int[] a){
int size=a.length-1;
while(size!=0){
swap(a,0,size);
buildChildPile(a,0,size-1);
size--;
}
}
③特性:
时间复杂度:O(n*logn);空间复杂度:O(1);稳定性:不稳定。
3、交换排序
(1)冒泡排序
①思想:
将大的往左边放,每次遇到一个数,比后面数大,就往后交换。
②代码实现:
public static void bubbleSort(int[] a){
int size=a.length;
boolean flag=true;
for(int i=0;i<size-1;i++){
for(int j=1;j<size-i;j++){
if(a[j-1]>a[j]){
swap(a,j-1,j);
flag=false;
}
}
if(flag){
break;
}
}
}
③特性:
时间复杂度:O(n^2);空间复制度:O(1);稳定性:稳定。
(2)双指针快速排序
①思想:找到一个基准值,默认为left指针,right往左移,left往右移,如果right小于基准,left大于基准,两者交换,循环进行到left<right不满足,基准值要和最后一个left交换,接着划分区间进行判断,直到区间里只有一个元素结束。
②代码实现:
public static void quickSort(int[] a){
quickChild(a,0,a.length-1);
}
public static void quickChild(int[] a,int left,int right){
if(left>=right){ //一个元素
return;
}
int index=index(a,left,right); //进行交换,并找到基准值的位置
quickChild(a,left,index-1); //左区间接着判断
quickChild(a,index+1,right); //右区间接着判断
}
public static int index(int[] a,int left,int right){
int temp=a[left];
int k=left;
while (left<right){
while(left<right&&a[right]>=temp){
right--;
}
while(left<right&&a[left]<=temp){
left++;
}
swap(a,left,right);
}
swap(a,k,left);
return left; //left后走的 right一定是走到了小于等于temp的地方
}
③特性:
时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。
(3)挖洞快速排序
①思想:找到一个基准值,默认为left指针,right往左移,left往右移,如果right小于基准,right和left交换;如果left大于基准,left和right交换,循环进行到left<right不满足,基准值放在最后一个left,接着划分区间进行判断,直到区间里只有一个元素结束。
②代码实现:
public static void quickSort2(int[] a){
quickChild2(a,0,a.length-1);
}
public static void quickChild2(int[] a,int left,int right){
if(left>=right){
return;
}
int index=index2(a,left,right);
quickChild2(a,left,index-1);
quickChild2(a,index+1,right);
}
public static int index2(int[] a,int left,int right){
int temp=a[left];
while(left<right){
while(left<right&&a[right]>=temp){
right--;
}
swap(a,left,right);
while(left<right&&a[left]<=temp){
left++;
}
swap(a,left,right);
}
a[left]=temp;
return left;
}
③特性:
时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。
(4)前后指针快速排序
①思想:前指针pre为left,后指针cur为前指针+1,当cur<=right时,若指针left的值大于指针cur的值,且++pre指针的值不等于cur,交换cur与pre的值,否则不交换;cur++;循环结束后交换left与pre的值。 ----琢磨
②代码实现:
public static void quickSort3(int[] a){
quickChild3(a,0,a.length-1);
}
public static void quickChild3(int[] a,int left,int right){
if(left>=right){
return;
}
int index=index3(a,left,right);
quickChild3(a,left,index-1);
quickChild3(a,index+1,right);
}
public static int index3(int[] a,int left,int right){
int pre=left;
int cur=pre+1;
while(cur<=right){
if(a[left]>a[cur]&&a[++pre]!=a[cur]){
swap(a,pre,cur);
}
cur++;
}
swap(a,left,pre);
return pre;
}
③特性:
时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。
(5)优化版快速排序
①思想:长度如果小于等于5采用直接插入排序;基准值不再默认为left的值,选择left、right、mid三者的中间值。
②代码实现:
public static void quickSort4(int[] a){
int size=a.length;
if(size<=5){
insertSort(a);
}else{
quickChild4(a,0,size-1);
}
}
public static void quickChild4(int[] a,int left,int right){
if(left>=right){
return;
}
int mid=findMid(a,left,right);
swap(a,mid,left); //改变基准值
int index=index4(a,left,right);
quickChild4(a,left,index-1);
quickChild4(a,index+1,right);
}
public static int index4(int[] a,int left,int right){
int temp=a[left];
while(left<right){
while(left<right&&a[right]>=temp){
right--;
}
swap(a,left,right);
while(left<right&&a[left]<=temp){
left++;
}
swap(a,left,right);
}
a[left]=temp;
return left;
}
public static int findMid(int[] a,int left,int right){
int mid=(left+right)/2;
if(a[left]>a[right]){
if(a[mid]<a[right]){
return right;
}else if(a[mid]>a[left]){
return left;
}else {
return mid;
}
}else{
if(a[mid]<a[left]){
return left;
}else if(a[mid]>a[right]){
return right;
}else {
return mid;
}
}
}
③特性:
时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。
(6)非递归快速排序
①思想:和双指针思想一样,只是此时将left和right的值放进了队列中,只要队列不为空,再拿出来进行交换并获取下标。
②代码实现:
public static void quickSort5(int[] a){
int right=a.length-1;
int left=0;
if(right-left>1){
quickChild5(a,left,right);
}
}
public static void quickChild5(int[] a,int left,int right){
Queue<Integer> queue=new LinkedList<>();
queue.offer(left);
queue.offer(right);
while (!queue.isEmpty()){
int start=queue.poll();
int end=queue.poll();
int index=index(a,start,end);
if(index-1>start){
queue.offer(start);
queue.offer(index-1);
}
if(right>index+1){
queue.offer(index+1);
queue.offer(right);
}
}
}
③特性:
时间复杂度:O(n*logn);空间复杂度:O(logn);稳定性:不稳定。
4、归并排序
(1)归并排序
①思想:不断划分区间,直到区间内只有一个元素时结束,然后在区间内进行排序(类似于双指针中的滑动窗口,指针指的数谁小,数往前放,指针后移)合并。
②代码实现:
public static void mergeSort(int[] a){
mergeChild(a,0,a.length-1);
}
public static void mergeChild(int[] a,int left,int right){
if(left>=right){
return;
}
int mid=(left+right)/2;
mergeChild(a,left,mid); //左边拆分
mergeChild(a,mid+1,right); //右边拆分
merge(a,left,mid,right); //合并
}
public static void merge(int[] a,int left,int mid,int right){
int s1=left;
int e1=mid;
int s2=mid+1;
int e2=right;
int[] temp=new int[right-left+1];
int k=0;
while(s1<=e1&&s2<=e2){
if(a[s1]<a[s2]){
temp[k++]=a[s1++];
}else {
temp[k++]=a[s2++];
}
}
if(s1>e1){
while(s2<=e2){
temp[k++]=a[s2++];
}
}else {
while(s1<=e1){
temp[k++]=a[s1++];
}
}
for(int i=0;i<temp.length;i++){
a[i+left]=temp[i]; //a是从left开始的 temp是从0开始的
}
}
③特性:
时间复杂度:O(n*logn);空间复杂度:O(n);稳定性:稳定。
(2)归并排序非递归
①思想:每次直接求出left,right,mid,然后调用方法进行排序。left根据分组元素求出,mid=left+group-1,right=mid+group。
②代码实现:
public static void mergeSort2(int[] a){
int group=1; //区间内元素个数:group+1
int size=a.length;
while(group<size){
for(int i=0;i<size;i+=group*2) { //left值
int left=i;
int mid=left+group-1;
if(mid>size-1){ //区间里元素个数不够凑成一个group 可能出现mid越界
mid=size-1;
}
int right=mid+group;
if(right>size-1){
right=size-1;
}
merge(a,left,mid,right); //进行合并
}
group*=2;
}
}
③特性:
时间复杂度:O(n*logn);空间复杂度:O(n);稳定性:稳定。
5、计数排序
①思想:统计每个数据出现的次数,并对应下标按照顺序存储,按照顺序再放入排序数组。
②代码实现:
public static void countSort(int[] a){
int size=a.length;
int min=a[0];
int max=a[0];
for(int i=0;i<size;i++){
if(a[i]>max){
max=a[i];
}else if(a[i]<min){
min=a[i];
}
}
int[] count=new int[max-min+1];
for(int i=0;i<size;i++){
count[a[i]-min]++;
}
int j=0;
for(int i=0;i<count.length;i++){
while (count[i]>0){
a[j++]=i+min;
count[i]--;
}
}
}
③特性:
时间复杂度:O(n);空间复杂度:O(范围);稳定性:稳定
6、桶排序
①思想:
7、基数排序
①思想:
个位数排序:
十位数排序:
百位数排序:
此时就已经有序了。