笔试算法题

〇、设计模式

六大设计原则:

开闭原则(扩展开放,修改关闭),单一职责原则(每个类的职责单一)、迪米特原则(最少知道原则,模块相对独立)、里氏代换原则(父类能用的,子类都能用)、接口隔离原则(多个好于一个,解耦)、依赖倒置原则(依赖于抽象,但不依赖于具体)

二十三种设计模式:

创建型模式:

  单例模式、建造者模式、工厂方法模式、简单工厂模式、抽象工厂模式、原型模式

结构性模式:

  代理模式、组合模式、适配器模式、装饰者模式、享元模式、外观模式、桥接模式

行为型模式:

  策略模式、状态模式、责任链模式、观察者模式、模板方法模式、迭代器模式、备忘录模式、访问者模式、中介者模式、解释器模式、命令模式

OO、数据结构

时间复杂度 T(n)=f(n),O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n!) < O(nn)

  f(n)=运行次数

12          O(1)            常数阶,不涉及循环,时间的消耗不会由某个变量增加而变长
log2n+3     O(logn)       对数阶
2n+3,for 0-n O(n)            线性阶
n+nlog2n+3  O(nlogn)        nlogn阶
3n^2+2n     O(n^2)          平方阶
n^3         O(n^3)          立方阶
n^k         O(n^k)          n方阶
2^n         0(2^n)          指数阶

1、数组

//常规使用
int[] arr = new int[3];
int[] arr = {1,2,3}

2、链表

//常规使用
LinkedList<String> list = new LinkedList<>();
list.add("b");
list.set(0,"a");
String a = list.get(0);
linkedList.contains("b");
linkedList.remove("b");

3、队列

//常规使用,FIFO
Queue<String> queue = new LinkedListed<>();
queue.offer("a");
queue.offer("b");
queue.offer("c");
queue.offer("d");
queue.poll();//a
//获取队首元素
queue.element();//b
queue.peek();//b

queue.poll();//b
queue.poll();//c

4、双向队列

//常规使用
Deque<Integer> deque=new LinkedList<>();
deque.offer(122);//尾部入队,区别在于如果失败了返回false
deque.poll(122);//尾删除,失败返回false
deque.peek();//获取队尾元素

deque.add(1);//头插入,失败抛异常
deque.remove();//头删除,失败抛异常
deque.element();//获取队首元素

5、栈

//常规使用
Deque<Integer> stack = new ArrayDeque<Integer>();
stack.push(12);//入栈
int tail = stack.pop();//出栈
tail = stack.peek();//获取栈顶元素

6、二叉树

OOO、算法

1、链表反转

public static Node reverseNode(Node node){
    Node pre=null;
    Node next=null;
    while(node != null){
        next = node.getNext();//先存储下一个节点
        node.setNext(pre);//当前节点的下个节点为null
        pre = node;//当前节点赋给pre
        node = next;//当前节点的下一个赋给当前节点node
    }
    return pre;
}

2、链表指定区间反转

public static Node reverseNodeOfInterval(Node node,int m,int n){
    Node nodeNew = new Node(0);
    nodeNew.setNext(node);
    Node curNode = nodeNew;
    Node nextNode = node;
    for (int i=0;i<m-1;i++){
        curNode = nextNode;
        nextNode = nextNode.getNext();
    }
    for(int i=0;i<n-m;i++){
        Node temp = nextNode.getNext();//永远把下一节点的下一个节点作为temp节点
        nextNode.setNext(temp.getNext());//下一节点的下一个为temp节点的下一个,跳过temp
        temp.setNext(curNode.getNext());//temp的下一个是当前节点的下一个,即反转后第一个数
        curNode.setNext(temp);//把temp当反转后的第一个数,并接在当前节点后面
    }
    return nodeNew.getNext();
}

一、单例模式

1、懒汉

//常规:懒汉,线程不安全的(不支持多线程)
public class Singleton {

    //1构造函数私有化
    private Singleton(){}
    private static Singleton singleton = null;

