常用排序算法详细讲解(Java篇)

常见排序列表
常见排序列表

选择排序

最简单但是最没用的排序算法,也有优化空间
最简单:算法思想简单
最没用:时间复杂度高O(n²)、不稳定

基本思想第i趟简单选择排序是指通过n-i次关键字的比较,从n-i+1个记录中选出关键字最小的记录,并与第i个记录进行交换。共需进行i-1趟比较,直到所有记录排序完成为止。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Java代码实现:

public class SelectionSort {

	public static void main(String[] args) {
		int[] arr = {5,3,6,8,4,7,2,1,9};
				
		for(int i = 0; i<arr.length-1;i++) {
			int minPos = i;				
			for(int j =i+1;j<arr.length;j++ ) {
				/*
				 * if(arr[j] < arr[minPos]) minPos = j;
				 */
				//可优化为
				minPos = arr[j] < arr[minPos] ? j : minPos;
			}			
			swap(arr,i,minPos);
			System.out.println();
			System.out.println("经过第"+(i+1)+"次循环之后,数组的内容:");			
			print(arr);
		}		
	}
	
	//交换
	static void swap(int[] arr,int i,int j) {
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
	
	//打印数组
	static void print(int[] arr) {
		for(int i = 0; i<arr.length;i++) {
			System.out.print(arr[i]+" ");
		}
	}

}

冒泡排序

冒泡排序算法的原理如下:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

Java代码实现:

public class BubbleSort {

	public static void main(String[] args) {
		int[] a = {9,8,5,3,1,2,7,6,4};
		sort(a);
		print(a);

	}
	
	static void sort(int[] a) {
		for(int i=a.length-1;i>0;i--)
			findMax(a,i);
	}
	
	static void findMax(int[] a, int n) {
		for(int j = 0; j < n; j++) {
			if(a[j] > a[j+1]) swap(a,j,j+1);
		}
	}
	
	static void swap(int[] a,int i,int j) {
		int temp = a[i];
		a[i] = a[j];
		a[j] = temp;
	}
	
	static void print(int[] a) {
		for(int i = 0; i<a.length;i++) {
			System.out.print(a[i]+" ");
		}
	}


}

插入排序

对于基本有序的数组最好用
稳定
基本思想在一个已排好序的记录子集的基础上,每一步将下一个待排序的记录有序地插入到已排好序的记录子集中,直到将所有待排记录全部插入为止。
即边插入边排序,保证子序列中随时都是排好序的

Java代码实现:

public class InsertionSort {

	public static void main(String[] args) {
		int[] a = {8,9,5,3,1,2,7,6,4};
		sort(a);
		print(a);

	}
	
	static void sort(int[] a) {
		for(int i=1;i<a.length;i++) {
			for(int j = i;j>0 && a[j] < a[j-1] ;j--) {
				//if(a[j] < a[j-1])
					swap(a,j,j-1);
			}
		}
			
	}
	
	static void swap(int[] a,int i,int j) {
		int temp = a[i];
		a[i] = a[j];
		a[j] = temp;
	}
	
	static void print(int[] a) {
		for(int i = 0; i<a.length;i++) {
			System.out.print(a[i]+" ");
		}
	}


}

分析:
   直接插入排序算法简便,比较适用于待排序记录数目较少且基本有序的情况。当待排记录数目较大时,直接插入排序的性能就不好,为此我们可以对直接插入排序做进一步的改进。在直接插入排序法的基础上,从减少“比较关键字”和“移动记录”两种操作的次数着手来进行改进。

简单排序算法总结:

冒泡:
  基本不用,太慢
选择:
  基本不用,不稳
插入:
  样本小且基本有序的时候效率比较高

希尔排序

改进的插入排序

希尔排序的基本思想先将待排序记录序列分割成若干个“较稀疏的”子序列,分别进行直接插入排序。经过上述粗略调整,整个序列中的记录已经基本有序,最后再对全部记录进行一次直接插入排序
间隔序列
比较相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比较就可能消除多个元素交换。

算法先将要排序的一组数按某个增量d分成若干组,每组中记录的下标相差d.对每组中全部元素进行排序,然后再用一个较小的增量对它进行分组,在每组中再进行排序。当增量减到1时,整个要排序的数被分成一组,排序完成。
一般的初次取序列的一半为增量,以后每次减半,直到增量为1。

希尔排序间隔的选取会直接影响算法的效率
后来发现这种 /2 的间隔序列并不是最高的,还有别的间隔序列比这种还要高
Knuth序列
 h=1
 h=3*h+1
Java代码实现:

public class ShellSort {

