1、只出现一次的数字
只出现一次的数字链接
解题思路及代码
/**
* 思路:
* 方法一:
* key-value模型,数字是key,出现次数是val
* 将数组放入map中
* 用map找到val为1的key
*
* 方法二:
* 两个相同的数异或后结果为0
* 任意数与0异或还是自己
* 所有数异或结果就是只出现一次的数
*/
public class SingleNumber {
public int singleNumber1(int[] nums) {
Map<Integer,Integer> map = new TreeMap<>();
for (int n : nums){
int count = map.getOrDefault(n,0);
map.put(n,count+1);
}
for (Map.Entry<Integer,Integer> e : map.entrySet()){
int n = e.getKey();
int c = e.getValue();
if (c == 1){
return n;
}
}
return -1;
}
public int singleNumber2(int[] nums) {
int r = 0;
for (int n : nums){
r = r ^ n;
}
return r;
}
}
总结:
- 任意数异或自己本身,结果为0;
- 任意数异或0,结果不变;
2、复制带随机指针的链表
解题思路及代码
/**
* 思路:
* 方法一:
* 按照链表的方式复制
* 难点:(Random)指向找到新链表的节点(Random)指向
* 1.将旧链表结点复制一份
* 2.将新旧链表连起来
* 3.根据旧链表的Random找新链表的Random
* 4.结束后将新旧链表分开
*
* 方法二:
* 通过Map保存新旧链表的映射关系
* 旧链表中的结点作为key,新链表中的结点作为value;
* 新链表node,旧链表cur
* node.random = map.get(cur.random)
* 解释:cur.random是key,get(cur.random)是key对应的value。
*/
public class CopyList {
public Node copyRandomList(Node head){
Node cur = head;
while (cur != null){
Node node = new Node();
node.val = cur.val;
node.next = cur.next;
cur.next = node;
cur = node.next;
}
cur = head;
//复制random
while (cur != null){
if (cur.random != null) {
cur.next.random = cur.random.next;
}else {
cur.next.random = null;
}
cur = cur.next.next;
}
cur = head;
if (head == null){
return null;
}
Node newHead = head.next;
//拆新旧链表
while (cur != null){
Node node = cur.next;
cur.next = node.next;
if (node.next != null) {
node.next = node.next.next;
}
cur = cur.next;
}
return newHead;
}
// 因为Map的key内部比较大小,得有Comparable或者比较器
// Node作为引用数据类型里面都没有,所以无法比较
// 写一个比较器
private static class NodeComparator implements Comparator<Node>{
@Override
public int compare(Node o1, Node o2) {
return o1.val - o2.val;
}
}
public Node copyRandomList2(Node head){
//要传入比较器
Map<Node,Node> map = new TreeMap<>(new NodeComparator());
Node newHead = null;
Node newLast = null;
Node cur = head;
while (cur != null){
Node node = new Node();
node.val = cur.val;
//node尾插到newHead
if (newHead == null){
newHead = node;
}else {
newLast.next = node;
}
newLast = node;
map.put(cur,node);
cur = cur.next;
}
总结:
1.引用类型的的比较要使用Comparable或者Comparator
2.使用Map或Set时,若key的类型是引用数据类型,则需要写一个比较器传进去才能进行key的比较,例如:Map<Node,Node> map = new TreeMap<>(new NodeComparator());
3.如果使用自定义类型作为TreeMap的K类型
1)要不自定义类型是Comparable
2)否则,需要在构造方法中传入一个比较器
3、宝石与石头
宝石与石头链接
解题思路及代码
/**
* 思路:
* 将我拥有的石头放到set中去
* 遍历宝石J集合,
* 若set中包含了J中的宝石,count++
*/
public class JewelsInStones {
public int numJewelsInStones(String J, String S) {
//把所有宝石放到Set集合中
Set<Character> jewels = new TreeSet<>();
for (char j : J.toCharArray()){
jewels.add(j);
}
//遍历所有的石头
int count = 0;
for (char s : S.toCharArray()){
if (jewels.contains(s)){
count++;
}
}
return count;
}
}
总结:
toCharArray()方法是将字符串转为字符数组;
4、坏键盘打字
坏键盘打字链接
解题思路及代码
/**
* 思路:
* 将输入的错误字符串放到一个set中
* 将要打印的坏掉的键放到一个Set类型的written中
* 若set中不包含正确字符且written中也不包含
* 则打印它的大写
*/
public class BrokenKeyboard {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String right = scanner.nextLine();
String wrong = scanner.nextLine();
Set<Character> set = new TreeSet<>();
for (char c : wrong.toCharArray()){
set.add(c);
}
Set<Character> written = new TreeSet<>();
for (char c : right.toCharArray()){
if (! set.contains(c)){
//字符转成大写
c = Character.toUpperCase(c);
/*
if (c >= 'a' && c <= 'z'){
c += ('A'-'a');
}
*/
if (! written.contains(c)){
System.out.print(c);
written.add(c);
}
}
}
System.out.println();
}
}
总结:
1.Character.toUpperCase()方法是将字母转为大写
2.将字母转为大写还可以:
c += ‘A’ - ‘a’;
5、前K个高频单词
前K个高频单词
解题思路及代码
/**
* 思路:
* 即出前k个单词,出现次数优先于字母顺序
* 先统计每个单词出现的次数
* 单词与次数形成映射关系,相同次数的单词放在一个数组中
* 对次数进行排序,从大到小
* 按次数取出k个单词
*/
public class TopKFrequent {
//统计单词出现次数
public static Map<String,Integer> calcCount(String[] words){
Map<String,Integer> map = new HashMap<>();
for (String w : words){
int count = map.getOrDefault(w,0);
map.put(w,count+1);
}
return map;
}
//反转,得到每个次数下有多少个单词
public static Map<Integer,List<String>> calcCountOfWords(Map<String,Integer> map){
Map<Integer,List<String>> ret = new HashMap<>();
for (Map.Entry<String,Integer> e : map.entrySet()){
String word = e.getKey();
int count = e.getValue();
if (!ret.containsKey(count)){
ret.put(count,new ArrayList<>());
}
ret.get(count).add(word);
}
return ret;
}
//对次数进行排序
public static class IntegerComparator implements Comparator<Integer>{
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
}
public List<String> topKFrequent(String[] words, int k) {
//获取每个单词出现次数
Map<String,Integer> wordsCount = calcCount(words);
//获取出现次数与单词的映射
Map<Integer,List<String>> countToWordList = calcCountOfWords(wordsCount);
//获取所有次数的集合
Set<Integer> counts = countToWordList.keySet();
//用比较器将次数从大到小排
// 1.将counts放到一个数组中
// 2.利用Collections接口的sort方法,进行排序(但为自然顺序、即升序)
// 3.写一个比较器将count调整为降序
List<Integer> countList = new ArrayList<>(counts);
Collections.sort(countList,new IntegerComparator());
/*
Collections.sort(countList, new Comparator<Integer>() {
//匿名类
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
*/
//取单词
List<String> result = new ArrayList<>();
int n = 0;
int i = 0;
while (n < k) {
int count = countList.get(i);
i++;
List<String> ws = countToWordList.get(count);
Collections.sort(ws);
if (ws.size() <= k - n) {
// 如果候选人数量小于需要人数,全部录取
result.addAll(ws);
n += ws.size();
} else {
// 否则,只录取需要的人数
result.addAll(ws.subList(0, k - n));
n += (k - n);
}
}
return result;
}
}
总结:
1.Collections.sort()默认是升序排序
2.当一些类只使用一次时可以用匿名类的形式。
Collections.sort(countList, new Comparator<Integer>() {
//匿名类
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});