    //2构建实例化方法
    public static Singleton getSingleton() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
}
/**
 *
 * 懒汉式(懒加载),支持多线程
 * 优点:第一次调用才初始化,避免内存浪费。
 * 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率。
 */
public class Singleton {

    private Singleton(){}
    private static Singleton singleton= null;

    public static synchronized Singleton getSingleton(){
        if(singleton != null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

2、饿汉

/**
 *
 * 饿汉式
 * 这种方式比较常用,但容易产生垃圾对象。
 *   优点:没有加锁,执行效率会提高。
 *   缺点:类加载时就初始化,浪费内存
 */
public class Singleton {
    private Singleton(){}
    private static Singleton singleton = new Singleton();

    public static Singleton getSingleton(){
        return singleton;
    }

}

二、排序算法

1、冒泡排序

/**
    每一轮挑选最大的放最右边,固定
    fori=0 length-1
    forj=0 length-1-i
*/
public class bubbleSort {
    /**
     * 冒泡法排序
     * @param array 数组
     */
    public static void bubbleSort(int[] array) {
        // 临时变量
        int temp;
        // 控制比较轮次,一共 n-1 趟
        for (int i = 0; i < array.length - 1; i++) {
            // 上次遍历之后倒数第 i 位开始 已经是有序的了,所以这次遍历的范围是 [0,array.length - 1 - i)
            for (int j = 0; j < array.length - 1 - i; j++) {
                // 如果前面的数比后面的数大,则交换
                if (array[j] > array[j + 1]) {
                    temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }

    }

}

2、选择排序

/*
    选择排序:第一个和所有的比最小,一轮下来固定最小
    fori length-1
    forj=i+1 length
 */
public class choiceSort {
    /**
     * 选择排序 从小到大排序
     * 时间复杂度O(n^2)
     * @param array array
     */
     public static void selectSort(int[] array) {
        for (int i = 0; i < array.length -1 ; i++) {
            for (int j = i + 1; j < array.length; j++) {
                if (array[i] > array[j]) { // 说明假定的最小值并不是最小,交换
                    int temp=array[i];
                    array[i] = array[j]; // 重置最小值
                    array[j] = temp; // 重置最小值的索引
                }
            }
        }
    }

}

三、生产者和消费者

1、线程之间的通信问题:object对象的wait、notifyAll

//管程法实现生产者消费者
public class CompanyProducts {
    Product[] goods = new Product[10];
    int count = 0;//计数器

    //生产者生产放入缓冲区
    public synchronized void produce(Product good){
        while(count == goods.length){//缓冲区满
            try {
                this.wait();//生产者等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        goods[count] = good;
        count++;

        this.notifyAll();//唤醒等待的线程
    }

    //消费者消费产品
    public synchronized Product consume(){
        while(count == 0){//缓存区为空
            try {
                this.wait();//消费者等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //消费者消费:没空,消费商品
        count--;//长度是10,数组0-9
        Product good = goods[count];

        this.notifyAll();//唤醒等待的线程
        return good;
    }
}

测试:

public class TestPC {
    public static void main(String[] args) {
        CompanyProducts products = new CompanyProducts();
        for (int i = 0; i < 40; i++) {
            int finalI = i+1;
            new Thread(()->{
                products.produce(new Product(finalI));
                System.out.println(Thread.currentThread().getName()+"->生产:第"+ finalI +"个商品");
            },"商店").start();
        }
        for (int i = 0; i < 20; i++) {
            int finalI = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"->消费了:第"+ products.consume().getId()+"个商品");
            },"顾客A").start();
        }
        for (int i = 0; i < 20; i++) {
            int finalI = i;
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"->消费了:第"+ products.consume().getId()+"个商品");
            },"顾客B").start();
        }
    }
}
/*
    真正的多线程开发,降低耦合性

    线程就是一个单独的资源类,没有任何附属的操作!!!
    1、属性、方法
 */
public class SaleTicket {

    public static void main(String[] args) {
        //并发:多线程操作同一个资源类,把资源类丢入接口
        Ticket_synchronized ticket = new Ticket_synchronized();
        //jdk1.8 lamdba表达式 (参数)->{代码}
        new Thread(()->{
            for (int i = 0; i < 20; i++) {
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                ticket.sale();
            }
        },"C").start();
    }
}

//资源类
class Ticket_synchronized{
    private int number = 50;

    /*
        synchronized 本质就是排队
     */
    public synchronized void sale(){
        if(number>0){
            System.out.println(Thread.currentThread().getName()+"卖出第"+number--+"票,剩余:"+number);
        }
    }
}

2、线程之间的通信问题:condition对象的await、signalAll

/*
    JUC Locked
    线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
    线程交替执行 A、B操作同一变量 num=0
    A num++
    B num--
 */
public class ProducterAndConsumer_JUC {
    public static void main(String[] args) {
        Data_JUC data = new Data_JUC();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();

        //C、D虚假唤醒问题:if->while
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.increment();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"C").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.decrement();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"D").start();
    }
}

//判断等待,业务,通知
class Data_JUC{
    private int num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();