	public static void main(String[] args) {
		int[] arr = {5,14,3,10,6,13,8,12,4,15,7,11,2,1,9};
		
		sort(arr);
		print(arr);

	}
	
	static void sort(int[] arr) {
		
		//Knuth序列确定间隔序列
		int h=1;
		while(h<=arr.length/3) {
			h=h*3+1;
		}
		
		for(int gap =h;gap>0;gap=(gap-1)/3) {
			
			for(int i=gap;i<arr.length;i++) {
				for(int j = i;j>gap-1 ;j-=gap) {
					if(arr[j] < arr[j-gap])
						swap(arr,j,j-gap);
					}
				}
				
			}
		}
					
	static void swap(int[] arr,int i,int j) {
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}

	static void print(int[] arr) {
		for(int i = 0; i<arr.length;i++) {
			System.out.print(arr[i]+" ");
		}
	}

}

归并排序

基本思想将两个或两个以上的有序表组合成一个新有序表
在内部排序中,通常采用的是2-路归并排序。即:将两个位置相邻的记录有序子序列
在这里插入图片描述
归并为一个记录的有序序列。

在这里插入图片描述
2-路归并排序排序过程

  1. 初始序列看成n个有序子序列,每个子序列长度为1
  2. 两两合并,得到  n/2  个长度为2或1的有序子序列
  3. 再两两合并,重复直至得到一个长度为n的有序序列为止

归并排序Java代码实现:

public class MergeSort {

	public static void main(String[] args) {
		int[] arr = {1,4,7,8,3,6,9};
		sort(arr,0,arr.length-1);
		print(arr);

	}
	
	static void sort(int[] arr, int left, int right) {
		if(left == right) return;
		//分为两半
		int mid = left + (right - left)/2;
		//左边排序
		sort(arr,left,mid);
		//右边排序
		sort(arr,mid+1,right);
		
		merge(arr,left,mid+1,right);
		
	}
		
	static void merge(int[] arr , int leftPtr, int rightPtr, int rightBound) {
		int mid =rightPtr - 1;
		int[] temp =new int[rightBound - leftPtr +1];
		
		int i=leftPtr;
		int j=rightPtr;
		int k=0;
		while(i <= mid && j<=rightBound) {
			temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
			/*
			 * if(arr[i] <= arr[j]) { temp[k++] = arr[i++]; } else { temp[k++] = arr[j++]; }
			 */
		}
		while(i<=mid) temp[k++] = arr[i++];
		while(j<=rightBound) temp[k++] = arr[j++];
		//print(temp);
		for(int m=0;m<temp.length;m++) arr[leftPtr +m]=temp[m];
	}
	
	
	static void swap(int[] arr,int i,int j) {
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}

	static void print(int[] arr) {
		for(int i = 0; i<arr.length;i++) {
			System.out.print(arr[i]+" ");
		}
	}

}

Java对象排序
对象排序一般要求稳定

快速排序

  首先对无序的记录序列进行“一次划分”,之后分别对分割所得两个子序列“递归”进行快速排序。
在这里插入图片描述
动画演示:
在这里插入图片描述
Java代码实现:

public class QuickSort {

	public static void main(String[] args) {
		int[] arr = {7,3,2,8,1,9,5,4,6,10};
		sort(arr,0,arr.length-1);
		print(arr);

	}
	
	static void sort(int[] arr,int leftBound,int rightBound) {
		if(leftBound >= rightBound) return;
		int mid = partition(arr,leftBound,rightBound);
		sort(arr,leftBound,mid-1);
		sort(arr,mid+1,rightBound);
	}
	
