/**
* 选择排序:(升序)
* 基本步骤:
*1:从原数组中选出最小的数与第一个数交换位置
*2.从第二个位置开始的数组中找到最小的数与原数组第二个位置上的数交换位置
*3.依次类推
*
*/
public static int[] xuanZe(int []a){
if(a==null||a.length<2)return a;
int n=a.length;
int temp,min;
for(int i=0;i<n-1;i++){//选择的次数
min=i;
for(int j=i+1;j<n;j++){
if(a[min]>a[j])min=j;//找到每一轮中最小那个数的下标
}
//交换位置
temp=a[min];
a[min]=a[i];
a[i]=temp;
}
return a;
}
/**
* 插入排序:(升序)
* @param a 原数组
* @return 排序后的数组
*
* 基本思想:将原数组从第二个数开始,为每一个数找到它合适的位置,并将其插入。
*/
public static int[] chaRu(int []a){
if(a==null||a.length<2)return a;
int n=a.length;
int k;
for(int i=1;i<n;i++){//从第二个数开始
int temp=a[i];
k=i-1;
while(k>=0&&a[k]>temp){
k--;
}
//找到需要插入的位置,即k+1
for(int j=i;j>k+1;j--){
a[j]=a[j-1];
}
//插进去
a[k+1]=temp;
}
return a;
}
/**
* 冒泡排序:(升序)
* @param a 原数组
* @return 排序后的数组
*
* 基本思想:比较第一个和第二个数,如果第一个比第二个大,则交换这两个数的位置。以此类推,比较 第二个数和第三个数....这样一轮下来,最大的数就在最大的下标上。
* 需要n-1轮
*
*/
public static int[] maoPao(int []a){
if(a==null||a.length<2)return a;
int n=a.length;
int temp;
for(int i=1;i<n-1;i++){
for(int j=0;j<n-1;j++){
if(a[j]>a[j+1]){//交换
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
}
return a;
}
/**
* 冒泡排序的升级版本:
* 如果在进行到某一轮比较时,没有发生过交换,即后面的数均是有序的,此时就可中断循环。
* @param a
* @return
*/
public static int[] maoPaoPush(int []a){
if(a==null||a.length<2)return a;
int n=a.length;
int temp;
boolean flag;
for(int i=0;i<n-1;i++){
flag=true;
for(int j=0;j<n-i-1;j++){
if(a[j]>a[j+1]){//交换
flag=false;
temp=a[j];
a[j]=a[j+1];
a[j+1]=temp;
}
}
if(flag)break;
}
return a;
}
/**
* 希尔排序:
* 希尔排序可以说是插入排序的一种;
* 希尔排序其实是为了加快速度简单改进了插入排序,交换不相邻的元素以对数组的局部进行排序
* 间隔h的取法(一种常见的取法,不同的取法会对时间复杂度有不同的影响):
* 1.h=n/2
* 2.h=h/2
* 3.循环步骤2
* 4.h=1
* 注意(理解此句话):对各个分组进行插入的时候并不是先对一个组排序完了再来对另一个组排序,而是轮流对每个组进行排序。
*/
public static int[] shell(int[]a){
if(a==null||a.length<2)return a;
int n=a.length;
for(int k=n/2;k>=1;k/=2){//轮数
for(int i=k;i<n;i++){//从k开始,就相当于插入排序中从第二个数开始,为每一个数找到合适的位置
insert_(a,k,i);
}
}
return a;
}
private static void insert_(int[] a, int k, int i) {
int temp=a[i];
int j;
for( j=i-k;j>=0&&temp<a[j];j-=k){
a[j+k]=a[j];
}
//插入
a[j+k]=temp;
}
/**
* 归并排序:(递归)
* 将一个大的无序数组有序,我们可以把大的数组分成两个,然后对这两个数组分别进行排序,
* 之后在把这两个数组合并成一个有序的数组。由于两个小的数组都是有序的,所以在合并的时候是很快的。
* 通过递归的方式将大的数组一直分割,直到数组的大小为 1,此时只有一个元素,那么该数组就是有序的了,
* 之后再把两个数组大小为1的合并成一个大小为2的,再把两个大小为2的合并成4的 ..... 直到全部小的数组合并起来。
*时间复杂度:
* 把一个n个元素的数组分割成只有一个元素的数组,那么我需要切logn次,
* 每次把两个有序的子数组汇总成一个大的有序数组,所需的时间复杂度为O(n)。
* 所以总的时间复杂度为O(nlogn)
*/
public static void guiBing(int[]a,int left,int right){
int mid;
if(left<right){
mid=left+(right-left)/2;
guiBing(a,left,mid);
guiBing(a,mid+1,right);
merge(a,left,mid,right);
}
}
//合并两个有序数组
public static void merge(int []a,int left,int mid,int right){
int []temp=new int[right-left+1];
int i=left;
int j=mid+1;
int k=0;
while (i<=mid&&j<=right){
if(a[i]<a[j])temp[k++]=a[i++];
else temp[k++]=a[j++];
}
while(i<=mid){
temp[k++]=a[i++];
}
while (j<=right){
temp[k++]=a[j++];
}
for(int p=0;p<temp.length;p++){
a[left++]=temp[p];
}
}
/**
* 归并排序:(非递归)
*/
public static int[] guiBing1(int[]arr){
int n = arr.length;
// 子数组的大小分别为1,2,4,8...
// 刚开始合并的数组大小是1,接着是2,接着4....
for (int i = 1; i < n; i += i) {
//进行数组进行划分
int left = 0;
int mid = left + i - 1;
int right = mid + i;
//进行合并,对数组大小为 i 的数组进行两两合并
while (right < n) {
// 合并函数和递归式的合并函数一样
merge(arr, left, mid, right);
left = right + 1;
mid = left + i - 1;
right = mid + i;
}
// 还有一些被遗漏的数组没合并,千万别忘了
// 因为不可能每个字数组的大小都刚好为 i
if (left < n && mid < n) {
merge(arr, left, mid, n - 1);
}
}
return arr;
}
/**
* 快速排序:
* 我们从数组中选择一个元素,我们把这个元素称之为中轴元素吧,
* 然后把数组中所有小于中轴元素的元素放在其左边,所有大于或等于中轴元素的元素放在其右边,
* 显然,此时中轴元素所处的位置的是有序的。也就是说,我们无需再移动中轴元素的位置。
*
* 从中轴元素那里开始把大的数组切割成两个小的数组(两个数组都不包含中轴元素),
* 接着我们通过递归的方式,让中轴元素左边的数组和右边的数组也重复同样的操作,
* 直到数组的大小为1,此时每个元素都处于有序的位置。
*
*/
public static int[] kuaiSu(int[]a,int left,int right){
if(left<right){
int mid=position(a,left,right);
kuaiSu(a,left,mid-1);
kuaiSu(a,mid+1,right);
}
return a;
}
public static int position(int[]a,int left,int right){
int point=a[left];//主元
int i=left+1;
int j=right;
while(1==1){
while(i<=j&&a[i]<point)i++;
while(i<=j&&a[j]>point)j--;
if(i<j){
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}else break;
}
a[left]=a[j];
a[j]=point;
return j;
}
/**
* 计数排序:
* 适合于最大值和最小值差值不是很大的整数数组
* 基本思想:就是把数组元素作为数组的下标,然后用一个临时数组统计该元素出现的次数,
* 例如 temp[i] = m, 表示元素 i 一共出现了 m 次。
* 最后再把临时数组统计的数据从小到大汇总起来,此时汇总起来是数据是有序的。
*/
public static int[] jiShu(int []a){
if(a==null||a.length<2)return a;
int n=a.length;
//1.获取临时数组的长度
int max=a[0];
int min=a[0];
for(int i=1;i<n;i++){
if(max<a[i])max=a[i];
if(min>a[i])min=a[i];
}
int [] temp=new int[max-min+1];
//2.
for(int i=0;i<n;i++){
temp[a[i]-min]++;
}
//3.
int k=0;
for(int i=0;i<temp.length;i++){
for(int j=temp[i];j>0;j--){//temp 数组中为0的下标不会再添加到数组a中
a[k++]=i+min;
}
}
return a;
}
/**
* 基数排序:
* 基数排序的排序思路是这样的:先以个位数的大小来对数据进行排序,接着以十位数的大小来多数进行排序,接着以百位数的大小......
*/
public static int[] radioSort(int[] arr) {
if(arr == null || arr.length < 2) return arr;
int n = arr.length;
int max = arr[0];
// 找出最大值
for (int i = 1; i < n; i++) {
if(max < arr[i]) max = arr[i];
}
// 计算最大值是几位数
int num = 1;
while (max / 10 > 0) {
num++;
max = max / 10;
}
// 创建10个桶
ArrayList<LinkedList<Integer>> bucketList = new ArrayList<>(10);
//初始化桶
for (int i = 0; i < 10; i++) {
bucketList.add(new LinkedList<>());
}
// 进行每一趟的排序,从个位数开始排
for (int i = 1; i <= num; i++) {
for (int j = 0; j < n; j++) {
// 获取每个数a[j]最后第 i 位
int radio = (arr[j] / (int)Math.pow(10,i-1)) % 10;
//放进对应的桶里
bucketList.get(radio).add(arr[j]);
}
//合并放回原数组
int k = 0;
for (int j = 0; j < 10; j++) {
for (Integer t : bucketList.get(j)) {
arr[k++] = t;
}
//取出来合并了之后把桶清光数据
bucketList.get(j).clear();
}
}
return arr;
}
/**
* 桶排序:
* 对于数组中包含浮点数,或者数组取值范围较大时,不能用计数排序,但可以采用桶排序
* 基本思想:就是把最大值和最小值之间的数进行瓜分,例如分成 10 个【区间】,10个区间对应10个桶,
* 我们把各元素放到对应区间的桶中去,再对每个桶中的数进行排序,可以采用归并排序,也可以采用快速排序之类的。
* 之后每个桶里面的数据就是有序的了,我们在进行合并汇总。
*/
public static double[] bucketSort(double[] array){
//1.得到数列的最大值和最小值,并算出差值d
double max = array[0];
double min = array[0];
for(int i=1; i<array.length; i++) {
if(array[i] > max) {
max = array[i];
}
if(array[i] < min) {
min = array[i];
}
}
double d = max - min;
//2.初始化桶
int bucketNum = array.length;
ArrayList<LinkedList<Double>> bucketList = new ArrayList<LinkedList<Double>>(bucketNum);
for(int i = 0; i < bucketNum; i++){
bucketList.add(new LinkedList<Double>());
}
//3.遍历原始数组,将每个元素放入桶中
for(int i = 0; i < array.length; i++){
int num = (int)((array[i] - min) * (bucketNum-1) / d);
bucketList.get(num).add(array[i]);
}
//4.对每个通内部进行排序
for(int i = 0; i < bucketList.size(); i++){
//JDK底层采用了归并排序或归并的优化版本
Collections.sort(bucketList.get(i));
}
//5.输出全部元素
double[] sortedArray = new double[array.length];
int index = 0;
for(LinkedList<Double> list : bucketList){
for(double element : list){
sortedArray[index] = element;
index++;
}
}
return sortedArray;
}
/**
* 堆排序:
*堆的特点就是堆顶的元素是一个最值,大顶堆的堆顶是最大值,小顶堆则是最小值。
*
* 堆排序就是把堆顶的元素与最后一个元素交换,交换之后破坏了堆的特性,
* 我们再把堆中剩余的元素再次构成一个大顶堆,然后再把堆顶元素与最后第二个元素交换....
* 如此往复下去,等到剩余的元素只有一个的时候,此时的数组就是有序的了。
*
*
*/
public static int[] headSort(int[] arr) {
int n = arr.length;
//1.构建大顶堆
for (int i = (n-1) / 2; i >= 0; i--) {
downAdjust(arr, i, n - 1);
}
//2.进行堆排序
for (int i = n - 1; i >= 1; i--) {
// 把堆顶元素与最后一个元素交换
int temp = arr[i];
arr[i] = arr[0];
arr[0] = temp;
// 把打乱的堆进行调整,恢复堆的特性
downAdjust(arr, 0, i - 1);
}
return arr;
}
//下沉操作
public static void downAdjust(int[] arr, int parent, int n) {
//临时保存要下沉的元素
int temp = arr[parent];
//定位左孩子节点的位置
int child = 2 * parent+1 ;
//开始下沉
while (child <= n) {
// 如果右孩子节点比左孩子大,则定位到右孩子
if(child + 1 <= n && arr[child] < arr[child + 1])
child++;
// 如果孩子节点小于或等于父节点,则下沉结束
if (arr[child] <= temp ) break;
// 父节点进行下沉
arr[parent] = arr[child];
parent = child;
child = 2 * parent+1;
}
arr[parent] = temp;
}
十大排序算法
最新推荐文章于 2021-12-19 11:34:28 发布