Популярные группы leetcode задач с кратким описание и полным кодом.Алгоритмы написаны на Scala 2.12 в java стиле.
Задача может быть сразу в нескольких разделах.
Личный блог. Заметки о программировании и не только
Популярные группы leetcode задач с кратким описание и полным кодом.
class RealDist {
HashMap words;
public RealDist() {
words = new HashMap();
}
public void set(String word) {
if (!words.containsKey(word)) {
words.put(word, 1);
} else {
words.put(word, words.get(word) + 1);
}
} //set
public int get() {
return words.size();
} //get
} // RealDist
CREATE TABLE t
AS
WITH
t1000 AS (SELECT /*+ materialize */ rownum AS n
FROM dual
CONNECT BY level <= 1E3)
SELECT rownum AS id,
mod(rownum,2) AS n_2,
mod(rownum,4) AS n_4,
mod(rownum,8) AS n_8,
mod(rownum,16) AS n_16,
mod(rownum,32) AS n_32,
mod(rownum,64) AS n_64,
mod(rownum,128) AS n_128,
mod(rownum,256) AS n_256,
mod(rownum,512) AS n_512,
mod(rownum,1024) AS n_1024,
mod(rownum,2048) AS n_2048,
mod(rownum,4096) AS n_4096,
mod(rownum,8192) AS n_8192,
mod(rownum,16384) AS n_16384,
mod(rownum,32768) AS n_32768,
mod(rownum,65536) AS n_65536,
mod(rownum,131072) AS n_131072,
mod(rownum,262144) AS n_262144,
mod(rownum,524288) AS n_524288,
mod(rownum,1048576) AS n_1048576,
mod(rownum,2097152) AS n_2097152,
mod(rownum,4194304) AS n_4194304,
mod(rownum,8388608) AS n_8388608,
mod(rownum,16777216) AS n_16777216
FROM t1000, t1000, t1000
WHERE rownum <= 1E8;


public static int fnv1a(String text) {
int hash = 0x811c9dc5;
for (int i = 0; i < text.length(); ++i) {
hash ^= (text.charAt(i) & 0xff);
hash *= 16777619;
}
return hash >>> 0;
} //fnv1a
// позиция первого ненулевого бита справа
public static int rank(int hash, int max_rank) {
int r = 1;
while ((hash & 1) == 0 && r <= max_rank) {
r++;
// смещаем вправо, пока не дойдет до hash & 1 == 1
hash >>>= 1;
}
return r;
} //rank
public void set(String word) {
int hash = fnv1a(word);
// убираем 24 бита из 32 справа - остается 8 левых
// (=256 разных значений)
int k = hash >>> 24;
// если коллизия, то берем наибольший ранк
hashes[k] = Math.max(hashes[k], rank(hash, 24));
} // count