    //AC业务排序区分
    public void increment() throws InterruptedException {
        lock.lock();//不属于业务
        try{
            while(num !=0){//等于0干活
                //等待
                condition.await();
            }
            num++;
            System.out.println(Thread.currentThread().getName()+"=>"+num);
            //通知其他线程,+1完毕
            condition.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

    }

    public void decrement() throws InterruptedException {
        lock.lock();//不属于业务
        try {
            while(num == 0){//不等于0干活
                //等待
                condition.await();
            }
            num--;
            System.out.println(Thread.currentThread().getName()+"=>"+num);
            //通知其他线程,我-1完毕了
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }

    }
}

四、死锁

public class DeadLock {
    public static void main(String[] args) {
        Makeup her1=new Makeup(0,"灰姑娘");
        Makeup her2=new Makeup(1,"⽩雪公主");
        new Thread(her1).start();
        new Thread(her2).start();
    }
}

class Lipstick{

}
class Mirror{

}
//资源类
class Makeup extends Thread{
    static Lipstick lipstick=new Lipstick();
    static Mirror mirror=new Mirror();
    int choice;
    String girlName;

    Makeup(int choice,String girlName){
        this.choice=choice;
        this.girlName=girlName;
    }

    public void run(){
        try{
            makeup();
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
    private void makeup()throws InterruptedException {
        if(choice==0){
            synchronized(lipstick){
                System.out.println(this.girlName+"获得⼝红的锁");
                Thread.sleep(1000);
                synchronized(mirror){
                    System.out.println(this.girlName+"获得镜⼦的锁");
                }
            }
        } else{
            synchronized(mirror){
                System.out.println(this.girlName+"获得镜⼦的锁");
                Thread.sleep(2000);
                synchronized(lipstick){
                    System.out.println(this.girlName+"获得⼝红的锁");
                }
            }
        }
    }
}

五、华为机试

ps:排序,字符串,数组,递归,循环,栈,滑动窗口最大值,成绩统计,日志按时间排序,数组整数对求最小值,机器人走迷宫,仿LISP运算,找城市(树和子树)

1、字符串最后一个单词的长度

描述:计算字符串最后一个单词的长度,单词以空格隔开,字符串长度小于5000。(注:字符串末尾不以空格为结尾)

核心:字符串截取后获取最后一个字符串的长度

核心代码:

  str.split(" ");
  s[s.length-1].length();

扩展:字符串分割用正则

  String[] sp = text.split("[^a-zA-Z]+");

import java.util.Scanner;
//思路:字符串截取后长度-1的数组位置
public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        String str = sc.nextLine();
        String[] s = str.split(" ");
        int length = s[s.length - 1].length();
        System.out.println(length);
    }   
}

考察:字符串的最后一个,字符串的截取

2、计算某字符出现次数

描述:接受一个由字母、数字和空格组成的字符串,和一个字符,然后输出输入字符串中该字符的出现次数。(不区分大小写字母)

核心:不区分大小写的某字符个数
核心代码:
  ss.toUpperCase();
  s.toUpperCase();
  String str = ss.replaceAll(s,"");
  ss.length()-str.length();

举一反三:

十进制转二进制字符串后1的个数

