LeetCode1720. 解码异或后的数组; 剑指offer21/22/23/24

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode1720. 解码异或后的数组; 剑指offer21/22/23/24相关的知识,希望对你有一定的参考价值。

LeetCode1720. 解码异或后的数组

题目描述

未知 整数数组 arr 由 n 个非负整数组成。

经编码后变为长度为 n - 1 的另一个整数数组 encoded ,其中 encoded[i] = arr[i] XOR arr[i + 1] 。例如,arr = [1,0,2,1] 经编码后得到 encoded = [1,2,3] 。

给你编码后的数组 encoded 和原数组 arr 的第一个元素 first(arr[0])。

请解码返回原数组 arr 。可以证明答案存在并且是唯一的。

示例 1:

输入:encoded = [1,2,3], first = 1
输出:[1,0,2,1]
解释:若 arr = [1,0,2,1] ,那么 first = 1 且 encoded = [1 XOR 0, 0 XOR 2, 2 XOR 1] = [1,2,3]
示例 2:

输入:encoded = [6,2,7,3], first = 4
输出:[4,2,0,7,4]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/decode-xored-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

2021.5.6每日一题,简单题,确实挺简单的,主要了解异或的性质:
(百度百科)

  1. 归零律:a ⊕ a = 0
  2. 恒等律:a ⊕ 0 = a
  3. 交换律:a ⊕ b = b ⊕ a
  4. 结合律:a ⊕b ⊕ c = a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c;
  5. 自反:a ⊕ b ⊕ a = b.

a ^ b = c,两边同时异或 a
a ^ a ^ b = c ^ a => b = c ^ a

class Solution {
    public int[] decode(int[] encoded, int first) {
        //a ^ b = c,那么b 怎么由a和c求出呢
        //对于每一个二进制位,b = a ^ c
        int l = encoded.length;
        int[] res = new int[l + 1];
        res[0] = first;
        for(int i = 1; i < l + 1; i++){
            res[i] = res[i - 1] ^ encoded[i - 1];
        }
        return res;
    }
}

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面

题目描述

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

示例:

输入:nums = [1,2,3,4]
输出:[1,3,2,4] 
注:[3,1,2,4] 也是正确的答案之一。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

简单的双指针,书中提到可以将判断条件放在一个函数里,保证代码的可重用性

class Solution {
    public int[] exchange(int[] nums) {
        //简单的双指针吧,
        int l = nums.length;
        int i = 0;
        int j = l - 1;
        while(i < j){
            while(i < l && nums[i] % 2 == 1)
                i++;
            while(j >= 0 && nums[j] % 2 == 0)
                j--;
            if(i < j){
                int temp = nums[i];
                nums[i] = nums[j];
                nums[j] = temp;
                i++;
                j--;
            }
        }
        return nums;
    }
}

剑指Offer 22. 链表中倒数第k个节点

链表的经典问题,倒数第k个节点,双指针,第一个指针先移动k步,然后两个指针同时移动,直到第一个指针到达链表的末尾(为null),这时,第二个指针走了 l - k 步,也就是到达了第l - k + 1个节点,即倒数第k个节点(l 为链表长度)
书里主要提到的是鲁棒性的考察,也就是对各种输入的处理,例如链表为空,k为0.或者节点数少于k等,特殊情况需要另外处理

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        //双指针,第二个先走k步
        ListNode index1 = head;
        ListNode index2 = head;
        for(int i = 0; i < k; i++){
            index2 = index2.next;
        }
        //然后第二个指针走向末尾
        while(index2 != null){
            index1 = index1.next;
            index2 = index2.next;
        }
        return index1;
    }
}

剑指Offer23 : 链表中环的入口节点

力扣剑指offer好像没有这道题,但是力扣142题就是这个题,141题找环,142题找环的入口节点
所以这道题应该分两步,第一步找环,找环,找环就是快慢指针,快指针移动两步,慢指针移动一步,没有环两个指针不可能相遇,如果有环,那么两个指针肯定会在环中相遇

		ListNode slow = head;   //慢指针
        ListNode fast = head;   //快指针
        //循环直到两个指针相遇
        while(true){
            if(fast == null || fast.next == null)
                return null;
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){
                break;
            }
        }

