128. Hash Function:点击打开链接
思路:所有的sum算完最后取模,和一边算一边取模,最后的结果是一样的
但是边算边取可以防止超出范围
class Solution {
/**
* @param key: A String you should hash
* @param HASH_SIZE: An integer
* @return an integer
*/
public int hashCode(char[] key,int HASH_SIZE) {
if(key==null || key.length==0){
return 0;
}
long sum=0; //sum在过程中可能会超出int范围
for(int i=0;i<key.length;i++){
sum=(sum*33+(int)key[i])%HASH_SIZE; //即时取模,防止超出范围
}
return (int)sum; //最后转为int
}
}
129.Rehashing:点击打开链接
/**
* Definition for ListNode
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
/**
* @param hashTable: A list of The first node of linked list
* @return: A list of The first node of linked list which have twice size
*/
public ListNode[] rehashing(ListNode[] hashTable) {
if(hashTable==null || hashTable.length==0){
return null;
}
int newCapacity=hashTable.length*2;
ListNode[] newTable=new ListNode[newCapacity];
for(ListNode head:hashTable){ //遍历原数组的每一个ListNode
while(head!=null){
int newIndex=(head.val%newCapacity+newCapacity)%newCapacity; //算出每一个ListNode在新数组的位置
if(newTable[newIndex]==null){ //如果新数组要放的位置上是空的
newTable[newIndex]=new ListNode(head.val); //直接把遍历的值放上去
}else{ //如果新数组要放的位置上已经有ListNode
ListNode cur=newTable[newIndex];
while(cur.next!=null){ //如果next一直有值,就一直往后找
cur=cur.next;
}
cur.next=new ListNode(head.val); //直到找到下一个为空,放上遍历的值
}
head=head.next; //不断向前推进,进入head!=null
} //原数组不为空才要进行后面的判断
}
return newTable;
}
}
130.Heapify:点击打开链接
思路:先赋值smallest为k,k就是每个父亲,如果父亲的左儿子比父亲小,smallest就是左儿子,如果父亲的右儿子比父亲小,smallest就是右儿子,然后哪个是最小的,哪个就和父亲交换下位置,然后父亲的位置赋值为最小
注意:index是每个二叉树节点的位置,val就是每个节点的值:
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
public class Solution {
/**
* @param A: Given an integer array
* @return: void
*/
public void heapify(int[] A) {
for(int i=A.length/2;i>=0;i--){ //遍历每一个父亲的位置
siftDown(A,i);
}
}
private void siftDown(int[] A,int k){
while(k<A.length){
int smallest=k;
if(k*2+1<A.length && A[k*2+1]<A[smallest]){
smallest=k*2+1;
}
if(k*2+2<A.length && A[k*2+2]<A[smallest]){
smallest=k*2+2;
}
if(smallest==k){ //如果父亲的位置就是最小的,break整个循环,进行下一个
break;
}
int temp=A[smallest];
A[smallest]=A[k];
A[k]=temp;
k=smallest;
}
}
}
4. ugly Number II :点击打开链接
思路:1. 把数组里的元素2,3,5装入同时PriorityQueue和HashSet
2. Long.valueOf(1)就是所求,如果只求第一个丑数,不满足for循环条件,直接跳到结果
number=1
3. 从结果求第二个开始才会正式进入for循环,poll出当前最小2,然后同时向PriorityQueue和HashSet里加2*2,2*3,2*5,
因为PriorityQueue已经poll出2,所以剩[3,5,4,6,10],而HashSet[2,3,5,4,6,10]
加之前需判断HashSet是否已经有这个元素,因为虽然HashSet不装重复元素,但是涉及到同时要向PriorityQueue里加
number=2
4. 然后同理要poll最小的元素3,同时向PriorityQueue和HashSet里加3*2,3*3,3*5,
因为PriorityQueue已经poll出3,再考虑上重复元素,所以剩[5,4,6,10,9,15],而HashSet[2,3,5,4,6,10,9,15]
number=3
5. 依次向后执行
注意:1. 对于Long n=1;表示的n是int类型,因此想要表示Long类型,就要Long n=Long.valueOf(1)或者Long n=1L
2. 对于结果是要将long类型number转化成int类型,变量不可以直接numberL
可以先转化成String再转成int,Integer.valueOf(String.valueOf(number))
或者number.intValue()
基础:这里是网上找的一篇关于heap和priorityQueue的基本理解:点击打开链接
class Solution {
/**
* @param n an integer
* @return the nth prime number as description.
*/
public int nthUglyNumber(int n) {
if(n<=0){
return 0;
}
Queue<Long> queue=new PriorityQueue<>();
Set<Long> set=new HashSet<>();
Long[] primes={Long.valueOf(2),Long.valueOf(3),Long.valueOf(5)};
for(Long prime:primes){
queue.offer(prime);
set.add(prime);
}
Long number=Long.valueOf(1);
for(int i=1;i<n;i++){
number=queue.poll();
for(Long prime:primes){
if(!set.contains(prime*number)){
queue.offer(prime*number);
set.add(prime*number);
}
}
}
return Integer.valueOf(String.valueOf(number));
}
}
544.Top k Largest Numbers:点击打开链接
思路:遍历数组nums,不断往小顶堆里加元素,直到元素个数大于k的时候,就要poll一个元素出去,保证小顶堆里只有k个元素
而由小顶堆的性质知,poll出去的一定是当前数组里最小的元素,然后继续加下一个遍历到的元素,再poll当前最小的元素
直到遍历完整个数组元素,小顶堆里剩下的k个元素就是所求。
注意:看题目是否要求了输出顺序
class Solution {
/*
* @param nums an integer array
* @param k an integer
* @return the top k largest numbers in array
*/
public int[] topk(int[] nums, int k) {
if(nums==null || nums.length==0){
return null;
}
Queue<Integer> pQueue=new PriorityQueue<>();
for(int num:nums){
pQueue.offer(num);
if(pQueue.size()>k){
pQueue.poll();
}
}
int[] result=new int[k];
for(int i=k-1;i>=0;i--){
result[i]=pQueue.poll();
}
return result;
}
}
545.Top k Largest Numbers II:点击打开链接
public class Solution {
Queue<Integer> pQueue;
int maxSize;
public Solution(int k) {
pQueue=new PriorityQueue<>();
maxSize=k;
}
public void add(int num) {
pQueue.offer(num);
if(pQueue.size()>maxSize){
pQueue.poll();
}
}
public List<Integer> topk() {
List<Integer> result=new ArrayList<>();
Iterator<Integer> it=pQueue.iterator();
while(it.hasNext()){
result.add(it.next());
}
Collections.sort(result, Collections.reverseOrder()); //用迭代器一个一个出来,所以没有顺序
return result; //所以要先排序,再用接口反成从大到小的顺序
}
}
104.Merge k Sorted Lists:点击打开链接
方法一:利用PriorityQueue
例如:[2->4->null,null,1->6->null,3->5->null]
思路:把每一个不为空的当前头装入小顶堆,哪个当前头小就poll出去哪个,同时把poll出去的这个接到最后要输出的list上,
然后把当前poll出去过的list的下一个是当前头,再与其他的头进行比较
解释:当前的4个list中有三个不是空的,装进去后poll出去最小值1,然后1的下一个6继续与2和3进行比较
注意:必须写比较器,即使这个时候用的是小顶堆,因为Comparator里的类是ListNode,不是Integer
只有Integer类型的小顶堆不用自定义比较器,它的大顶堆还有所有其他类型都要自定义
/**
* Definition for ListNode.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int val) {
* this.val = val;
* this.next = null;
* }
* }
*/
public class Solution {
/**
* @param lists: a list of ListNode
* @return: The head of one sorted list.
*/
private Comparator<ListNode> myComparator = new Comparator<ListNode>() {
public int compare(ListNode left, ListNode right) {
return left.val - right.val;
}
};
public ListNode mergeKLists(List<ListNode> lists) {
if (lists == null || lists.size() == 0) {
return null;
}
Queue<ListNode> pQueue = new PriorityQueue<ListNode>(lists.size(), myComparator);
for (ListNode head: lists) {
if (head != null) {
pQueue.offer(head);
}
}
ListNode dummy = new ListNode(-1);
ListNode head = dummy;
while(!queue.isEmpty()) {
ListNode curMin = queue.poll();
head.next = curMin;
head = head.next;
if(curMin.next != null){
queue.offer(curMin.next);
}
curMin = curMin.next;
}
return dummy.next;
}
}
方法二:merge two by two
思路:两个list两个list进行merge,如果lists.size()是奇数,还剩一个当两个merge过的list看待,如果lists.size()是偶数,正好分完。
直到lists.size()<=1跳出while循环
/**
* Definition for ListNode.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int val) {
* this.val = val;
* this.next = null;
* }
* }
*/
public class Solution {
/**
* @param lists: a list of ListNode
* @return: The head of one sorted list.
*/
public ListNode mergeKLists(List<ListNode> lists) {
if (lists == null || lists.size() == 0) {
return null;
}
while(lists.size()>1){
List<ListNode> newLists=new ArrayList<>();
for(int i=0;i<lists.size()-1;i+=2){
ListNode mergeList=merge(lists.get(i),lists.get(i+1));
newLists.add(mergeList);
}
if(lists.size()%2==1){
newLists.add(lists.get(lists.size()-1));
}
lists=newLists;
}
return lists.get(0);
}
private ListNode merge(ListNode head1,ListNode head2){
ListNode dummy=new ListNode(-1);
ListNode head=dummy;
while(head1!=null && head2!=null){
if(head1.val<head2.val){
head.next=head1;
head1=head1.next;
}else{
head.next=head2;
head2=head2.next;
}
head=head.next;
}
if(head1!=null){
head.next=head1;
}
if(head2!=null){
head.next=head2;
}
return dummy.next;
}
}
方法三:Divide & Conquer
/**
* Definition for ListNode.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int val) {
* this.val = val;
* this.next = null;
* }
* }
*/
public class Solution {
/**
* @param lists: a list of ListNode
* @return: The head of one sorted list.
*/
public ListNode mergeKLists(List<ListNode> lists) {
if (lists == null || lists.size() == 0) {
return null;
}
return mergeHelper(lists,0,lists.size()-1);
}
private ListNode mergeHelper(List<ListNode> lists,int start,int end){
if(start==end){
return lists.get(start);
}
int mid=(start+end)/2;
ListNode left=mergeHelper(lists,start,mid);
ListNode right=mergeHelper(lists,mid+1,end);
return mergeTwoLists(left,right);
}
private ListNode mergeTwoLists(ListNode node1,ListNode node2){
ListNode dummy=new ListNode(-1);
ListNode head=dummy;
while(node1!=null && node2!=null){
if(node1.val<node2.val){
head.next=node1;
node1=node1.next;
}else{
head.next=node2;
node2=node2.next;
}
head=head.next;
}
if(node1!=null){
head.next=node1;
}
if(node2!=null){
head.next=node2;
}
return dummy.next;
}
}
486.Merge K Sorted Arrays:点击打开链接
思路:与Merge k Sorted Lists的方法一思路相同
注意:1. 这个题的返回值类型是List,用Merge k Sorted Lists的方法二不好做,因为涉及到每merge的新数组装到大数组的问题
2. corner case,给定:[ ][ ],Output:[ ],所以最早加到pQueue的时候要判断arrays[i].length!=0
class Record{ //封装内部类
int row; //每一个数组值的row,col,val
int col;
int val;
public Record(int row,int col,int val){
this.row=row;
this.col=col;
this.val=val;
}
}
public class Solution {
/**
* @param arrays k sorted integer arrays
* @return a sorted array
*/
public List<Integer> mergekSortedArrays(int[][] arrays) {
List<Integer> result = new ArrayList<>();
if(arrays==null || arrays.length==0){
return result;
}
Queue<Record> pQueue = new PriorityQueue<Record>(arrays.length, myComparator);
for (int i=0; i<arrays.length; i++) {
if (arrays[i].length != 0) { //易出错的地方
pQueue.offer(new Record(i,0,arrays[i][0])); //因为每一行数组的长度不相等
}
}
while (!pQueue.isEmpty()) {
Record cur = pQueue.poll();
result.add(cur.val);
if (cur.col+1 < arrays[cur.row].length) { //装完之后判断这一行的下一个
pQueue.offer(new Record(cur.row,cur.col+1, arrays[cur.row][cur.col+1]));//如果还有数,就是这一行的头
}
}
return result;
}
private Comparator<Record> myComparator=new Comparator<Record>() {
public int compare(Record x, Record y) {
return x.val-y.val;
}
};
}
613.High Five:点击打开链接
方法一:大顶堆
思路:1. Map<id,此id对应的所有成绩>,而每个id对应的所有成绩装入到一个大顶堆里
2. 然后再对每个id从大顶堆里poll出5个成绩,就是最高的5个成绩,除以5.0取平均就是所求
注意:这道题很容易TLE,时间卡的比较严,有时候同样的代码多提交几次又不超时了。
/**
* Definition for a Record
* class Record {
* public int id, score;
* public Record(int id, int score){
* this.id = id;
* this.score = score;
* }
* }
*/
public class Solution {
/**
* @param results a list of <student_id, score>
* @return find the average of 5 highest scores for each person
* Map<Integer, Double> (student_id, average_score)
*/
public Map<Integer, Double> highFive(Record[] results) {
if(results==null || results.length==0){
return null;
}
Map<Integer, PriorityQueue<Integer>> scoresMap = new HashMap<>();
PriorityQueue<Integer> maxHeap = null;
for(Record r: results) {
if(!scoresMap.containsKey(r.id)){
maxHeap = new PriorityQueue<Integer>(5,Collections.reverseOrder());
maxHeap.offer(r.score);
scoresMap.put(r.id, maxHeap);
}else{
maxHeap=scoresMap.get(r.id);
maxHeap.offer(r.score);
}
}
Map<Integer, Double> map = new HashMap<>();
for(Integer id: scoresMap.keySet()) {
int sum=0;
maxHeap = scoresMap.get(id);
for(int i=0; i<5; i++) {
sum+=maxHeap.poll();
}
map.put(id, sum/5.0);
}
return map;
}
}
方法二:小顶堆
思路:1. Map<id,此id对应的所有成绩>,而每个id对应的成绩装入到一个小顶堆里,装好了5个,装到第6个的时候就poll出当前最小的一个
2. 依次,保证小顶堆里只有5个元素,最后留下来的5个就是poll出了所有相对小的后留下来的结果
/**
* Definition for a Record
* class Record {
* public int id, score;
* public Record(int id, int score){
* this.id = id;
* this.score = score;
* }
* }
*/
public class Solution {
/**
* @param results a list of <student_id, score>
* @return find the average of 5 highest scores for each person
* Map<Integer, Double> (student_id, average_score)
*/
public Map<Integer, Double> highFive(Record[] results) {
if(results==null || results.length==0){
return null;
}
Map<Integer, PriorityQueue<Integer>> map = new HashMap<>();
PriorityQueue<Integer> minheap=null;
for (Record r : results) {
if (!map.containsKey(r.id)) {
minheap= new PriorityQueue<>();
minheap.offer(r.score);
map.put(r.id, minheap);
} else {
minheap=map.get(r.id);
minheap.offer(r.score);
if (minheap.size() > 5) {
minheap.poll();
}
}
}
Map<Integer, Double> resultMap = new HashMap<>();
for (Map.Entry<Integer, PriorityQueue<Integer>> entry : map.entrySet()){
PriorityQueue<Integer> pq = entry.getValue();
double sum = 0;
while (!pq.isEmpty()) {
sum += pq.poll();
}
resultMap.put(entry.getKey(), sum/5.0);
}
return resultMap;
}
}
612.K Closest Point:点击打开链接
思路:同样是大顶堆解决的问题,一看到Kth什么什么,要想到小顶堆或者大顶堆
/**
* Definition for a point.
* class Point {
* int x;
* int y;
* Point() { x = 0; y = 0; }
* Point(int a, int b) { x = a; y = b; }
* }
*/
public class Solution {
/**
* @param points a list of points
* @param origin a point
* @param k an integer
* @return the k closest points
*/
private Point o=null;
public Point[] kClosest(Point[] points, Point origin, int k) {
o=origin; //给原点赋值
PriorityQueue<Point> pq = new PriorityQueue<> (k, myComparator);
for (int i = 0; i < points.length; i++) {
pq.offer(points[i]);
if (pq.size() > k)
pq.poll();
}
Point[] result = new Point[k];
while (!pq.isEmpty()){
result[--k] = pq.poll();
}
// for(int i=result.length-1;i>=0;i--){ //遍历的时候两种写法都可以,但是不能如下:
// result[i]=pq.poll(); //for(int i=result.lenght-1;i>=0;i--){
// } // while(!pq.isEmpty()){
return result; // result[i]=pq.poll();
} // }
//}
private int getDistance(Point a, Point b) { //因为这表示对于每个i,只要pq不空,就都会poll出来
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
private Comparator<Point> myComparator=new Comparator<Point> () { //自定义比较器
public int compare(Point a, Point b) { //如果两点到origin的距离相等,就看x
int diff=getDistance(b,o) - getDistance(a,o); //如果两点到x的距离也相等,就看y
if(diff==0){
diff=b.x-a.x;
}
if(diff==0){
diff=b.y-a.y;
}
return diff;
}
};
}
471.Top K Frequent Words:点击打开链接
public class Solution {
/**
* @param words an array of string
* @param k an integer
* @return an array of string
*/
Map<String,Integer> map=null;
public String[] topKFrequentWords(String[] words, int k) {
if(words==null || words.length==0 || k==0){
return new String[0];
}
map=new HashMap<>();
for(String word:words){
if(!map.containsKey(word)){
map.put(word,1);
}else{
map.put(word,map.get(word)+1);
}
}
Queue<String> minHeap=new PriorityQueue<>(k,myComparator);
for(String word:map.keySet()){
minHeap.offer(word);
if(minHeap.size()>k){
minHeap.poll();
}
}
String[] result=new String[k];
while(!minHeap.isEmpty()){
result[--k]=minHeap.poll();
}
return result;
}
private Comparator<String> myComparator=new Comparator<String>(){ //自定义比较器
public int compare(String s1,String s2){
int diff=map.get(s1)-map.get(s2); //顺序一样就是由大到小
if(diff!=0){
return diff;
}
return s2.compareTo(s1); //compareTo是比较两个字符串的大小的
} //是按照字典里的顺序
}; //依然是和参数顺序相反就从大到小,反之从小到大
}
class Pair{ //方法二:自定义内部类封装对象
String word;
Integer freq;
public Pair(String word,Integer freq){ //Pair类有word和freq两个内容
this.word=word;
this.freq=freq;
}
}
public class Solution {
/**
* @param words an array of string
* @param k an integer
* @return an array of string
*/
public String[] topKFrequentWords(String[] words, int k) {
if(words==null || words.length==0 || k==0){
return new String[0];
}
Map<String,Integer> map=new HashMap<>();
for(String p:words){
if(!map.containsKey(p)){
map.put(p,1);
}else{
map.put(p,map.get(p)+1);
}
}
Queue<Pair> minHeap=new PriorityQueue<>(k,myComparator);
for(Map.Entry<String,Integer> entry:map.entrySet()){
Pair p=new Pair(entry.getKey(),entry.getValue()); //这边容易出错
minHeap.offer(p);
if(minHeap.size()>k){
minHeap.poll();
}
}
String[] result=new String[k];
while(!minHeap.isEmpty()){
result[--k]=minHeap.poll().word;
}
return result;
}
private Comparator<Pair> myComparator=new Comparator<Pair>(){
public int compare(Pair p1,Pair p2){
if(p1.freq!=p2.freq){
return p1.freq-p2.freq;
}
return p2.word.compareTo(p1.word);
}
};
}
插一道LeetCode相似题:
347. Top K Frequent Elements:点击打开链接
public class Solution {
Map<Integer,Integer> map=null;
public List<Integer> topKFrequent(int[] nums, int k) {
List<Integer> result=new ArrayList<>();
if(nums==null || nums.length==0){
return result;
}
map=new HashMap<>();
for(Integer num:nums){
if(!map.containsKey(num)){
map.put(num,1);
}else{
map.put(num,map.get(num)+1);
}
}
Queue<Integer> minHeap=new PriorityQueue<>(k,myComparator);
for(Map.Entry<Integer,Integer> entry:map.entrySet()){
minHeap.offer(entry.getKey());
if(minHeap.size()>k){
minHeap.poll();
}
}
while(!minHeap.isEmpty()){
result.add(minHeap.poll());
}
return result;
}
private Comparator<Integer> myComparator=new Comparator<Integer>(){
public int compare(Integer num1,Integer num2){
return map.get(num1)-map.get(num2);
}
};
}
134.LRU Cache: 点击打开链接
数据结构:HashMap<Key, DoublyListNode> -> LinkedHashMap
思路:LRU-Least Recently Used最近最没有用到的那个
每次装之前都要判断cache里有没有,有就把这个放最后,没有就装进去,如果cache满了就要删除第一个位置,把它放最后位置
例如:[1,2,3,2,5,3,6,7],假设cache的capacity=3,也就是只有三个坑,在cache里的位置越靠前表示越老,最后面的是最近才放进去的
1. [1,2,3],在装每一个之前都要判断这个是不是已经存在于cache里,因为装前三个的时候都没有存在,且cache也没有满,直接装
2. [1,2,3]+2->[1,3,2],装第四个的时候cache里已经有2,不管cache有没有满,把2拿出来,使2.pre和2.next相连接,把2放到最后
3. [1,3,2]+5->[3,2,5],装第五个的时候cache里没有这个5,但是cache满了,就要remove第一个,把5放进来到最后的位置
class Node{
Node pre;
Node next;
int key;
int val;
public Node(int key,int val){
this.key=key;
this.val=val;
this.pre=null;
this.next=null;
}
}
public class LRUCache {
private int capacity;
private Node head=new Node(-1,-1);
private Node tail=new Node(-1,-1);
Map<Integer,Node> map=new HashMap<>();
public LRUCache(int capacity) {
this.capacity=capacity;
head.next=tail;
tail.pre=head;
}
public int get(int key) {
if(!map.containsKey(key)){
return -1;
}
Node cur=map.get(key);
cur.pre.next=cur.next;
cur.next.pre=cur.pre;
moveToTail(cur);
return map.get(key).val;
}
public void set(int key, int value) {
// get(key)这边执行的时候已经把这个Node moveToTail了
if(get(key)!=-1){ //上面的方法的结果!=-1,也就是cache里有要set的数
map.get(key).val=value;
return;
}
if(map.size()==capacity){
map.remove(head.next.key);
head.next=head.next.next;
head.next.pre=head;
}
Node newNode=new Node(key,value);
map.put(key,newNode);
moveToTail(newNode);
}
private void moveToTail(Node cur){
cur.pre=tail.pre;
tail.pre=cur;
cur.pre.next=cur;
cur.next=tail;
}
}