  Integer.toBinaryString(5);

十六进制转十进制

  描述:0xAA -> 10*16^0+10*16^1=170

  核心代码:Integer.decode("0xAA");

  扩展:

    十进制转二进制字符串:Integer.toBinaryString(5);

    四舍五入:Math.round(4.4);  Math.round(4.5);

    数组排序:Arrays.sort(arr);

十进制转n进制:

  核心:str.charAt(m%n); M/=N;

import java.util.*;

public class Solution {
    public String solve (int M, int N) {
        if(M == 0) return "0";
        boolean flag = true;
        if(M < 0){
            M = - M;
            flag = false;
        }
        StringBuffer res = new StringBuffer();
        String hex = "0123456789ABCDEF";
        while(M != 0){
            res.append(hex.charAt(M % N));
            M = M / N;
        }
        return flag == true ? res.reverse().toString() : "-"+res.reverse().toString();
    }
}
import java.util.Scanner;
//思路:大字符串的长度-字符截取后字符串的长度
public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        String ss = sc.nextLine().toLowerCase();
        String s =sc.nextLine().toLowerCase();
        String str = ss.replaceAll(s,"");
        System.out.println(ss.length() - str.length());
    }   
}

二进制加减:

  add、substact、multiply、divide

BigInteger b1=new BigInteger("10",2);
BigInteger b2=new BigInteger("1",2);
BigInteger c=b1.add(b2);
System.out.println(Integer.toBinaryString(Integer.valueOf(c.toString())));
//加减乘除 最大最小
System.out.println(b1.add(b2));   //加法操作
System.out.println(b1.subtract(b2));   //减法操作
System.out.println(b1.multiply(b2));   //乘法操作
System.out.println(b1.divide(b2));   //除法操作

System.out.println(b1.max(b2));    //较大数
System.out.println(b1.min(b2));    //较小数
核心代码:
  Set set = new TreeSet();
  set.add(scan.nextInt());
  Iterator it = set.iterator();
  it.hashNext();  
  it.next();
import java.util.Scanner;
import java.util.TreeSet;
import java.util.Set;
import java.util.Iterator;
public class Main{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        //每次输入
        while(scan.hasNext()){
            int n = scan.nextInt();
            //去重排序
            Set set = new TreeSet();
            for(int i=0;i<n;i++){
                set.add(scan.nextInt());
            }
            //遍历
            Iterator it = set.iterator();
            while(it.hasNext()){
                 System.out.println(it.next());
            }
        }
    }
}

 4、字符串分割

描述:按长度为8拆分,不够用0补位

核心:不满足8的整数倍补8个0,再依次截取8的区间

核心代码:

  s+="00000000";

  s.substring(i*8,i*8+8);

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
       Scanner sc = new Scanner(System.in);
       while(sc.hasNext()){
            String s = sc.nextLine();
            if(s.length()%8 !=0 )
                s = s + "00000000";
            int n = s.length()/8; 
            for(int i=0;i<n;i++){
                System.out.println(s.substring(i*8, i*8+8));
            }
       }
    }   
}
    

6、反向提取不重复的整数

描述:987673   >>   37689

核心:字符串反转;set去重;遍历

核心代码:

  str.reverse();

  if(set.add(str.substring(i,i+1)))

    System.out.println(str.substring(i,i+1));

扩展:(1)数组反转

  String ss = "i am a boy";
  String[] arr = ss.split(" ");
  for(int i=arr.length-1;i>=0;i--){
         System.out.print(arr[i]+" ");
  }

  (2)字符全反

  new StringBuilder(str).reverse().toString()

import java.util.Scanner;
import java.util.Set;
import java.util.HashSet;
public class Main{
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        String str = scan.nextLine();
        StringBuffer sb = new StringBuffer(str);
        sb.reverse();
        
        Set set = new HashSet();
        for(int i = 0;i<sb.length();i++){
           String s = sb.substring(i,i+1);
           if(set.add(s)){
               System.out.print(s);
           }
        }
    }   
}

7、质数因子

描述:输入一个数,输出这个数所有的因子

