七大排序算法

阿狗要好好来整理一下啦,感觉自己数据结构不太熟,比如排序算法的思想大概理解,用代码复现又是另一回事,懂得不同场景下的应用又又又又是另一回事,碰到笔试排序算法的题又双叒叕是另另另一回事。所以说啊,学无止境,学海无涯阿狗得飘啊。还有还长一段时间呢,阿狗加油ヾ(◍°∇°◍)ノ゙

01---算法介绍:

1、冒泡排序

思想:

通过与相邻元素的比较和交换来把最大的值交换到最后面。这个过程类似于水泡一样,重的往下沉(轻的往上浮),因此得名。

实现:

定义两个for循环,外层循环控制排序趟数,内层循环控制每一趟排序多少次

这个图实在是太可爱啦,如图所以,每次相邻两两比较将最大的往后放,再来放次大的,直到遍历结束。

看我捞代码还带测试:

public class BubbleSort {
	public static void bubblesort(int[] arr){
		for(int i=0;i<arr.length-1;i++){            //外层循环控制排序趟数
			for(int j=0;j<arr.length-i-1;j++){      ////内层循环控制每一趟排序多少次
				if(arr[j]>arr[j+1]){
					int t=arr[j];
					arr[j]=arr[j+1];
					arr[j+1]=t;
				}
			}
		}
	}
	  public static void main(String[] args){
		  int[] arr={2,5,8,3,5,6,9,1};
		  BubbleSort.bubblesort(arr);
		  for(int i=0;i<arr.length;i++){
			  System.out.print(arr[i]+",");
		  }
	  }
}

2、选择排序

思想:

首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找次小元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 

实现:

定义两个for循环,外层循环控制比较次数,内层循环遍历未排序元素。

与冒泡排序相比,冒泡是通过相邻元素的比较和交换,而选择排序是通过对整体遍历一遍再进行的挑选,是在确定了最小数的前提下才进行交换,大大减少了交换的次数。

public class SelectSort {
	public static void selectsort(int[] arr){
		for(int i=0;i<arr.length-1;i++){
			for(int j=i+1;j<arr.length;j++){
				if(arr[i]>arr[j]){
					int t=arr[i];
					arr[i]=arr[j];
					arr[j]=t;
				}
			}
		}
	}

3、插入

思想:

通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描找到相应位置并插入。这个其实和我们打扑克牌的时候一毛一样,你想想你是怎么排牌的,哈哈哈哈哈~~

实现:定义两个for循环,外层循环遍历元素,内层循环比较未排序元素应该向已排序元素插入的位置。

public static void insertsort(int[] arr){
		for(int i=0;i<arr.length;i++){
			for(int j=i;j>0;j--){
				if(arr[j]<arr[j-1]){
					int t=arr[j];
					arr[j]=arr[j-1];
					arr[j-1]=t;
				}
			}
		}
	}

4、归并

思想:

该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。即:先递归划分子问题,然后合并结果。

实现:

1)把长度为n的输入序列分成两个长度为n/2的子序列;

2)对这两个子序列分别采用归并排序;

3)将两个排序好的子序列合并成一个最终的排序序列。

分治法通常有3步:Divide(分解子问题的步骤) 、 Conquer(递归解决子问题的步骤)、 Combine(子问题解求出来后合并成原问题解的步骤)。

伪代码:

 注意合并的思想,其实就是两段子序列分别比较它们的首元素,大的就往list里面放。加一个循环判断,如果左边的放完了就直接把右边的整个拷贝,反之亦然。

注意一定要判断如果子序列长度为1,返回其本身,不然会StackOverflowError!!!

public class MergeSort{
	public static void main(String[] args){
		int [] arr={3,56,2,1,8,4,6};
		int [] arr2=sort(arr);
		for(int i:arr2){
			System.out.print(i+",");
		}
	}
	public static int[] sort(int[] arr){
		if(arr.length <=1){
			return arr;
		}
		int len=arr.length/2;
		int[] left=sort(Arrays.copyOfRange(arr, 0,len));
		int[] right=sort(Arrays.copyOfRange(arr, len, arr.length));
		return merge(left,right);
	}
	public static int[] merge(int[] left,int[] right){
		int l=0;
		int r=0;
		List<Integer> list=new ArrayList();
		while(l<left.length&&r<right.length){
			if(left[l]<right[r]){
				list.add(left[l]);
				l++;
			}
			else{
				list.add(right[r]);
				r++;
			}
		}
		if(l>=left.length){
			for(int i=r;i<right.length;i++){
				list.add(right[i]);
			}
		}
		if(r>=right.length){
			for(int i=l;i<left.length;i++){
				list.add(left[i]);
			}
		}
		int[] result=new int[list.size()];
		for(int i=0;i<list.size();i++){
		result[i]=list.get(i);
	}
		return result;
	}
	
}

5、快排

思想:

在待排序的n个记录中任取一个记录(通常取第一个记录),把该记录放入适当位置后,数据序列被此记录划分成两部分。所有关键字比该记录关键字小的记录放置在前一部分,所有比它大的记录放置在后一部分,并把该记录排在这两部分的中间(称为该记录归位),这个过程称作一趟快速排序。

