标签7位运算技巧和链表、字符串模拟加减乘

位运算:

注意事项:位运算加括号会避免一些不必要的错误

位运算leetcode全集:https://leetcode-cn.com/problems/shu-zu-zhong-shu-zi-chu-xian-de-ci-shu-lcof/solution/zhi-chu-xian-yi-ci-de-shu-xi-lie-wei-yun-suan-by-a/

java中的位运算一般有:

^ 异或

& 与

|或

~ 非

>> 右移 常用(右移运算符,符号左侧数值 按位右移 符号右侧数值指定的位数,若为正数则高位补0,若为负数则高位补1)

>>>无符号右移(无符号右移运算符,符号左侧数值 按位右移 符号右侧数值指定的位数,无论正负高位补0)

<<左移(没有无符号左移)

一般是异或运算:一堆数字,其他的数都出现偶数次(比如2次),有一个数出现了奇数次(比如一次),把所有的数做异或运算,因为异或运算有性质:X1 ^ X2 ^ X3  = X1 ^(X2 ^ X3),最后的结果就得到出现奇数次的数。详见下面解析。

268. 缺失数字

解法:位运算,异或https://leetcode-cn.com/problems/missing-number/solution/que-shi-shu-zi-by-leetcode/

java中异或的符号是^;异或是相同为0,相异为1;因此x^x = 0

class Solution {
    //异或是逐位异或的,比如0^3=3
    public int missingNumber(int[] nums) {
        //任何数字和0异或都为它本身
        int answer = 0;
        for(int i = 0;i < nums.length;i ++){
            answer ^= (i ^ nums[i]);
        }
        return answer ^ nums.length;
    }
}

136. 只出现一次的数字

解法:位运算:异或运算,看解析什么时候用异或运算。

详见解析:https://leetcode-cn.com/problems/single-number/solution/zhi-chu-xian-yi-ci-de-shu-zi-by-leetcode-solution/

class Solution {
    //位运算
    public int singleNumber(int[] nums) {
        if(nums.length % 2 == 0) {
            throw new IllegalArgumentException();
        }else if(nums.length == 1) {
            return nums[0];
        }else {
            /*
            //0和任何数x异或结果为x
            int answer = 0;
            for(int i = 0;i < nums.length;i ++) {
                answer ^= nums[i];
            }
            return answer;
            */
            for(int i = 1;i < nums.length;i ++) {
                nums[0] ^= nums[i];
            }
            return nums[0];
        }
    }
}

剑指 Offer 56 - I. 数组中数字出现的次数 

解法:根据异或运算的性质,原来的数组相当于那两个只出现一次的数进行异或,根据异或的定义异或的结果任何一位为1的位表示这一位这两个只出现一次的数该位不一样,比如4和6的异或结果是010,也就是十进制的2,那么将4和6右移一位,与1进行与运算,可以将4和6分开。

下面的无符号右移改成有符号右移也对。

class Solution {
    public int[] singleNumbers(int[] nums) {
        if(nums.length <= 2) {
            return nums;
        }else {
            //任何数和0异或就是它本身
            int answer = 0;
            for(int i = 0;i < nums.length;i ++) {
                //下面也是可以的
                //answer = answer ^ nums[i];
                answer ^= nums[i];
            }
            int rightmove = 0;
            //System.out.println(answer & 1);
            //answer & 1 != 1 不对
            //应该是(answer & 1) != 1
            while((answer & 1) != 1) {
                answer = answer >> 1;
                rightmove ++;
            }
            int[] result = new int[2];
            for(int i = 0;i < nums.length;i ++) {
                if(((nums[i] >> rightmove) & 1) != 1) {
                    result[0] ^= nums[i];
                }else {
                    result[1] ^= nums[i];
                }
            }
            return result;
        }
    }
}

 645. 错误的集合

解法:类似于上面一道题:剑指 Offer 56 - I. 数组中数字出现的次数 ,比如说数组值是1 2 2 4,下标是0 1 2 3,下标加一是:1 2 3 4;对于数组值和1 2 3 4 来说,这八个数进行异或,结果相当于2和3进行异或,任何数字与本身异或结果为0,任何数字与0异或结果为它本身。