public double get_loglog() {
double count = 0;
for (int i = 0; i < hashes.length; i++) {
count += 1 / Math.pow(2, hashes[i]);
}
return 47072.7126712022335488 / count;
} //get_loglog
public int get() {
long pow_2_32 = 4294967296L;
double E = get_loglog();
// коррекция
if (E <= 640) {
int V = 0;
for (int i = 0; i < 256; i++) {
if (hashes[i] == 0) {
V++;
}
}
if (V > 0) {
E = 256 * Math.log((256 / (double) V));
}
} else if (E > 1 / 30 * pow_2_32) {
E = -pow_2_32 * Math.log(1 - E / pow_2_32);
}
// конец коррекции
return (int) Math.round(E);
} //get
//двухсвязный список
class Node<Value> {
//ключ списка
int key;
//абстрактное значение
Value value;
//счетчик обращений к элементу
int cnt;
//указатель на предыдущий элемент
Node prev;
//указатель на следующий элемент
Node next;
//элемент был перемещен из конца списка в начало
boolean swaped;
public Node(int key, Value value){
this.key = key;
this.value = value;
this.cnt = 1;
this.swaped = false;
}
//получить значение из списка
public Value getValue() {
//увеличиваем счетчик обращений
cnt++;
//сбрасываем признак смещений, если элемент был прочитан
this.swaped = false;
return value;
}
//установить значение
public void setValue(Value val) {
this.value = val;
//также увеличиваем счетчик
cnt++;
//сбрасываем признак смещений, если элемент был перезаписан
this.swaped = false;
} //setValue
} //Node
public class Lru<Value> {
//доступной число элементов в кэше
int capacity;
//хэш массив элементов для быстрого доступа
HashMap<Integer, Node> map;
//указатель на начало (горячие элементы)
Node head = null;
//указатель на середину (начало холодных элементов)
Node cold = null;
//указатель на конец (самый редкоиспользуемый)
Node end = null;
//число элементов в кэше
int cnt;
//конкструктор с числом элементов в кэше
public Lru (int capacity) {
this.capacity = capacity;
//хэш массив создаем с нужным числом секций = загруженности
map = new HashMap<Integer, Node>(capacity);
}
//получить элемент из кэша
public Value get(int key) {
//быстрое извлечение их хэш массива
if(map.containsKey(key)) {
//и инкремент счетчика обращений
return (Value) map.get(key).getValue();
}
return null;
} //get
//места достаточно, добавляем вначало
protected void addHead(Node n) {
//первый элемент
if(this.head == null) {
//устанавливаем начало и конец = элементу
this.head = n;
this.end = n;
} else {
//вставляем вначало
//следующий для нового элемента = начало списка
n.next = this.head;
//предыдущий для начала списка = новый элемент
this.head.prev = n;
this.head = n;
//второй элемент
if(this.end.prev == null) {
//предыдущий для конца = новый элемент
this.end.prev = n;
}
}
//устанавливаем середину
if(cnt == capacity / 2) {
this.cold = n;
}
//счетчик элементов + 1
cnt++;
} //addHead
//удаляем конца списка
private void delEnd() {
//из хэш массива
map.remove(this.end.key);
//и делаем концом списка = предыдущий элемент
this.end = this.end.prev;
this.end.next = null;
} //delEnd
//вконце малопопулярный блок
protected void addColdUnPop(Node n) {
//удаляем конец
delEnd();
//у старой середины изменяем счетчик на 1
if(this.cold.swaped) {
//если смещенный элемент не был ни разу считан
//и дошел до середины,
//то сбрасываем счетчик в 1
this.cold.cnt = 1;
this.cold.swaped = false;
}
//новый блок в середину = cold
//проставляем ссылки у нового элемента
n.prev = this.cold.prev;
n.next = this.cold;
//и разрываем связи и соседей
n.prev.next = n;
n.next.prev = n;
this.cold = n;
} //addColdUnPop
//вконце популярный блок
protected void addColdPop(int key, Value value) {
//делим счетчик на пополам
this.end.cnt = this.end.cnt / 2;
//открепляем конец
Node n = this.end;
//удаляем конец
delEnd();
//конец перемещаем в начало
n.prev = null;
n.next = this.head;
this.head.prev = n;
this.head = n;
//помечаем, что элемент был перемещен из конца в начало
this.head.swaped = true;
//смещаем середину на 1 влево
if(this.cold.swaped) {
//если смещенный элемент не был ни разу считан
//и дошел до середины,
//то сбрасываем счетчик в 1
this.cold.cnt = 1;
this.cold.swaped = false;
}
this.cold = this.cold.prev;
//рекурсивно пытаемся вставить вконец
//TODO: если все популярные? то вставка будет идти очень долго
this.set(key, value);
} //addColdPop
create table l as select level as id, 'name_' || level as title, rpad('*', level) as pad from dual connect by level <= 50;
create table r as select rownum as id, mod(rownum, 50) as l_id, rpad('*', 20) as pad
from (select * from dual connect by level <= 1000 ) join (select * from dual connect by level <= 1000 ) on 1=1;
begin
DBMS_STATS.GATHER_TABLE_STATS(USER, 'L');
DBMS_STATS.GATHER_TABLE_STATS(USER, 'R');
end;
explain plan for
select *
from l
join r on l.id = r.l_id
WHERE l.title = 'name_5';
select * from table(dbms_xplan.display(format=>'ALLSTATS ALL ADVANCED'));
Plan hash value: 3967001914
----------------------------------------------------------------------------------------
| Id | Operation | Name | E-Rows |E-Bytes| Cost (%CPU)| E-Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 20000 | 1308K| 184 (20)| 00:00:01 |
|* 1 | HASH JOIN | | 20000 | 1308K| 184 (20)| 00:00:01 |
| 2 | JOIN FILTER CREATE | :BF0000 | 1 | 38 | 2 (0)| 00:00:01 |
|* 3 | TABLE ACCESS STORAGE FULL| L | 1 | 38 | 2 (0)| 00:00:01 |
| 4 | JOIN FILTER USE | :BF0000 | 1000K| 27M| 171 (15)| 00:00:01 |
|* 5 | TABLE ACCESS STORAGE FULL| R | 1000K| 27M| 171 (15)| 00:00:01 |
----------------------------------------------------------------------------------------
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
1 - SEL$58A6D7F6
3 - SEL$58A6D7F6 / L@SEL$1
5 - SEL$58A6D7F6 / R@SEL$1
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("L"."ID"="R"."L_ID")
3 - storage("L"."TITLE"='name_5')
filter("L"."TITLE"='name_5')
5 - storage(SYS_OP_BLOOM_FILTER(:BF0000,"R"."L_ID"))
filter(SYS_OP_BLOOM_FILTER(:BF0000,"R"."L_ID"))
package Hash;
import java.util.concurrent.ThreadLocalRandom;
public class BloomFilter {
//long переменная в 64бита под битовый массив
private long data;
//битов в битовой карте = числу битов в long
private int bit_array_size = Long.SIZE;
//примесь для случайного хэширования
private int seed = ThreadLocalRandom.current().nextInt(1, bit_array_size);
//хэшировани = номер бита в битовом массиве
public long hashCode(String s, int hash_num) {
long result = 1;
//для каждого байта в строке
for (int i = 0; i < s.length(); ++i) {
//применяем хэш функцию под номером hash_num и обрезаем по маске
//простая хэш функция = ascii значение буквы * примесь * номер функции * хэш от предыдущей функции & обрезка по маске
//1 = (1 * 1 + 58)
//1 = ( 0001 * 0001 + 11 0001 ) & 1111 1111 1111 1111
result = ((hash_num + seed) * result + s.charAt(i)) & this.hashMask;
}
//установить index бит в битовой карте
public void setBit(long index) {
//= битовая карта OR 1 смещенное влево на index
this.data = this.data | (1L << index );
} //setbit
//добавить элемент в блум фильтр
public void add(String s) {
//++ счетчик элементов
cnt++;
//для каждой хэш функции
for(int i = 1; i <= hash_nums; i++) {
//расчитаем номер индекса в битовой карте и установим его
long index = hashCode(s, i);
setBit(index);
}
} //add
//получить значение бита на index месте
public long getBit(long index) {
//=битовая карта смещенная вправо на index мест (>>> пустые места справа заполняются 0)
// & 01 - проверка только крайнего правого бита (все остальные игнорируются)
return ( this.data >>> index ) & 1;
} //getBit
//проверка наличия элемента в блум фильтре
public boolean test(String s) {
//для каждой хэш функции
for(int i = 1; i <= hash_nums; i++) {
//определяем номер бита в битовой карте
long index = hashCode(s, i);
//если хотябы одна проверка не прошла - элемента нет
if( getBit(index) == 0L ) return false;
}
//иначе элемент вероятно есть
return true;
} //test
//вероятность ложного срабатывания
public double getFalsePossb() {
if(cnt == 0) return 0;
return 1 / Math.pow(Math.E, bit_array_size * Math.log(2) * Math.log(2) / cnt );
} //getFalsePossb
//оптимальное число функций хэширования
public int getOptimalFncCnt() {
if(cnt == 0) return 1;
return (int)Math.ceil( bit_array_size / cnt * Math.log(2) );
} //getOptimalFncCnt
[1] = 1 (остаток от деления 1 на 10 = 1, т.к. ключ 1 был пустой, то он и занимается) [2] = 11 (остаток от деления 11 на 10 = 1, это коллизия для предыдущего элемента. Т.к. ключ 1 занят, то берется следующий пустой справа = 2) [3] = 2 (остаток от деления = 2, т.к. элемент занят, то берется следующий = 3) [4] = 3 (и т.д.)
[1] = 1 -> 11 [2] = 2 [3] = 3 [4] = 4
| Сравниваемое свойство | Цепочки | Открытая адресация |
| Разрешение коллизий | Используются дополнительные списки | Хранится в самом хэш массиве |
| Использование памяти | Дополнительная память для указателей в списке. В случае неудачной хэш функции, если все элементы легли в один элемент массива (хэш секцию), то размер памяти будет почти в 2 раза больше, т.к. необходимая память под указатели будет равна памяти под ключи. |
Нет доп. расходов |
| Зависимость производительности от заполненности таблицы | Прямо пропорционально значению = число элементов / кол-во хэш секции таблицы | число элементов / ( кол-во хэш секции таблицы - число элементов ) |
| Возможность записи числа элементов больше размера хэш таблицы | Да, за счет записи коллизий в список | Нет, т.к. колллизии храняться в томже массиве |
| Простота удаления | Да, удаляется элемент из списка | Нет, пустое место нужно помечать удаленным, а не очищать физически. Т.к. при вставке ищутся пусты места справа, что может сломать последовательность |
| Использование кэша процессора | Нет (переходы по ссылке) | Да (Все данные рядом) |