一.求众数
思路:
- 排序(调用系统中的排序算法,效率较高,自己写的话可以用归并)
- 如果某个数字为众数,那么排序后他会占主体部分,所以用index定位到中间是又很大概率能找到,假设找打的数为a
- 左右扫描,找出该数字的左右边界下边,就是这一堆a左边到哪,右边到哪,并计算该数字的重数,并将它初始化为Mode,以后找到重数更多的遍覆盖掉它
- 分治,计算a左走两边数组的长度(不含a),如果长度都比a的重数少就可以确定里面肯定没有众数,如果长度比a大就递归步骤3
import java.util.Arrays;
/**
* @author SJ
* @date 2020/10/10
*/
public class FindMode {
public static void main(String[] args) {
int[] nums={1,2,2,3,3,4,4,4,4,3,3,3};
findMode(nums);
}
public static void findMode(int[] nums){
Arrays.sort(nums);
Mode mode=fun(nums,0,nums.length-1);
System.out.println("众数是:"+mode.value+"重数是:"+mode.num);
}
public static Mode fun(int[] nums,int left,int right){
int index=(right+left)/2;
int leftbound=findLeftIndex(nums,index,0);
int rigntbound=findRightIndex(nums,index,right);
int middleNums=rigntbound-leftbound+1;
Mode currentMode = new Mode(nums[index], middleNums);
if ((leftbound-left)>middleNums&&left<leftbound){
int temp=fun(nums,left,leftbound-1).num;
if (temp>middleNums){
currentMode.num=temp;
currentMode.value=fun(nums,left,leftbound-1).value;
}
}
if ((right-rigntbound)>currentMode.num&&rigntbound<right){
int temp;
temp = fun(nums,rigntbound+1,right).num;
if (temp>middleNums){
currentMode.num=temp;
currentMode.value=fun(nums,rigntbound+1,right).value;}
}
return currentMode;
}
public static int findLeftIndex(int[] nums,int index,int left){
int leftBound=index;
for (int i = index-1; i >=left; i--) {
if(nums[i]==nums[index])
leftBound=i;
else
break;
}
return leftBound;
}
public static int findRightIndex(int[] nums,int index,int right){
int rightBound=index;
for (int i = index+1; i <=right; i++) {
if(nums[i]==nums[index])
rightBound=i;
else
break;
}
return rightBound;
}
public static class Mode{
public int value;
public int num=0;
public Mode(int value, int num) {
this.value = value;
this.num = num;
}
}
}
"C:\Program Files\Java\jdk1.8.0_131\bin\java.exe"...
众数是:3重数是:5
Process finished with exit code 0
二. 逆序对
import java.util.Arrays;
/**
* @author SJ
* @date 2020/10/10
*/
public class ReversePairs {
public static void main(String[] args) {
int[] nums={7,5,6,4};
int num=mergeSort(nums,0,nums.length-1);
//答案是5
System.out.println(num);
}
public static Integer[] temp=new Integer[10];
public static int merge(int[] nums,int left,int right){
int count=0;
for (int i = left; i <=right ; i++) {
temp[i]=nums[i];
}
int middle=(left+right)/2;
int l=left;
int r=middle+1;
for (int i = left; i<=right ; i++) {
if (l==middle+1&&r<right+1)
nums[i]=temp[r++];
else if (r==right+1&&l<middle+1)
nums[i]=temp[l++];
else if (temp[l]<=temp[r])
nums[i]=temp[l++];
else {
nums[i]=temp[r++];
//右边得数组,r指得那个数字 在右边。按理说左边数组所有的数字都比它小,但是只有l前面的数字比它小
// ,从l到middle(闭区间)所有的数字都比他大。所以从l到middle 与当前r指的数字都构成逆序对
count+=middle-l+1;
}
}
return count;
}
public static int mergeSort(int[] nums,int left,int right){
int leftCount=0;
int rightCount=0;
int aa=0;
if (left==right)
return 0;
else if (left<right){
leftCount= mergeSort(nums,left,(left+right)/2);
rightCount=mergeSort(nums,(left+right)/2+1,right);
aa= merge(nums,left,right);
}
return leftCount+rightCount+aa;
}
}
"C:\Program Files\Java\jdk1.8.0_131\bin\java.exe"
5
Process finished with exit code 0
一开始用静态变量count计算的,但是wa了,因为下一组测试用例还是会在上一组的count上累加。改了改之后通过了。
三.不无聊序列
首先,如果这个序列(包含n个整数)是不无聊序列list,那么它必然包含一个数 list[k] 在此序列里只出现一次。
接下来我们只要判断list[0]~list[k-1] 到 list[k+1]~list[n-1]是不是不无聊序列就行。
触底->窗口减小到2,且这两个数字不相同。
首先,如何找到这个只出现一次的数呢?暴力寻找试试?
暴力法:用map记录每个数字出现的次数,找出只出现一次的,作为中间值,开始递归重复
import java.util.*;
/**
* @author SJ
* @date 2020/10/12
*/
public class SimpleNoBoring {
public static void main(String[] args) {
int[] nums = {1, 2, 3, 2, 1,2,2};
if (isNoBoring(nums, 0, nums.length - 1))
System.out.println("NoBoring!");
else
System.out.println("Boring!");
}
static class Num {
public int nums = 0;//出现频次
public List<Integer> index;//同一个数据出现的下标列表
public Num(int nums, int i) {
this.nums = nums;
index = new ArrayList<>();
index.add(i);
}
}
//判断无不无聊
public static boolean isNoBoring(int[] nums, int left, int right) {
//划分到只剩一个的情况,不无聊
if (left==right)
return true;
//划分到只剩两个的情况,只要两个数字不相同,就不无聊
if (right - left == 1 ) {
if (nums[left]==nums[right])
return false;
}
//划分三个以上的情况
else {
int middle = findGreatestIndex(nums,left,right);
if (middle==-1)//找不到唯一存在的数字
return false;
if (middle == left)//唯一存在的数字在最左边
return isNoBoring(nums, middle + 1, right);
else if (middle == right)//唯一存在的数字在最右边
return isNoBoring(nums, left, middle - 1);
else {//唯一存在的数字在中间,则继续划分
return isNoBoring(nums, 0, middle - 1) && isNoBoring(nums, middle + 1, right);
}
}
return true;
}
//找唯一数据,找到就返回下标,找不到就返回-1
public static int findGreatestIndex(int[] nums,int left,int right) {
//key是数字的值
Map<Integer, Num> map = new HashMap<>();
for (int i = left; i <= right; i++) {
if (map.containsKey(nums[i])) {
Num num = map.get(nums[i]);
num.nums++;
num.index.add(i);
} else
map.put(nums[i], new Num(1, i));
}
Set<Integer> set = map.keySet();
for (Integer integer : set) {
if (map.get(integer).nums == 1) {
return map.get(integer).index.get(0);
}
}
return -1;
}
}
其实想了想之后,上面的程序第一次扫描就已经记录了该数组的所有信息(包括数字,该数字出现的次数,值为该数字的所有下标),根本不需要每次递归都new一个map来记录。我们每次递归只要从第一次new的map里找信息就可以了。
尝试一下。
。。。。
尝试失败了。
//list里保存了所有单独的数字。
//判断list里有没有某个数x,使得left<=x<=right,如果有,证明该范围内有唯一数字,继续拆分
递归拆分之后只要是left~right范围内唯一就好了,不一定是全局范围内唯一,而list记录的是全局唯一的数字的下标,所有我这个想法是错的。