核心:因子范围【2,数的平方根】;取余没有余数即整除;最后判断数是否为1

核心代码:

  for i=2;i<=Math.sqrt(data);

  while(data % i == 0){

    System.out.println(i+" ");

    data /= i;  

  }

  if(data != 1){

    System.out.println(data); 

  }

扩展:此题不能用位运算代替,位运算代替时,除数一定要为2^N

import java.util.Scanner;
public class Main{
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        int data = sc.nextInt();
        int ms =  (int)(Math.sqrt(data)+1);
        for (int i = 2; i < ms; i++) {
            while (data % i == 0) {
                System.out.print(i + " ");
                data = data / i;
            }
        }
        if(data != 1){
            System.out.println(data+"");
        }
    }   
}

8、递归求最小公倍数

核心:两数乘积÷最大公约数

核心代码:最大公约数(辗转相除法)

 private static int gys(int a, int b) {
        if (b == 0) return a;
        return gys(b, a % b);
    }

import java.util.Scanner;
public class Main {
    /**
     * 求最大公约数:辗转相除法
     */
    private static int gys(int a, int b) {
        if (b == 0) return a;
        return gys(b, a % b);
    }
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int a = in.nextInt();
        int b = in.nextInt();
        int gysNum = gys(a, b);
        System.out.println(a*b/gysNum);
    }
}

9、数组分组:递归求可能满足条件的情况

描述:被3整除分一组(不包括5的倍数),被5整除分一组,其余随意分配,两组数组和相同

核心:3、5的数组累加,然后和其余的数组分别求值

import java.util.Scanner;
public class Main {
    public static void main(String args[]){    
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            int n = scanner.nextInt();
            int[] nums = new int[n];
            int index = 0,sum1 = 0,sum2 = 0;
            for(int i=0;i<n;i++){
                int tmp = scanner.nextInt();
                if(tmp % 5 == 0) sum1 += tmp;
                else if (tmp % 3 == 0) sum2 += tmp;
                else nums[index++] = tmp;
            }
            System.out.println(isExists(sum1, sum2, nums, 0));
        }
        scanner.close();
    }  
    public static boolean isExists(int sum1,int sum2,int[] nums,int index){
        if(index == nums.length && sum1 != sum2) return false;
        if(index == nums.length && sum1 == sum2) return true;
        if(index < nums.length) return isExists(sum1+nums[index], sum2, nums, index+1) || isExists(sum1, sum2+nums[index], nums, index+1);
        return false;
    }
}

10、密码合格性:数组+字符串

要求:length>8;满足大小写、数字、其他字符任意3种;最大重复子串不超过2

核心: Pattern.compile("[a-z]").matcher(str).find(); //正则表达式

  s.substring(i+3).contains(s.substring(i,i+3));//最大重复子串判断 contains

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Main{
	public static void main(String[] args){
		Scanner sc=new Scanner(System.in);
        while(sc.hasNext()){
            String str=sc.nextLine();
            if(str.length()>8 && regex(str) && !repeat(str))
                System.out.println("OK");
            else
                System.out.println("NG");
        }
	}
	
	public static boolean regex(String s){
		int count=0;
		String[] str={"[a-z]","[A-Z]","[0-9]","[^a-zA-Z0-9]"};
		for(int i=0;i<str.length;i++){
			Pattern p=Pattern.compile(str[i]);
			Matcher m=p.matcher(s);
			if(m.find())
				count++;
		}
		return count>=3;
	}
	
	public static boolean repeat(String s){
		for(int i=0;i<s.length()-3;i++){
            String str = s.substring(i+3);
            String str2 = s.substring(i,i+3);
			if(str.contains(str2))
				return true;
		}
		return false;
	}
}

11、字符串排序

规则:a-z不区分大小写;大小写按顺序来;非字母位置不变

核心:字符串append,遍历A-Z(+32a-z);for循环字符串把非字母元素补上

a-z 97,122   A-Z 65,90

