很多时候,不需要自己写一个排序查找算法,只需要借用写好的。
虽然有写好的,但是我那边学校招生简章上要求是java或c。没有c++所以说有可能需要自己写算,不能直接调用,因为c的函数库不太了解。
如果有ide的话就直接看源码好了。看完再调用测试。
Arrays工具类有2个常用的方法
import java.util.Arrays;
Arrays.sort(int[] a, int fromIndex, int toIndex);//仅排序fromIndex到toIndex下标的数据
Arrays.sort(int[] a);
//都是默认增序排列
int i1 = Arrays.binarySearch(a, 3);
//二分查找,在a中查3的位置,0开始(一定是在排好序中的数组中查找)
例题3.1
如果是逆序排序则把int[]类型转换为Interger[]型,然后在原来的排序基础上,额外添加一个参数。Collections.reverseOrder()
import java.util.Collections;
Arrays.sort(a,Collections.reverseOrder());//逆序
第二点就是自定义数据类型了。
自定义数据类型
两种方法:
- 设计比较函数
- 定义大小关系
例题3.2
https://www.cnblogs.com/blogxjc/p/9687297.html,
java随机数
Random random = new Random(1);
random.
这种情况,明显要定义一个类。实现comparable接口,再实现compareTo方法
Comparable接口
class Student implements Comparable<Student> {
int number;//学号
int score;//成绩
Student(int number,int score){
this.number = number;
this.score = score;
}
@Override
public int compareTo(Student o) {
return this.score - o.score;//负数,0,正数 对应 小于,等于,大于
//也就是说,this在前面是升序,
//反之就是降序
}
}
否则就是用冒泡排序。
冒泡
Student studentx = new Student(-1, -1);
for (int i = 0; i < n; i++) {
for (int i1 = i; i1 < n; i1++) {//冒泡排序
if(!Compare(student[i], student[i1]) ){
studentx=student[i];
student[i]=student[i1];
student[i1]=studentx;
}
}
}
for (Student student1 : student) {
System.out.print(student1.number);
System.out.print("---");
System.out.println(student1.score);
}
static boolean Compare(Student x,Student y){
if(x.score == y.score){//如果成绩一样,就按学号顺序排名
return x.number<y.number;
}else {
return x.score< y.score;//如果x成绩小就是true
}
}
class Student {
int number;//学号
int score;//成绩
Student(int number,int score){
this.number = number;
this.score = score;
}
}
最后一种方式就是重载符号
重载符号
很遗憾,java不支持重载符号。
下面是c++和c才有的特性。
例题3.2
这里突然卡壳了,后来想了想才反应过来,这里的话,光是数组是很麻烦得,必须得上表。因为奇偶数的个数不能确定。
import java.util.*;
public class c3 {
public static void main(String[] args) {
Random random = new Random(1);
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
ArrayList arrayList = new ArrayList();
ArrayList arrayList1 = new ArrayList();//存偶数
ArrayList arrayList2 = new ArrayList();//存奇数
for (int i = 0; i < n; i++) {
arrayList.add(random.nextInt(100));
}
for (Object o : arrayList) {
if((int)o%2==0)arrayList1.add(o);
else arrayList2.add(o);
}
Collections.sort(arrayList1);
Collections.sort(arrayList2);
System.out.println(arrayList1);
System.out.println(arrayList2);
}
}
再稍微修改一下输出格式就好啦。我这里偷个懒。
排序算法的特性,
线性排序–>奇数排序
逆序数对–>归并排序
排序算法的时间下界是nlogn,(基于比较)
如果要突破下限,也就是说要达到n的时间复杂度必须要给定范围
该组数据是需要辅助数组,分别是0出现了多少次,1出现了多少次,2出现了多少次。等等。
可以看出,我们仅需要再次遍历辅助数组,就可以得到顺序或逆序序列。总的时间来说应该是2N,可以姑且认为比nlogn时间小。
例题 sort
这里用java就稍微有点头疼,所以我稍微思考了一下。详细都写在下面啦。
import java.util.Scanner;
public class c4 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();//n个数
int m = scanner.nextInt();//m数值
scanner.nextLine();
// 这里尤为注意的是nextInt()后的\n会被识别为nextline();所以说要额外多一个nextline来吸收掉
// 比如说
// 5 3
// 3 -35 92 213 -644
// 第一行的3后面有一个换行符\n,这个换行符必须被占据掉才能正确读取第二行的数据,nextInt()不吸收换行符\n。
int[] arr = new int[1000010];//数组,为了避免下标问题,就变得大一点,int数组默认是0.
int[] number = new int[1000010];//辅助数组
String s = scanner.nextLine();
String[] str = s.split(" ");//分割数据
for (int i = 0; i < n; i++) {
arr[i] = Integer.parseInt(str[i]);//得到每一个数据
number[arr[i]+500000]+=1;//建立辅助数组以500000为基准0来判断正负号
}
System.out.println();
for (int i = 0; i < 1000010; i++) {
if(number[i]>0&&i-500000>=m){
System.out.println(i-500000);
}
}
}
}
emm。基本思路一样,主要重点就是scanner的特性和Integer.parseInt()的方法。
我这里输出的是小于m的从小到大的的数据,题目要求是从大到小的m个数据。偷个懒嘿嘿嘿。只需要把范围并逆序查找即可。
多组数据的话再封装一下类就好啦,不过我估计java应该不会有多组数据,或者就是只需要写方法传参数即可。
例题
这里我们回忆一下归并排序
static void MergeSort(int[] arr, int left, int right) {
if (left < right) {
int middle = (left + right) / 2;//中间值
MergeSort(arr, left, middle);
MergeSort(arr, middle + 1, right);
//排序
merge(arr, left, middle, right);
}
}
static void merge(int[] arr, int left, int middle, int right) {
//分别计算两个分组的长度
int n1 = middle - left + 1;
int n2 = right - middle;
//创建两个辅助数组
int[] L = new int[n1];
int[] R = new int[n2];
//将原数组的元素拷贝到辅助数组中
for (int i = 0; i < n1; i++) {
L[i] = arr[left + i];
}
for (int j = 0; j < n2; j++) {
R[j] = arr[middle+1+j];
}
//遍历两个数组,当其中任意一个数组遍历完毕的时候停止循环
int i = 0,j = 0;
int k = left;
while (i<n1 && j<n2){
if (L[i]<=R[j]){
//当遍历到的左边数组元素小于等于遍历到右边的数组,原数组对应位置换为左边数组遍历到的值
arr[k] = L[i];
i++;
}else {
//当遍历到的左边数组元素大于遍历到右边的数组,原数组对应位置换为右边数组遍历到的值
arr[k] = R[j];
j++;
}
k++;
}
//将剩余的数组追加到原数组的尾部
while (i < n1){
arr[k] = L[i];
i++;
k++;
}
while (j < n2){
arr[k] = R[j];
j++;
k++;
}
}
好啦,这上面就是一套标准的归并排序啦。
就是归并的时候稍微复杂一点。不过稍微分析一下来时很好办的。
例题 第k大的数
简单分析一下,第k大是从大到小排列的。
这里来说,需要nlogn的时间排序,再经过一次常数级别的查找。(这里只求第k大,所以没必要排序其他数据,并不关心)
这里用快速排序的思想,就能在O(n)级别内,查到数据。
快速排序的思想:
每次先将所有的值中,任意挑选值为中心值,所有小于4的放左边,比4大的放右边。
每次不变换左右的位置。
然后优化的部分就是,每次算出后,如果另外一边大于或小于目标数,则只算一边
static void QuickSort(int arr[], int low, int high) {
int i;//左边
int j;//右边
int temp;//基准位
int t;
if (low > high) {
return;
}
i = low;
j = high;
//temp就是基准位
temp = arr[low];
while (i < j) {
//先看右边,依次往左递减
while (temp <= arr[j] && i < j) {//比temp大的不管,让j停留在第一个比temp小的。
j--;
}
//再看左边,依次往右递增
while (temp >= arr[i] && i < j) {//比temp小的不管,让i停留在第一个比temp大的。
i++;
}
//每次只找到第一个满足条件的就跳出来,然后交换,先从右边,再从左边
//如果满足条件则交换
if (i < j) {
t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//以下全为i==j
//最后将基准位与i和j相等位置的数字交换
arr[low] = arr[i];
arr[i] = temp;
//递归调用左半数组
QuickSort(arr, low, j - 1);
//递归调用右半数组
QuickSort(arr, j + 1, high);
}
这一部分是标注快排算法。
对于题目来说还有优化的地方
也就是最后一部分
此时,我们仅仅算一半就好了,对该题目从时间复杂度O(nlogn)下降为O(n)的时间复杂度
if(k<i){
//递归调用左半数组
QuickSort(arr, low, j - 1);
}
if(k<i)
//递归调用右半数组
QuickSort(arr, j + 1, high);
if(k==i){
System.out.println(arr[i]);
}