class Solution {
    public int[] findErrorNums(int[] nums) {
        int[] answer = new int[2];
        if(nums.length == 0) {
            return answer;
        }else if(nums.length == 1) {
            answer[0] = nums[0];
            return answer;
        }else {
            //下面的写法类似于数组中出现的数字的次数
            int number = 0;
            for(int i = 0;i < nums.length;i ++) {
                number ^= ((i + 1) ^ nums[i]);
            }
            int rightmove = 0;
            while((number & 1) != 1) {
                rightmove ++;
                number >>= 1;
            }
            for(int i = 0;i < nums.length;i ++) {
                if((((i + 1) >> rightmove) & 1) == 1) {
                    answer[0] ^= (i + 1);
                }else {
                    answer[1] ^= (i + 1);
                }
                if(((nums[i] >> rightmove) & 1) == 1) {
                    answer[0] ^= nums[i];
                }else {
                    answer[1] ^= nums[i];
                }
            }
            for(int i = 0;i < nums.length;i ++) {
                if(answer[0] == nums[i]) {
                    return answer;
                }
            }
            int tem = answer[0];
            answer[0] = answer[1];
            answer[1] = tem;
            return answer;
        }
    }
}

剑指 Offer 56 - II. 数组中数字出现的次数 II

解法:3,3,3,4,最后的array[0-31]结果是:3,3,1,0,0,0......

另外注意无符号右移,还有从array恢复数字的时候,注意恢复成有符号int,要考虑正负号,详见注释

class Solution {
    public int singleNumber(int[] nums) {
        if(nums.length == 0) {
            throw new IllegalArgumentException();
        }else if(nums.length == 1) {
            return nums[0];
        }else {
            //一个int型的数32位,array数组存储的是一比特
            //根据题意,array数组的元素是要么是3的倍数要么不是3的倍数
            int[] array = new int[32];
            int answer = 0;
            //
            for(int i = 0;i < nums.length;i ++) {
                //采用无符号右移,无符号右移高位补0,有符号右移负数高位补1
                for(int j = 0;j < 32;j ++) {
                    //array[j] = (nums[i] >>> j) & 1;
                    array[j] += (nums[i] >>> j) & 1;
                }
            }
            /*
            for(int number : array) {
                System.out.println(number);
            }
            */
            //因为原数组的数是有符号的,因此要考虑array[31] % 3是1还是0
            //下面遍历array的[0,30]不是[0,31]
            for(int i = 0;i < 31;i ++) {
                if(array[i] % 3 != 0) {
                    //answer += (2 << i);
                    answer += (1 << i);
                }
            }
            if(array[array.length - 1] % 3 == 1) {
                return -1 * answer;
            }else {
                return answer;
            }
        }
    }
}

 137. 只出现一次的数字 II

解法:和上面一样。

class Solution {
    public int singleNumber(int[] nums) {
        if(nums.length == 0) {
            throw new IllegalArgumentException();
        }else if(nums.length == 1) {
            return nums[0];
        }else {
            //一个int型的数32位,array数组存储的是一比特
            //根据题意,array数组的元素是要么是3的倍数要么不是3的倍数
            int[] array = new int[32];
            int answer = 0;
            //
            for(int i = 0;i < nums.length;i ++) {
                //采用无符号右移,无符号右移高位补0,有符号右移负数高位补1
                for(int j = 0;j < 32;j ++) {
                    //array[j] = (nums[i] >>> j) & 1;
                    array[j] += (nums[i] >>> j) & 1;
                }
            }
            /*
            for(int number : array) {
                System.out.println(number);
            }
            */
            //因为原数组的数是有符号的,因此要考虑array[31] % 3是1还是0
            //下面遍历array的[0,30]不是[0,31]
            for(int i = 0;i < 31;i ++) {
                if(array[i] % 3 != 0) {
                    //answer += (2 << i);
                    answer += (1 << i);
                }
            }
            if(array[array.length - 1] % 3 == 1) {
                return -1 * ((int)Math.pow(2,31) + 1 - answer);
            }else {
                return answer;
            }
        }
    }
}