实现:

选数列中第一个数作为 “基准”(pivot),定义两个变量---i、j。j从后面开始遍历,若小于基准则与其交换位置,同时 从前开始遍历,若大于基准再交换位置,再从后开始遍历。这样一趟下来所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

image

public class QuickSort{
	public static void partion(int[] arr,int start,int end){
		if(start>end){
			return;
		}
		int i=start;
		int j=end;
		int index=arr[i];
		while(i<j&arr[j]>=index){
			j--;
		}
		if(i<j&&arr[j]<index){
			arr[i++]=arr[j];
		}
		while(i<j&&arr[i]<=index){
			i++;
		}
		if(i<j&&arr[i]>index){
			arr[j--]=arr[i];
		}
		arr[i]=index;
		partion(arr, start, i-1);
		partion(arr, i+1, end);
	}
	public static void quicksort(int arr[]){
		partion(arr,0,arr.length-1);	
	}
	public static void main(String[] args){
		int[] arr={3,5,34,14,52,13};
		quicksort(arr);
		System.out.println(Arrays.toString(arr));
	}
}

6、希尔

思想:

递减增量排序算法实际上是一种分组插入方法。

实现:

先取定一个小于n的整数d1作为第一个增量,把表的全部记录分成d1个组,所有距离为d1的倍数的记录放在同一个组中,在各组内进行直接插入排序;然后,取第二个增量d2(<d1),重复上述的分组和排序,直至所取的增量dt=1(dt<dt-1<…<d2<d1),即所有记录放在同一组中进行直接插入排序为止。    

三重循环,一重用来控制每次步长,直到变为1。一重用来控制比较的元素,一重控制相隔n个步长要比较的元素。

同时注意在第三重比较的时候,为了防止数组越界我定义  j=i-h  比较 arr [ j ] 和 arr [ j+h ]的值。

public static void shell(int[] arr){
		int l=arr.length/2;
		for(int h=l;h>0;h=h/2){
			for(int i=h;i<arr.length;i++){
				for(int j=i-h;j>=0;j=j-h){
					if(arr[j]>arr[j+h]){
						int m=arr[j];
						arr[j]=arr[j+h];
						arr[j+h]=m;
					}
					else break;
				}
			}
		}
	}

7、堆排序

思想:

堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

注意:如果想升序排序就使用大顶堆,反之使用小顶堆。原因是堆顶元素需要交换到序列尾部。

实现:

1)创建一个堆H[0..n-1]

2)把堆首(最大值)和堆尾互换

3)把堆的尺寸缩小1,并调用shift_down(0),目的是把新的数组顶端数据调整到相应位置

4) 重复步骤2,直到堆的尺寸为1

package paixu;

public class HeapSort{
	//堆排序
  public static void adjustMinHeap(int a[],int pos,int len){
	  int temp;
	  int child;
	  for(temp=a[pos];2*pos+1<=len;pos=child){
		  child=2*pos+1;
		  if(child<len&&a[child]>a[child+1]) 
			  child++;
		  if(a[child]<temp) 
			  a[pos]=a[child];
		  else break;
	  }
	  a[pos]=temp;
  }
  public static void myMinHeapSort(int array[]){
	  int i;
	  int len=array.length;
	  for(i=len/2-1;i>=0;i--)
		  adjustMinHeap(array,i,len-1);
	  for(i=len-1;i>=0;i--){
		  int tmp=array[0];
		  array[0]=array[i];
		  array[i]=tmp;
		  adjustMinHeap(array,0,i-1);
	  }
  }
  public static void main(String[] args){
	  int a[]={3,9,8,4,2,1};
	  myMinHeapSort(a);
	  for(int i=0;i<a.length;i++){
      	System.out.print(a[i]+",");
	  }
  }
}

7、基数排序

思想

它是一种非比较排序。基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。

02---算法复杂度

 相关概念

稳定:两个相同的值a=b,如果a原本在b前面,排序之后a仍然在b的前面。

不稳定:两个相同的值a=b,如果a原本在b的前面,排序之后 a 可能会出现在 b 的后面。

时间复杂度对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。

空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。 

03---场景应用

  • 数据规模很小(插入、简单选择、冒泡)

    • 数据有序时,可选直接插入排序;
    • 数据无序时,对稳定性不作要求宜用简单选择排序,对稳定性有要求宜用插入或冒泡
  • 数据规模一般(快速排序、归并排序)

    • 完全可以用内存空间,序列杂乱无序,对稳定性没有要求,快速排序,此时要付出log(N)的额外空间。
    • 序列本身可能有序,对稳定性有要求,空间允许下,宜用归并排序
  • 数据规模很大(归并、桶)

    • 对稳定性有求,则可考虑归并排序。
    • 对稳定性没要求,宜用堆排序
  • 待排序列初始基本有序(正序),宜用直接插入,冒泡

注:快速排序是目前基于比较的内部排序中最好的方法, 其次是归并和希尔,堆排序在数据量很大时效果明显。当数据是随机分布时快速排序的平均时间最短。

猜你喜欢

转载自blog.csdn.net/zhouboke/article/details/82713964