第二步,在已知有环的情况下找环的入口节点:
书中所给的方法是统计环的长度,也就是有一个指针再次从相遇点走到相遇点,走过的长度就是环的长度;知道环的长度以后,再由两个指针,第一个指针先走环的长度步数,然后两个指针同时移动,相遇点即为环的入口(设链表总长度n,相遇点x,环长度y,x + y = n)

用一个更加简洁的方法,接上面快慢指针相遇,此时相遇点为 t,即从链表头部到相遇点的距离为 t ,假设入环点是x,环的长度为 y ,慢指针移动的距离就是 t ,快指针移动的距离是 2t , 因为相遇的情况肯定是快指针比慢指针多走了几个环的长度,也就是2t - t = ny ==》 t = ny
从开始走到入环点的距离,肯定等于x + ny(x + n圈还是入环点),而此时慢指针相当于已经走了ny,那么再走 x 就肯定能到达入环点,所以此时再将快指针重新指向链表头部,同时移动快慢指针(没有创建 新的,因此还叫快慢,其实不快了),相遇即为入口点

public class Solution {
    public ListNode detectCycle(ListNode head) {
        //假设链表开始入环到第一个节点的长度为a,环的长度为b
        //假设两个指针,一个慢的步长为1,一个快的步长为2,因为有环,所以肯定会相遇
        //假设相遇时慢指针走了s,快指针走了f,则f=2s;另外,此时f=s+nb;所以s=nb;
        //另外,由于到达开始入环的点走过的距离肯定等于a+nb,所以此时的慢指针再走a步就到达了入环点,而a恰好是链表到入环点的距离
        //此时再一个指针从零开始和慢指针走,走过a步就和在入环点会相遇,记录这个新指针走过的距离就是入环点的位置

        ListNode slow = head;   //慢指针
        ListNode fast = head;   //快指针
        //循环直到两个指针相遇
        while(true){
            if(fast == null || fast.next == null)
                return null;
            slow = slow.next;
            fast = fast.next.next;
            if(slow == fast){
                break;
            }
        }
        //相遇后创建一个新的指针,指向头部
        ListNode res = head;
        //让两个指针同时移动,当相遇时就在入环的那个节点处
        while(res != slow){
            slow = slow.next;
            res = res.next;
        }
        return res;      
    }
}

剑指 Offer 24. 反转链表

同样是链表的经典问题,还是双指针

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode pre = null;
        ListNode temp = head;

        while(temp != null){
            ListNode next = temp.next;
            temp.next = pre;
            pre = temp;
            temp = next;
        }
        return pre;
    }
}

另外,递归写法

class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null || head.next == null){
            return head;
        }
        ListNode reverse = reverseList(head.next);
        //reverse指向的是现在的尾部节点,而此时头节点还和reverse的尾部节点相连
        head.next.next = head;
        head.next = null;
        return reverse;
    }
}

剑指 Offer 25. 合并两个排序的链表

同样迭代和递归两种方法

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(-1);	//虚拟头节点
        ListNode curr = curr;
        while(l1 != null && l2 != null){
            if(l1.val <= l2.val){
                curr.next = l1;
                l1 = l1.next;
            }
            else{
                curr.next = l2;
                l2 = l2.next;
            }
            curr = curr.next;
        }
        curr.next = (l1 == null? l2 : l1);
        return dummy.next;
    }
}

递归的思考方法也是拆开理解,假设后面合并好了,那么当前应该如何合并
而当前层只需要关心两个节点的大小关系,把后面合并好的链表接在当前较小的节点后面

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        //递归
        if(l1 == null)
            return l2;
        else if(l2 == null)
            return l1;
        
        if(l1.val <= l2.val){
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        }
        else{
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        } 
    }
}

以上是关于LeetCode1720. 解码异或后的数组; 剑指offer21/22/23/24的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode1720. 解码异或后的数组(C++)

LeetCode1720. 解码异或后的数组(C++)

LeetCode:1720. 解码异或后的数组190. 颠倒二进制位

LeetCode1720. 解码异或后的数组; 剑指offer21/22/23/24

我用java刷 leetcode 1720. 解码异或后的数组

LeetCode每日一题(1720. 解码异或后的数组)