题目一览:
888. 公平的糖果交换 - 力扣(LeetCode)(hashmap 简单)
136. 只出现一次的数字 - 力扣(LeetCode)(hashset 简单)
138. 随机链表的复制 - 力扣(LeetCode)(hashmap 中等)
771. 宝石与石头 - 力扣(LeetCode)(hashset 简单)
7.07~7.11的五道题目,几乎是简单题,重心暂时不放在算法练习上。
题解:
888.公平的糖果交换
爱丽丝和鲍勃拥有不同总数量的糖果。给你两个数组 aliceSizes
和 bobSizes
,aliceSizes[i]
是爱丽丝拥有的第 i
盒糖果中的糖果数量,bobSizes[j]
是鲍勃拥有的第 j
盒糖果中的糖果数量。
两人想要互相交换一盒糖果,这样在交换之后,他们就可以拥有相同总数量的糖果。一个人拥有的糖果总数量是他们每盒糖果数量的总和。
返回一个整数数组 answer
,其中 answer[0]
是爱丽丝必须交换的糖果盒中的糖果的数目,answer[1]
是鲍勃必须交换的糖果盒中的糖果的数目。如果存在多个答案,你可以返回其中 任何一个 。题目测试用例保证存在与输入对应的答案。
解:这题我采用的是哈希表,关键是---->如何才能在两个数组中找到正确的要交换的两个数。
1)可以根据方程计算:设Alice交换的糖果数为x,bob交换的糖果数为y,因为在交换后两个人的糖果数量相同,所以aliceSizes - x + y = bobSizes - y + x,将这个方程进行调整,可以得到(aliceSizes - bobSizes) / 2 + y = x。
2)我们将aliceSizes数组的每个值 以及 对应的下标 作为哈希表的键对值,这样在后续的的查找中,我们可以去遍历bobSizes数组。
3)将((aliceSizes - bobSizes) ) / 2 + bobSizes[ i ] 传给map.get,看map中是否能找到对应的value,找到的值即为alice要交换的那盒糖的下标 j ,将bobSizes[ i ] 和 aliceSizes[ j ] 给answer数组返回即可
代码:
class Solution {
public int[] fairCandySwap(int[] aliceSizes, int[] bobSizes) {
//哪两个数可以交换的方法思想
Map<Integer,Integer> hashmap = new HashMap<>();
int numA = 0;
int numB = 0;
for(int i = 0;i < aliceSizes.length;i++){
hashmap.put(aliceSizes[i],i);
numA = numA + aliceSizes[i];
}
for(int i = 0;i < bobSizes.length;i++){
numB = numB + bobSizes[i];
}
int ret = (numA - numB)/2;
int[] answer = new int[2];
for(int j = 0;j<bobSizes.length;j++){
Integer key = hashmap.get(ret+bobSizes[j]);
if(key != null){
answer[0] = aliceSizes[key];
answer[1] = bobSizes[j];
break;
}
}
return answer;
}
}
2.两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
解:这道题可以用简单的数学计算去思考。
1) 两个链表的指针从头开始同时向后移动,例如链表A:2->4->3,链表B:5->6->4,将2和4相加后,链表同步向后,分别指向4和6。
2 )根据朴素的数学计算,我们知道十进制是 十进一,所以我们需要一个变量carry 去记录进位的数,然后加到下一次的计算中。
3)如果一个链表先为空,而另一个链表不为空,则将下一次相加的数设置为0,直到两个链表为空,计算完毕。
4)需要注意的是,最后循环结束,判断carry是否大于0 ,如果大于0 ,则代表还需要进位,在新链表的表尾插入一个节点即可。
代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
//1.需要一个新的链表
//2.用carry记录进位数
ListNode head = null;
ListNode tail = null;
int carry = 0;//进位数
while(l1 != null || l2 != null){
int n1 = l1 != null ? l1.val : 0;
int n2 = l2 != null ? l2.val : 0;
//按照初等数学算数
int res = (n1 + n2 + carry) % 10;
carry = (n1 + n2 + carry) / 10;
if(head == null){
head = tail = new ListNode(res);
}else{
tail.next = new ListNode(res);
tail = tail.next;
}
//移动指针 两个链表同时往后移 找到下一个对应的数
if(l1 != null){
l1 = l1.next;
}
if(l2 != null){
l2 = l2.next;
}
}
if(carry > 0){
tail.next = new ListNode(carry);
tail = tail.next;
}
return head;
}
}
136. 只出现一次的数字
给你一个 非空 整数数组 nums
,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
你必须设计并实现线性时间复杂度的算法来解决此问题,且该算法只使用常量额外空间。
解:这道题可以用异或,通过位运算得到结果。但是因为我最近在学map和set,这里采用set的方法。
1)利用for循环遍历数组nums,set.contains(nums[ i ])检查set中是否已经存在nums[ i ] 。不存在则添加进set,存在则利用set.remove(nums[ i ])去重,循环结束后 在set中只留下一个数,就是正确结果。
2)因为最后返回类型为int,我们需要将set中的数返回,所以再次用一个for循环,找到set中留下的数是谁,并将其返回
代码:
class Solution {
public int singleNumber(int[] nums) {
//采用set
Set<Integer> set = new HashSet();
for(int i = 0;i < nums.length;i++){
if(!set.contains(nums[i])){
set.add(nums[i]);
}else{
set.remove(nums[i]);
}
}
for(int i = 0;i < nums.length;i++){
if(set.contains(nums[i])){
return nums[i];
}
}
return -1;
}
}
138. 随机链表的复制
给你一个长度为 n
的链表,每个节点包含一个额外增加的随机指针 random
,该指针可以指向链表中的任何节点或空节点。
构造这个链表的 深拷贝。 深拷贝应该正好由 n
个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next
指针和 random
指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。
例如,如果原链表中有 X
和 Y
两个节点,其中 X.random --> Y
。那么在复制链表中对应的两个节点 x
和 y
,同样有 x.random --> y
。
返回复制链表的头节点。
用一个由 n
个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index]
表示:
val
:一个表示Node.val
的整数。random_index
:随机指针指向的节点索引(范围从0
到n-1
);如果不指向任何节点,则为null
。
你的代码 只 接受原链表的头节点 head
作为传入参数。
解:这道题目的要解决 首先要弄清楚题意 (画图解释)
而怎样把原链表中节点的关系 复制到 新链表中?这里采用hashmap
我们将原链表和新链表放入map中,使二者拥有映射关系,这样就可以将关系复制。
map.put(cur,node),其中cur是原链表节点,node是新链表节点
这样我们利用map.get()时,就可以将原节点放入,从而找到新节点,使新节点之间按照原链表的连接方式连接起来,构造新链表
代码:
/*
// Definition for a Node.
class Node {
int val;
Node next;
Node random;
public Node(int val) {
this.val = val;
this.next = null;
this.random = null;
}
}
*/
//1. 先创建对应数量的节点 将val一一传入
//2. 将老链表和新链表建立映射关系 利用hashmap
//3.
class Solution {
public Node copyRandomList(Node head) {
Node cur = head;
Map<Node,Node> map = new HashMap<>();
//将新节点都创建完毕
while(cur != null){
Node node = new Node(cur.val);
// 老表 新表 建立映射关系
map.put(cur,node);
cur = cur.next;
}
Node temp = head;
while(temp != null){
//将新表的各节点连接
map.get(temp).next = map.get(temp.next);
//random也要对应
map.get(temp).random = map.get(temp.random);
temp = temp.next;
}
return map.get(head);
}
}
771. 宝石与石头
给你一个字符串 jewels
代表石头中宝石的类型,另有一个字符串 stones
代表你拥有的石头。 stones
中每个字符代表了一种你拥有的石头的类型,你想知道你拥有的石头中有多少是宝石。
字母区分大小写,因此 "a"
和 "A"
是不同类型的石头。
解:这里我们首先想到的是,石头中的元素一一和宝石中的元素作对比,看是否为宝石。而为了实现这个想法,并且尽量去降低时间复杂度,使用HashSet。
1)将String类型的宝石中的元素利用charAt()方法 一个个放进HashSet中
2)再去遍历石头,利用charAt()将石头中的字符与集合中的元素对照(利用contains()方法判断)
代码:
class Solution {
//1.将string类型改为char[] 类型
//2.将jewels放入集合中
//3.遍历石头数组 看是否存在宝石
public int numJewelsInStones(String jewels, String stones) {
Set<Character> arr = new HashSet<>();
for(int i = 0 ; i < jewels.length();i++){
arr.add(jewels.charAt(i));
}
int count = 0;
for(int i = 0; i < stones.length();i++){
if(arr.contains(stones.charAt(i))){
count++;
}
}
return count;
}
}
okk