一、 排序方式
冒泡排序、选择排序、插入排序、归并排序;
1、冒泡排序
原理:相邻元素相比较,每遍历一次,就在数组的末尾确定一个max值。
“向后沉,相邻排”
2、选择排序
原理:每遍历一次,确定一个min值存放在数组首段。
“借用min存放,再相交换”
3、插入排序
原理:假设前i个已经排列好顺序,将第i+1个插入进已排好的序列中。
冒泡排序、选择排序、插入排序代码
package basis_01_myExamination;
public class sort {
public static void main(String[] args) {
// TODO Auto-generated method stub
int[] arr=new int[]{0,2,1,5,3,8};
//TEST1:冒泡排序测试
//arr=bubbleSort(arr);
//printArray(arr);
//TEST2:选择排序测试
//arr=selectSort(arr);
//printArray(arr);
//TEST3:插入排序测试
arr=insertSort(arr);
printArray(arr);
}
//冒泡排序:向后沉,相邻比较;
public static int[] bubbleSort(int[] arr){
for(int i=arr.length-1 ; i>0 ; i--){
for(int j=0 ; j<i ; j++){
if(arr[j]>arr[j+1]){
change(arr,j,j+1);
}
}
}
return arr;
}
//选择排序
public static int[] selectSort(int[] arr){
int min;
for(int i=0;i<arr.length;i++){
min=i;
for(int j=i+1;j<arr.length;j++){
if(arr[min]>arr[j]){
min=j;
}
}
change(arr,i,min);
}
return arr;
}
//插入排序
public static int[] insertSort(int[] arr){
for(int i=1;i<arr.length;i++){
/*for(int j=i-1;j>=0;j--){
if(arr[j]>arr[j+1]){
change(arr,j,j+1);
}
}*/
//上面的for+if语句可以将if语句合并到for语句中;
for(int j=i-1;j>=0 && arr[j]>=arr[j+1];j--){
change(arr,j,j+1);
}
}
return arr;
}
//交换两个元素
public static void change(int[] arr, int p1, int p2){
int temp=arr[p1];
arr[p1]=arr[p2];
arr[p2]=temp;
}
//打印一个数组
public static void printArray(int[] arr){
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
}
}
二、归并排序
一、原理
递归+外排的思路;
“将左右两边分别排好,再合并排好”
二、常见应用
1、求数组小和
merge函数设计;
merge原理:对于两个有序的数组,如果p1所指的数,比p2小,则p1比p2及其之后的元素都小,计算:p1*(p2及其之后元素的个数)
2、求数组逆序对
merge函数的设计:
merge原理:对于两个有序的数组,如果p1所指的元素大于p2,则p1及其之后的每一个元素都大于p2,都与p2构成一个逆序对。
3、数组小和、逆序对代码
//Q3:求解小和问题
//小和问题描述:在一个数组,每一个数左边的数,比当前数小的累计和,称为改数组的小和;
//思路:归并排序
// 左边求最小和:递归,调用自己smallSum
// 右边求最小和:递归,调用自己smallSum
// 合并求最小和:merge函数;
public static int smallSum(int[] arr,int L,int R){
if(L==R)
return 0;
int mid=L+((R-L)>>2);
int leftSum=smallSum(arr,L,mid);
int rightSum=smallSum(arr,mid+1,R);
int mergesum=mergeSum(arr,L,mid,R);
//左边的最小和+右边的最小和+合并后的最小和
return leftSum+rightSum+mergesum;
}
public static int mergeSum(int[] arr, int L,int mid, int R){
int p1=L;
int p2=mid+1;
int[] helpArr=new int[(R-L+1)];
int i=0;
int sum=0;
while(p1<=mid&&p2<=R){
//注意,在helpArr数组之前,不能改变p1和p2的值
sum =+ arr[p1]< arr[p2]? arr[p1]*(R-p2+1):0;
helpArr[i++]=arr[p1]< arr[p2]?arr[p1++]:arr[p2++];
}
while(p1<=mid)
helpArr[i++]=arr[p1++];
while(p2<=R)
helpArr[i++]=arr[p2++];
for(int j=0;j<helpArr.length;j++)
//arr[L++]=helpArr[j];错误,不能改变L的值。
arr[L+j]=helpArr[j];
return sum;
}
//Q4:逆序对问题
//在一个数组中,如果左边的数比右边的数大,则两个数构成一个逆序对,求一个数组的所有逆序对;
//思路:
public static void reverseOrder(int[] arr, int L, int R){
if(L==R)
return;
int mid=L+((R-L)>>2);
reverseOrder(arr,L,mid);
reverseOrder(arr,mid+1,R);
mergeOrder(arr,L,mid,R);
}
public static void mergeOrder(int[] arr, int L,int mid, int R){
int p1=L;
int p2=mid+1;
int[] helpArr=new int[(R-L+1)];
int i=0;
while(p1<=mid&&p2<=R){
//注意,在helpArr数组之前,不能改变p1和p2的值
//当p1所指的数,比p2所指的数大时,则说明,p1及其之后的全部数,都比p2大,即p1即其之后的全部数,都和p2构成逆序;
if(arr[p1]>arr[p2]){
int m=p1;
while(m<=mid){
System.out.println(arr[m++]+" "+arr[p2]);
}
}
helpArr[i++]=arr[p1] < arr[p2] ? arr[p1++] : arr[p2++]; //保证了两个小数组,始终有序,且为升序排序
}
while(p1<=mid){
helpArr[i++]=arr[p1++];
}
while(p2<=R){
helpArr[i++]=arr[p2++];
}
for(int j=0; j<helpArr.length; j++){
arr[L+j]=helpArr[j];
}
}
三、对数器
一、对数器的作用
(1)在没有OG时,利用对数器测试算法的正确性;
(2)验证贪心算法的正确性;
(3)在测试小OG时算法通过,但是测试大OG时算法错误,可以利用对数器迅速找到错误原因。
(4)当无法得到一个完全正确的对比算法时,利用对数器将每次错误的OG样本和OG结果打印出来,人为干预,对OG样本进行干预,判断错误的对象和原因,人为进行调整。
二、对数器的原理
Step1:设计一个随机样本产生器;
Step2:设计一个时间复杂度和空间复杂度没有那么好的绝对正确的对比算法;
Step3:利用随机样本产生器,多次产生OG,对两个算法进行测试,并比较二者的结果。