import java.util.Scanner;
public class Main{
	public static void main(String[] args){
		Scanner sc = new Scanner(System.in);
		while (sc.hasNext()){
			String str = sc.nextLine();
			char [] cha = str.toCharArray();
			StringBuffer sb = new StringBuffer();
			for (int i = 0; i<26; i++){
				char c = (char)(i + 'A');
				for (int j = 0; j<str.length(); j++){
                    //同一个大小写同时存在按照顺序插入
					if (cha[j] == c || cha[j] == (char)(c + 32))
					sb.append(cha[j]); 
				}
			}
			
			for (int k = 0; k<str.length(); k++){
				if ((cha[k] < 'A' || cha[k] > 'Z') && (cha[k] < 'a' || cha[k] > 'z'))
				sb.insert(k, cha[k]);
			}
			System.out.println(sb.toString());
		}
	}
		
}

1*、坐标移动

描述:输入字符串读取坐标移动,最后输出坐标(0,0)>>  A10S20W10D30 >> (0,10)

开发一个坐标计算工具, A表示向左移动,D表示向右移动,W表示向上移动,S表示向下移动。从(0,0)点开始移动,从一些坐标,并将最终输入结果输出到输出文件里面。

import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//思路:方向,步长(合理校验)
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        String str = in.nextLine();
        int x=0,y=0;//x表横坐标,y代表纵坐标
        String[] ss = str.split(";");
        String direction;//方向
        for(String s :ss){
            if (s.equals("")||s==null){
                continue;
            }
            direction =  s.substring(0,1);//WSAD方向
            String step = s.substring(1);//步数,需要校验
            if(Pattern.compile(".*[a-zA-Z]+.*").matcher(step).matches()){
                continue;
            }
            switch(direction){
                case "A"://横坐标左移
                    x-=Integer.parseInt(step);
                    break;
                case "D"://横坐标右移
                    x+=Integer.parseInt(step);
                    break;
                case "S"://纵坐标下移
                    y-=Integer.parseInt(step);
                    break;
                case "W"://纵坐标上移
                    y+=Integer.parseInt(step);
                    break;
                default:
                    break;
            }
        }
        System.out.println(x+","+y);
    }
}

2*、识别有效的IP地址和掩码并进行分类统计:ABCDENI

请解析IP地址和对应的掩码,进行分类识别。要求按照A/B/C/D/E类地址归类,不合法的地址和掩码单独归类。

所有的IP地址划分为 A,B,C,D,E五类

A类地址从1.0.0.0到126.255.255.255;

B类地址从128.0.0.0到191.255.255.255;

C类地址从192.0.0.0到223.255.255.255;

D类地址从224.0.0.0到239.255.255.255;

E类地址从240.0.0.0到255.255.255.255

 

私网IP范围是:

从10.0.0.0到10.255.255.255

从172.16.0.0到172.31.255.255

从192.168.0.0到192.168.255.255

 
子网掩码为二进制下前面是连续的1,然后全是0。(例如:255.255.255.32就是一个非法的掩码)
(注意二进制下全是1或者全是0均为非法子网掩码)
 
