Java排序算法
左(少) | 中 | 右(多) |
---|---|---|
left(less) | mid | right(more) |
- 排序:将数据元素按照从小到大或从大到小的顺序
- 数据结构:存储数据的不同方式。
- 算法(Algorithm):同一个问题的不同解决方法。(针对特定数据结构的)
- 测算算法的优劣
- 时间测算
- 计算算法时间差 幅度不够循环来凑(for循环)
- 空间测算
- O表示时间复杂度
- O(1):以不变应万变(查询数组中的某一个数字arr[1]和arr[100]所花费的时间是一样的)
- O(n):随着查询规模的扩大,查询时间随之扩大 (for循环、数组平均数)
- O表示时间复杂度
排序问题:
宋词记忆法:
选泡插,
快归堆希统计基,
恩方恩老恩一三,
对恩加K恩加K,
不稳稳稳不稳稳,
不稳不稳稳稳稳。
- 稳定性是干什么用的
1.1 选择排序:(不稳)
最简单也最没用(时间复杂度高,且不稳)
/**
* 选择排序
* 有能想到minPos中间变量的思维进行中间变量的转换
* 优化:
* 如果一次遍历不仅能找到最小值还能找到最大值,则外循环就能减少一倍
*/
public class My1Selection {
public static void main(String[] args) {
int[] arr = new int[]{15, 68, 95, 25, 14};
for (int i = 0; i < arr.length; i++) {
int minPos = i; //进行再一次替换最小值
for (int j = i + 1; j < arr.length; j++) {
minPos = arr[minPos] > arr[j] ? j : minPos;
}
new My1Selection().swap(arr,i,minPos);
System.out.print("经过第" + (i+1) + "次循环后的结果");
System.out.println(Arrays.toString(arr));
}
}
public void swap(int[] arr,int i,int minPos){
//将最小值的位置按顺序排列
int temp = arr[i];
arr[i] = arr[minPos];
arr[minPos] = temp;
}
}
时间复杂度
1.1.1 验证算法–(对数器)
产生足够多随机样本
比对被验证算法的结果是否准确
public class My1DataChecker {
public static void main(String[] args) {
check();
}
//产生随机数组
static int[] random(){
Random r = new Random();
int[] arr1 = new int[1000];
for (int i = 0; i < arr1.length; i++) {
arr1[i] = r.nextInt(1000); //100随机数产生的范围
}
return arr1;
}
//选择排序算法
static void SelectionSort(int[] arr2){
for (int i = 0; i < arr2.length; i++) {
int isPos = i;
for (int j = i+1; j < arr2.length; j++) {
isPos = arr2[isPos]>arr2[j]?j:isPos;
}
new My1DataChecker().swap(arr2,i,isPos);
}
}
//数组位置的交换
static void swap(int[] arr2,int i,int isPos){
int temp = arr2[i];
arr2[i] = arr2[isPos];
arr2[isPos] = temp;
}
//系统算法和选择排序结果比较(本节重点)
static void check(){
int[] arr1 = random();
int[] arr2 = new int[arr1.length];
System.arraycopy(arr1,0,arr2,0,arr1.length);//复制数组
System.out.println("未排序前的数组:" + Arrays.toString(arr1));
Arrays.sort(arr1);//系统排序
SelectionSort(arr2);//采用选择算法进行排序
System.out.println("arr1 = "+Arrays.toString(arr1));
System.out.println("arr2 = "+Arrays.toString(arr2));
boolean same = true;
for (int i = 0; i < arr2.length; i++) {
if(arr1[i] != arr2[i]){
same = false;
}
}
System.out.println(same == true?"true":"false");
}
}
1.2 冒泡排序(太慢)
算法核心:比较相邻两个元素,将较大的元素互换。
关键代码实现
/**
* 冒泡排序(方法一)
* 注意内层循环的取值范围
* 数组中的数据是否是顺序都要执行判断一遍
*/
public class My2Bubble {
static void mySort(int[] arr1){
for (int i = 0; i < arr1.length; i++) {//排序次数--共有几轮比较
for (int j = 0; j < arr1.length-i-1; j++) { //交换次数(数组越界问题)
if(arr1[j]>arr1[j+1]){
int temp = arr1[j];
arr1[j] = arr1[j+1];
arr1[j+1] = temp;
}
}
}
}
}
/**
* 冒泡排序(方法二)
* 时间复杂度减少
* 当所给数组是按照顺序排列则只进行了arr.length次for循环
*/
public class My2Bubble {
static void mySort(int[] arr1){
boolean swaped=true;
while(swaped){
for (int i = 0; i < arr1.length-1; i++) {
swaped=false;
if(arr1[j]>arr1[j+1]){
int temp = arr1[j];
arr1[j] = arr1[j+1];
arr1[j+1] = temp;
swaped=true;
}
}
}
}
检验算法
public class My2DataChecker {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {//重复查询10次以确保数据的准确性
swap();
}
}
//随机数组
static int[] myRandom() {
Random ran = new Random();//随机数的生成
int[] arr = new int[100]; //设置数组的元素个数
for (int i = 0; i < arr.length; i++) {
arr[i] = ran.nextInt(100); //设置数组的取值范围
}
return arr;
}
//比对
static void swap() {
int[] arr = myRandom();
int[] arr2 = new int[arr.length];
System.arraycopy(arr, 0, arr2, 0, arr2.length); //数组的复制
Arrays.sort(arr);
new My2Bubble().mySort(arr2);
System.out.println("arr "+Arrays.toString(arr));
System.out.println("arr2"+Arrays.toString(arr2));
boolean same1 = true;
for (int i = 0; i < arr2.length; i++) {
same1 = arr[i]==arr2[i]?true:false;
}
System.out.println(same1);
}
}
1.3 插入排序(基本有序时效率高)
样本小且基本有序的时候效率比较高
核心代码:
* 插入排序的方法
* 注意:内层for循环的初始化设置
*/
public class My3Insertion {
public void mySort(int[] arr){
for (int i = 1; i < arr.length; i++) {//顺序排好的个数
for (int j = i; j > 0; j--) {//未排序的数组
if(arr[j]<arr[j-1]){
int temp = arr[j];
arr[j] = arr[j-1];
arr[j-1] = temp;
}
}
}
}
}
检验方法相同(冒泡排序)
1.4 希尔排序(改进的插入排序)
过程:
- 给定一个间隔,跳着按插入排序进行运算
- 以此类推,一轮拍完之后减少间隔再次排序
- 直至间隔为1时,再次采用插入排序进行运算
间隔最佳方法
- Knuth序列:h=3*h+1
核心代码:(验证方法冒泡排序)
/**
* 希尔排序
* 注意间隔的变化
* for循环的问题变化比较多
*/
public class My4Shell {
public void mySort(int[] arr){
int h = 1;//当h=1时就是插入排序
while(h<= arr.length/3){ //判断数组的长度是否满足最佳间隔(Knuth序列)
h = 3*h+1; //间隔个数
}
for (int i = h; i >0 ; i=(i-1)/3) {//i--可随自己的需求改变
for (int j = i; j < arr.length; j++) {
for (int k = j; k > i-1 ; k-=i) {
if(arr[k]<arr[k-i]){
int temp = arr[k];
arr[k] = arr[k-i];
arr[k-i] = temp;
}
}
}
}
}
}
1.5 归并排序(递归的扩展应用)
/**
* 递归
* 前10项和
*/
public class my5Merge_1 {
public static void main(String[] args) {
System.out.println(Recursion(10));
}
static int Recursion(int x){
if(x<0){
return -1;
}
if(x==0){
return 0;
}
return x+(Recursion(x-1));
}
}
归并排序(算法核心)
static void mySort(int[] arr,int left,int right){
if(left == right) return;
//分两份
int l = left+(right-left)/2;
//左边
mySort(arr,0,l);
//右边
mySort(arr,l+1,right);
myswap(arr,left,l+1,right);
}
static void myswap(int[] arr,int leftPrt,int rightPrt,int rightEnd){
int initiation = leftPrt;
int centre = rightPrt;
int[] arr1 = new int[rightEnd-leftPrt+1];
int i = 0;
while(initiation < rightPrt && centre <= rightEnd){
arr1[i++] = arr[initiation]<=arr[centre]?arr[initiation++]:arr[centre++];
/*
if (arr[begin] <= arr[centre]) { //“=”代表稳定性的问题
arr1[i] = arr[begin];
begin++;
i++;
}
else{
arr1[i] = arr[centre];
centre++;
i++;
}
*/
}
while(initiation<rightPrt)arr1[i++]=arr[initiation++];//(1,3,4,8 10),2,5
while(centre<=rightEnd)arr1[i++]=arr[centre++];//1,3,5,(2,4,6,8,10)
for (int j = 0; j < arr1.length; j++) {
arr[leftPrt+j] = arr1[j];
}
}
对象排序一般要求稳定
1.6 快速排序
过程:(拿到边缘的值时是最坏的情况)
- 选中一个值作为目标(比他大的排在前面,比他小的排在后面)
- 在第一轮的基础上,再在左边的序列中选择一个作为目标重复相同的操作
- 直至选中值的左边只有一个数时直接输出(右边同理)
算法核心:
public class My6Quick {
static void mySort(int[] arr,int left,int right) {
if(left>=right) return; //当只有一个数时
int mid = myopinion(arr, left, right);
mySort(arr, left,mid-1);
mySort(arr, mid+1,right);
}
static int myopinion(int[] arr,int leftPrt,int rightPrt){
int point = arr[rightPrt];
int point1 = leftPrt;
int point2 = rightPrt-1;
while(point1<=point2){//"=",最后两个数的排序时进入循环
while(point1<=point2 && arr[point1]<=point) point1++; //对数组左边的数进行比较判断(从第一个数到目标位置)
while(point1<=point2 && arr[point2] >point) point2--; //对数组右边的数进行比较判断(从最后一个数到目标位置)
if(point1<point2){ //两个目标位置比较大小{1,3,2,(8),5,6,(4),9,8}
swap(arr,point1,point2);
}
}
swap(arr,point1,rightPrt);
return point1;
}
static void swap(int[] arr,int i,int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
1.7 计数排序(量大但范围小)
桶排序的个例之一
非比较排序
适用于特定问题,对源程序有要求。
- 算法思想
- 量大但范围小
- 数万员工的年龄
- 高考名
- 量大但范围小
过程:
- 首先统计某一个数出现的次数,
- 在一个等长的数组中对其输出
算法核心
static int[] mySort(int[] arr){
int[] result = new int[arr.length];
int[] count = new int[5];
for (int i = 0; i < arr.length; i++) {
count[arr[i]]++;//出现的次数进行计算
}
System.out.println("result = "+Arrays.toString(count));
//结果的输出
/**
* 方法一
*/
/*
for (int i = 0,j = 0; i < count.length; i++) {
while(count[i]-->0) result[j++] = i;
}
System.out.println("排序后:" + Arrays.toString(result));
*/
/**
* 方法二
*/
for (int i = 0; i < count.length; i++) {
count[i]+=count[i-1];//统计数组的个数(每一个值-1就是相同数字的最后一个)比如([1, 1, 0, 2, 0, 3, 4, 4, 2, 3]
// [2, 4, 6, 8, 10])2-1=1(最后一个0所在位置)
//[0, 0, 1, 1, 2, 2, 3, 3, 4, 4]
}
for (int i = arr.length-1; i > 0 ; i--) {
result[--count[arr[i]]] = arr[i];
}
return result;
}
以下排序算法很少用到知晓其工作原理即可。
1.8 基数排序
多关键字的排序
1.9 桶排序
1.10 堆排序