	static int partition(int[] arr,int leftBound,int rightBound) {
		int pivot = arr[rightBound];
		int left = leftBound;
		int right = rightBound-1;
		
		while(left<=right) {
			while(left <= right && arr[left]<= pivot) left++;
			while(left <= right && arr[right]>pivot) right--;
			
			if(left < right) swap(arr,left,right);
		}
		swap(arr,left,rightBound);
		
		return left;
	}
		
	
	
	static void swap(int[] arr,int i,int j) {
		int temp = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}

	static void print(int[] arr) {
		for(int i = 0; i<arr.length;i++) {
			System.out.print(arr[i]+" ");
		}
	}


}

计数排序

非比较排序
桶思想的一种
算法思想:
  计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值等于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。将元素相同的个数存储在对应的数组(类似于桶)中,最后按顺序输出即可。
适用于特定的问题,也就是对源数据有要求
量大但是范围小
如:某大型企业数万名员工年龄排序
  如何快速得知高考名次(腾讯面试)

public class CountSort {

	public static void main(String[] args) {
		int[] arr = {2,4,2,3,7,1,1,2,2,5,6,9,8,5,7,4,8,9,0};
		
		int[] result = sort(arr);
		
		System.out.println(Arrays.toString(result));
	}
	
	static int[] sort(int[] arr) {
		int[] result = new int[arr.length];
		
		int[] count = new int[10];
		
		for(int i=0; i<arr.length;i++) {
			count[arr[i]]++;
		}
		
		System.out.println(Arrays.toString(count));
	
		//不稳定
		//for(int i=0,j=0; i<count.length;i++) {
		//	while(count[i]-- >0) result[j++] = i;
		//}
		
		//累加数组
		for(int i=1; i<count.length;i++) {
			count[i] = count[i]+count[i-1];
		}
		System.out.println(Arrays.toString(count));
		for(int i=arr.length-1;i>=0;i--) {
			result[--count[arr[i]]] = arr[i];
		}
		
		
		return result;
	}

}

基数排序

非比较排序
桶思想的一种
多关键字排序
基本思想 基数排序是一种借助“多关键字排序”的思想来实现
多关键字排序
最高位优先MSD
  先对最高位关键字排序,将序列分成若干子序列,每个子序列有相同的值;
  然后让每个子序列对次关键字排序,又分成若干更小的子序列;
  依次重复,直至就每个子序列对最低位关键字排序,就可以得到一个有序的序列。

最低位优先LSD
  首先依据最低位排序码对所有对象进行一趟排序。
  再依据次低位排序码对上一趟排序结果排序。
  依次重复,直到依据排序码最后一趟排序完成,就可以得到一个有序的序列。

Java代码实现:

public class RadixSort {

	//最低位优先实现
	public static void main(String[] args) {
		int[] arr = {421,24,1105,534,305,430,126};
		
		//查找数组中最大值
		int max = findMax(arr);
		
		//得到最大值的位数
		int digit = findDigit(max);
		
		int[] result = sort(arr, digit);
		
		System.out.println(Arrays.toString(result));
	}
	
	static int[] sort(int[] arr,int digit) {
		int[] result = new int[arr.length];
		
		int[] count = new int[10];
		
		for(int i=0; i<digit;i++) {
			int division = (int)Math.pow(10, i);
			//System.out.println(division);
			for(int j=0; j<arr.length;j++) {
				int num = arr[j]/division %10;
				
				//System.out.println(num+" ");
				
				count[num]++;
			}
			//System.out.println();
			//System.out.println(Arrays.toString(count));
	
			for(int m=1; m<count.length;m++) {
				count[m] = count[m]+count[m-1];
			}
			//System.out.println(Arrays.toString(count));
			for(int n=arr.length-1;n>=0;n--) {
				int num = arr[n]/division %10;
				result[--count[num]] = arr[n];
			}
		
			System.arraycopy(result,0,arr,0,arr.length);
			Arrays.fill(count,0);
		}
			
		return result;
	}
	
	//找到最大值
	static int findMax(int[] arr) {
		int max = arr[0];
		for(int i = 1; i < arr.length; i++) {
			max= arr[i] > max ? arr[i] :max;
		}	
		return max;
	}
	
	static int findDigit(int digit) {
		 int count=0;		  
		   while(digit>0)
		   {
			 digit/=10;
		     count++;
		   }
		 return count;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值