Java之有限状态机

前言

有限状态机,顾名思义有限个状态在事件的触发下做出相应状态的转换。

一、例题

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

1、HashMap

可以用Map记录一个元素出现的次数,然后遍历Map得到记录次数为1的数字。

public int singleNumber3(int[] nums) {
        //用map记录该数字是否记录过
        Map<Integer, Integer> record = new HashMap<>();
        for (int num : nums) {
            record.put(num, record.getOrDefault(num, 0) + 1);
        }
        //寻找map中记录中只记录一次的数字
        for (int num : nums) {
            if (record.get(num) == 1)
                return num;
        }
        return 0;
    }

2、位运算

Integer,Java中4字节,4 x 8 = 32位,统计每一位为1出现的次数,再对3进行取余,就可以得到单次出现数字该位的值。

public int singleNumber4(int[] nums) {
        //1.按位相加
        int[] bit = new int[32];
        for (int num : nums) {
            for (int i = 0; i < 32; i++) {
                bit[i] += (num & (1 << i)) >> i;
            }
        }
        //2.按位对3取余得到结果的每一位
        int res = 0;
        for (int i = 0; i < 32; i++) {
            res |= (bit[i] % 3) << i;
        }
        return res;
    }

3、有限状态机

第二种方法种的对3取余,暗含着每一位上有3种状态,即0,1,2,即状态机0->1->2->0。
1)触发事件为0,状态不发生改变。
2)触发事件为1,状态0 -> 1 -> 2 -> 0这样改变。
在这里插入图片描述
注:图片来自于leetcode大神-------K神。
对于二进制,用2位来表示这三个状态,即00 -> 01 -> 10。然后接下来的目的就是更新这两位。(在此记为two one 两位)

a、如何更新?

  1. 更新one位:
if two == 0:
  if n == 0:
    one = one
  if n == 1:
    one = ~one
if two == 1:
    one = 0

如何简化?内层配合two的值,变换的过程就像一个异或过程。

if two == 0:
    one = one ^ n
if two == 1:
    one = 0

再次简化,与外层two配合,整个内层与外层就像是逻辑与过程。

one = one ^ n & ~two

在这里插入图片描述

  1. 更新two位
    1)本来状态转移之后应该是01->10->00,但是先更新one位,得到01->00->10.
    2)把01->00->10也看成一个状态机,然后根据这个新状态机更新two位,得到01->10->00.(即我们要的结果)
    3)那是不是根据新状态机要再搞一个表达式来更新two?我们发现新状态机01->00->10跟旧状态机10->00->01的两位刚好相反,所以交换位置,用旧表达式来完成更新two.
  1. 如何实施?
    1)上面分析1位的状态如何,Integer有32位,可以同时运算。
    2)最后的状态只有00、01.只需取低位0或1来表示当前位的值。(即取one位)

b、源码

public int singleNumber5(int[] nums) {
        int ones = 0, twos = 0;
        for (int num : nums) {
            ones = ones ^ num & ~twos;
            twos = twos ^ num & ~ones;
        }
        return ones;
    }

总结

1)HashMap
2)位运算
3)有限状态机(二进制状态转换的简化过程)

参考文献

[1] Leetcode原题
[2] K神
[3] 数据结构与算法
[4] Java取反
[5] 运算符详解及优先级

附录

补充知识

  1. 异或、源码、反码、补码
  2. 运算符优先级
    在这里插入图片描述
  3. Java中取反操作
    1)二进制取反:~1 = 0;~0 = 1;
    2)Java中数字取反:
    1.在计算机中,所有数据的表示方式都是以补码的方式存在;
    2.正数:符号位为0,原码,反码,补码相同;
    3.负数:符号位为1,补码 = 反码 + 1。

A. 正数取反解析:
step 1:先求得该数的原码;
step 2:原码取反得到答案原码;
step 3:答案原码转为补码,最后化为数值。

B. 负数取反解析:
step 1:先求得该数的原码;
step 2:再求得该数的补码;
step 3:补码取反得到答案补码,最后化为数值。

C. 举例说明:
(1)、~5 = -6过程解析
step 1:表示5的原码
5的原码为:00000000 00000000 00000000 00000101。
step 2:对5的原码取反
得到答案的原码:11111111 11111111 11111111 11111010
step 3:将答案的原码转成补码
通过原码得到反码:10000000 00000000 00000000 00000101
补码 = 反码 + 1:
答案的补码为:10000000 00000000 00000000 00000110
得到值:-6
前文中的答案4就是依此而来,10 + ~5 即为 10 + (-6)= 4。

D. 小结
取反操作符(~)结论总结:
当n为正数时,~(n) = -(n+1)
当n为负数时,~(-n) = n - 1,忽略负号

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
有限状态机(Finite State Machine,FSM)是一种数学模型,用于描述自动化系统的行为。在Java中,可以使用类来实现有限状态机。 首先,需要定义一个状态枚举类,如下所示: ```java public enum State { STATE1, STATE2, STATE3 } ``` 然后,可以定义一个状态转移表,它描述了从一个状态到另一个状态的转移条件。例如: ```java public class TransitionTable { private static final Map<State, Map<String, State>> table = new HashMap<>(); static { // 从STATE1转移到STATE2,条件是输入的字符串为"abc" Map<String, State> transitionsFromState1 = new HashMap<>(); transitionsFromState1.put("abc", State.STATE2); table.put(State.STATE1, transitionsFromState1); // 从STATE2转移到STATE3,条件是输入的字符串为"def" Map<String, State> transitionsFromState2 = new HashMap<>(); transitionsFromState2.put("def", State.STATE3); table.put(State.STATE2, transitionsFromState2); // ... } public static State getNextState(State currentState, String input) { if (!table.containsKey(currentState)) { throw new IllegalArgumentException("Invalid state: " + currentState); } Map<String, State> transitions = table.get(currentState); if (!transitions.containsKey(input)) { throw new IllegalArgumentException("Invalid input: " + input); } return transitions.get(input); } } ``` 最后,可以编写一个状态机类,该类包含一个当前状态和一个输入方法,用于根据输入和状态转移表更新当前状态。例如: ```java public class StateMachine { private State currentState; public StateMachine(State initialState) { this.currentState = initialState; } public void input(String input) { currentState = TransitionTable.getNextState(currentState, input); } public State getCurrentState() { return currentState; } } ``` 可以使用以下代码来测试状态机: ```java StateMachine stateMachine = new StateMachine(State.STATE1); stateMachine.input("abc"); stateMachine.input("def"); System.out.println(stateMachine.getCurrentState()); // 输出STATE3 ``` 这是一个简单的有限状态机实现,可以根据需要进行扩展。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值