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每日一题,简单题,确实挺简单的,主要了解异或的性质:
(百度百科)
- 归零律:a ⊕ a = 0
- 恒等律:a ⊕ 0 = a
- 交换律:a ⊕ b = b ⊕ a
- 结合律:a ⊕b ⊕ c = a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c;
- 自反: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的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode:1720. 解码异或后的数组190. 颠倒二进制位
LeetCode1720. 解码异或后的数组; 剑指offer21/22/23/24