注意:
1. 类似于【0.*.*.*】和【127.*.*.*】的IP地址不属于上述输入的任意一类,也不属于不合法ip地址,计数时请忽略
2. 私有IP地址和A,B,C,D,E类地址是不冲突的
import java.util.*;
//思路:分别统计ABCDENI的数量,T存储临时变量
//要求:~拆分ip地址与子网掩码;校验子网掩码合法性;校验ip地址合法性;统计非法ip地址count
public class Main {
    public static void main(String[] args) {
        Map<String,Integer> map = new HashMap<>();
        map.put("A",0);//A类地址,网络地址最高位必须为0,由网络地址(1B)和主机地址(3B)组成。[1,126]
        map.put("B",0);//B类地址,网络地址最高位必须为10,由网络地址(2B)和主机地址(2B)组成。[128,191]
        map.put("C",0);//C类地址,网络地址最高位必须为110,由网络地址(3B)和主机地址(1B)组成。[192,223]
        map.put("D",0);//D类地址,网络地址最高位必须为1110,不分网络地址和主机地址。[224,239]
        map.put("E",0);//E类地址,网络地址最高位必须为11110,不分网络地址和主机地址。[240,255]
          /*
            私网地址:
            10.0.0.0~10.255.255.255 A类私有IP
            172.16.0.0~172.31.255.255 B类私有IP
            192.168.0.0~192.168.255.255 C类私有IP
        */
        map.put("N",0);//私有ip地址,ABC各一个
        map.put("I",0);//不合法的ip地址。
        map.put("T",0);//不合法的ip地址。
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            String str = sc.next();
            String[] arr = str.split("~");
            String ip = arr[0];
            String subnet = arr[1];
            if(!isLegalIP(ip) || !isLegalSubnet(subnet)){
                if(!isException(ip)){//排除第一个网络字节是0或127的
                    map.put("I",map.get("I")+1);
                }
            }else{
                if(isPrivateIp(ip)){
                    map.put("N",map.get("N")+1);
                }
                String type = getTypeABCDE(ip);
                map.put(type,map.get(type)+1);
            }
        }
        System.out.println(map.get("A")+" "+map.get("B")+" "+map.get("C")+" "+map.get("D")+" "+map.get("E")+" "
                           +
                          map.get("I")+" "+map.get("N"));
    }
     
    //是否是合法ip
    public static boolean isLegalIP(String ip){
        String[] arr = ip.split("\\.");
        if(arr.length != 4){//4B
            return false;
        }
        for(int i = 0; i < 4;i++){
            int num = Integer.parseInt(arr[i]);
            if(num < 0 || num > 255){//长度限制[1,255]
                return false;
            }  
        }
        return true;
    }
    //是否是合法掩码
    public static boolean isLegalSubnet(String subnet){
        if(!isLegalIP(subnet)){
            return false;
        }
        String[] arr = subnet.split("\\.");
        String binarySunbet = "";
        for(int i = 0; i < 4;i++){
            int num = Integer.parseInt(arr[i]);
            String str = Integer.toBinaryString(num);
            while(str.length() < 8){
                str = "0"+str;
            }
            binarySunbet += str;
        }
        if(!binarySunbet.contains("10")|| binarySunbet.contains("01")){
            return false;
        }else{
            return true;
        }
    }
    //是否是私网IP
    public static boolean isPrivateIp(String ip){
        String[] arr = ip.split("\\.");
        if(Integer.parseInt(arr[0]) == 10){//ip第一个字节=10
            return true;
        }
        //ip第1个字节172,ip第二个字节[16,31]
        if(Integer.parseInt(arr[0]) == 172 && 16 <= Integer.parseInt(arr[1]) && Integer.parseInt(arr[1]) <=31){
            return true;
        }
        //ip第1个字节192,ip第二个字节168
        if(Integer.parseInt(arr[0]) == 192 && Integer.parseInt(arr[1]) == 168){
            return true;
        }
        return false;
    }
    
    //判断属于哪一类IP
    public static String getTypeABCDE(String ip){
        String[] arr = ip.split("\\.");
        if(1 <= Integer.parseInt(arr[0]) && Integer.parseInt(arr[0]) <=126){
            return "A";
        }
        if(128 <= Integer.parseInt(arr[0]) && Integer.parseInt(arr[0]) <192){
            return "B";
        }
        if(192 <= Integer.parseInt(arr[0]) && Integer.parseInt(arr[0]) <224){
            return "C";
        }
        if(224 <= Integer.parseInt(arr[0]) && Integer.parseInt(arr[0]) <240){
            return "D";
        }if(240 <= Integer.parseInt(arr[0]) && Integer.parseInt(arr[0]) <=255){
            return "E";
        }
        return "T";
    }
    
    //判断是否是例外,计数忽略
    public static boolean isException(String ip){
        if(!isLegalIP(ip)){
            return false;
        }
        String[] arr = ip.split("\\.");
        if(Integer.parseInt(arr[0]) == 0 || Integer.parseInt(arr[0]) == 127){
            return true;
        }else{
            return false;
        }
    }
}

 

posted @ 2022-04-28 09:33  小吴dnd  阅读(27)  评论(0编辑  收藏  举报