字符串或者链表模拟加减乘法

67. 二进制求和

解法:模拟进位加法,遍历的时候,index1 >= 0 || index2 >= 0,哪个index变成-1,对应的数字用0相加

class Solution {
    //存储答案的Stringbuilder的先存储
    public String addBinary(String a, String b) {
        if(b == null || b.equals("0")){
            return a;
        }else if(a == null || a.equals("0")){
            return b;
        }else{
            int index1 = a.length() - 1;
            int index2 = b.length() - 1;
            //number表示进位
            int number = 0;
            int number1 = 0;
            int  number2 = 0;
            int sum = 0;
            StringBuilder sb = new StringBuilder();
            while(index1 >= 0 || index2 >= 0){
                number1 = (index1 >= 0 ? a.charAt(index1) - '0' : 0);
                number2 = (index2 >= 0 ? b.charAt(index2) - '0' : 0);
                sum = number1 + number2 + number;
                number = sum / 2;
                sb.append(sum % 2);
                if(index1 >= 0) {
                    index1 --;
                }
                if(index2 >= 0) {
                    index2 --;
                }
            }
            if(number != 0){
                sb.append(number);
            }
            sb = sb.reverse();
            return sb.toString();
        }
    }
}

2. 两数相加

解法:用链表模拟加法,思想是短链表,补0的思想。

class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        if(l1 == null) {
            return l2;
        }else if(l2 == null) {
            return l1;
        }else {
            ListNode answer = new ListNode(0);
            ListNode cur = answer;
            ListNode cur1 = l1;
            ListNode cur2 = l2;
            //number表示相加产生的进位
            int number = 0;
            int n = 0;
            int m = 0;
            int sum = 0;
            //为了预防两链表长度不一样的情况,采用哪个链表先遍历完,这个链表相加的时候相当于加的是0
            while(cur1 != null || cur2 != null) {
                n = (cur1 == null ? 0 : cur1.val);
                m = (cur2 == null ? 0 : cur2.val);
                sum = n + m + number;
                //结果new节点
                cur.next = new ListNode(sum % 10);
                number = sum / 10;
                cur = cur.next;
                if(cur1 != null) {
                    cur1 = cur1.next;
                }
                if(cur2 != null) {
                    cur2 = cur2.next;
                }
            }
            //注意最后一个进位
            if(number != 0) {
                cur.next = new ListNode(number);
            }
            return answer.next;
        }
    }
}

706. 设计哈希映射

设计一个简单的取余的哈希函数,使用链地址法

class MyHashMap {
    public static class Pair{
        int key;
        int value;
        public Pair(int key,int value) {
            this.key = key;
            this.value = value;
        }
    }
    public LinkedList<Pair>[] array;
    /** Initialize your data structure here. */
    public MyHashMap() {
        array = new LinkedList[1000];
        for(int i = 0;i < 1000;i ++) {
            array[i] = new LinkedList<>();
        }
    }
    
    /** value will always be non-negative. */
    public void put(int key, int value) {
        LinkedList<Pair> tem = array[key % 1000];
        for(int i = 0;i < tem.size();i ++) {
            if(tem.get(i).key == key) {
                tem.get(i).value = value;
                return;
            }
        }
        tem.add(new Pair(key,value));
    }
    
    /** Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key */
    public int get(int key) {
        LinkedList<Pair> tem = array[key % 1000];
        for(Pair pair : tem) {
            if (pair.key == key) {
                return pair.value;
            }
        }
        return -1;
    }
    
    /** Removes the mapping of the specified value key if this map contains a mapping for the key */
    public void remove(int key) {
        LinkedList<Pair> tem = array[key % 1000];
        for(int i = 0;i < tem.size();i ++) {
            if(tem.get(i).key == key) {
                tem.remove(i);
                return;
            }
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值